d3js v4: Масштабирование кругов с увеличением

#javascript #svg #d3.js #topojson

Вопрос:

У меня есть карта мира, сделанная с помощью d3js v4 и topojson, которая имеет масштабирование / перетаскивание / круги. Все кажется прекрасным, за исключением того, что я не могу масштабировать круги вместе с увеличением.

Когда я прокручиваю карту, мои круги остаются того же размера, что делает их большими по сравнению с картой.

Как я могу применить преобразование к кругам при увеличении масштаба?

 var width = 660,  height = 400;   var zoom = d3.zoom()  .scaleExtent([1, 10])  .on("zoom", zoomed);    var projection = d3.geoMercator()  .center([50, 10]) //long and lat starting position  .scale(150) //starting zoom position  .rotate([10,0]); //where world split occurs   var svg = d3.select("svg")  .attr("width", width)  .attr("height", height)  .call(zoom);   var path = d3.geoPath()  .projection(projection);   var g = svg.append("g");   //Zoom functionality  function zoomed() { const currentTransform = d3.event.transform; g.attr("transform", currentTransform); }  d3.select(".zoom-in").on("click", function() {  zoom.scaleBy(svg.transition().duration(750), 1.2); }); d3.select(".zoom-out").on("click", function() {  zoom.scaleBy(svg.transition().duration(750), 0.8); });   // load and display the world and locations  d3.json("https://gist.githubusercontent.com/d3noob/5193723/raw/world-110m2.json", function(error, topology) {   var world = g.selectAll("path")  .data(topojson.object(topology, topology.objects.countries).geometries)  .enter()  .append("path")  .attr("d", path)  ;    var locations = g.selectAll("circle")  .data(devicesAll)  .enter()  .append("circle")  .attr("cx", function(d) {return projection([d.LastLocation.lon, d.LastLocation.lat])[0];})  .attr("cy", function(d) {return projection([d.LastLocation.lon, d.LastLocation.lat])[1];})  .attr("r", 2)  .style("fill", "black")  .style("opacity", 1)  ;  var simulation = d3.forceSimulation()  .force('x', d3.forceX().x(function(d) {return projection([d.LastLocation.lon, d.LastLocation.lat])[0]}))  .force('y', d3.forceY().y(function(d) {return projection([d.LastLocation.lon, d.LastLocation.lat])[1]}))  .force("charge", d3.forceManyBody().strength(0.5)) // Nodes are attracted one each other of value is gt; 0  .force("collide", d3.forceCollide().strength(.1).radius(2).iterations(2)) // Force that avoids circle overlapping   // Apply these forces to the nodes and update their positions.  // Once the force algorithm is happy with positions ('alpha' value is low enough), simulations will stop.  simulation  .nodes(devicesAll)  .on("tick", function(d){  locations  .attr("cx", function(d){ return d.x; })  .attr("cy", function(d){ return d.y; })  });  

Ответ №1:

Если я правильно понял вашу проблему, вам нужно добавить ее в поведение масштабирования.

 //Zoom functionality  function zoomed() { const currentTransform = d3.event.transform; g.attr("transform", currentTransform); }  

здесь вы применяете свое преобразование к элементам, и это нормально. Однако вы не применяете никакой логики к радиусу. Эта логика зависит от вас, и она будет зависеть от k свойства события преобразования ( currentTransform.k ). Я буду использовать некоторую фиктивную логику для вашего радиуса. Ваш масштаб составляет от 1 до 10, вам нужна логика, в которой радиус уменьшается с увеличением масштаба (больше k). Также важно, чтобы ваш радиус не был меньше 1, потому что площадь круга будет уменьшаться намного быстрее (помните, что площадь зависит от r^2, а r^2 lt; r для r Поэтому моя логика будет такой: радиус равен 2,1 — (k / 10). Опять же, я упрощаю, вы можете изменить его или настроить для вашего конкретного случая.

В конце концов, это должно выглядеть примерно так:

 //Zoom functionality  function zoomed() { const currentTransform = d3.event.transform; g.attr("transform", currentTransform);  g.selectAll("circle")  .attr("r", 2.1 - (currentTransform.k / 10)) }  

Я не тестировал код, но скажите мне, работает ли это! Может быть, вы можете добавить его в jsfiddle, если это необходимо

Комментарии:

1. Да, большое спасибо, это как раз то, что я ищу. Переход немного грубоват и может быть более плавным, но я думаю, что его уже можно использовать. Вечером я создам скрипку, чтобы показать вам свой результат. (нужно переделать некоторые вещи, чтобы они работали)

2. @TeaAt5 чтобы сгладить переход, вы можете просто добавить событие перехода с общей продолжительностью для обоих преобразований