d3js v4:缩放圆圈

d3js v4: Scale circles with zoom

我有一张用 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 > 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; })
            });

如果我正确理解了你的问题,你需要将它添加到你的缩放行为中。

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

在这里,您将转换应用于元素,这很好。但是,您没有对半径应用任何逻辑。 该逻辑由您决定,并且取决于转换事件 (currentTransform.k) 的 k 属性。 我将为您的半径使用一些虚拟逻辑。您的缩放范围在 1 到 10 之间,您需要一个逻辑,其中半径随着缩放的增加而减小(更大的 k)。同样重要的是你的半径不要低于 1,因为圆的面积会减少得更快(记住面积取决于 r^2,r^2 < r for r < 1) 所以我的逻辑是:半径是 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