【问题标题】:d3js v4: Scale circles with zoomd3js v4:缩放圆圈
【发布时间】:2021-12-22 01:43:35
【问题描述】:

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

【问题讨论】:

    标签: javascript svg d3.js topojson


    【解决方案1】:

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

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

    在这里,您将转换应用于元素,这很好。但是,您没有对半径应用任何逻辑。 该逻辑由您决定,它取决于转换事件 (currentTransform.k) 的 k 属性。 我将为您的半径使用一些虚拟逻辑。您的比例范围在 1 到 10 之间,您需要一个随着缩放增加(更大的 k)半径减小的逻辑。同样重要的是,您的半径不要低于 1,因为圆的面积会减小得更快(请记住,面积取决于 r^2,对于 r

    最后应该是这样的:

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

    我还没有测试过代码,但是告诉我这是否有效!如果需要,也许您可​​以将其添加到 jsfiddle

    【讨论】:

    • 是的,非常感谢,这正是我想要的。过渡有点粗鲁,可能会更流畅,但我认为它已经可以使用了。我将在晚上创建一个 Fiddle 向您展示我的结果。 (必须改造一些东西才能正常工作)
    • @TeaAt5 要平滑过渡,您可以简单地为两个转换添加一个具有共享持续时间的过渡事件
    猜你喜欢
    • 2016-02-27
    • 2017-04-09
    • 1970-01-01
    • 2012-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多