#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 чтобы сгладить переход, вы можете просто добавить событие перехода с общей продолжительностью для обоих преобразований