为什么我的D3变焦转换不能正确居中?

Dil*_*e-O 3 zoom map translate scale d3.js

我正在将我的地图升级到D3的v3并使用此处找到的D3示例代码中概述的点击放大转换.

我的代码几乎相同,只是我的地图尺寸略小(564 x 300而不是960 x 500).另外,我的地图嵌套在div中,并且在我页面的左上角(虽然我不认为这很重要)

我的地图加载的初始加载很好(目前使用黑色背景区分)

// Clear existing map in case there is remnant data.
$("#map").html(null);

var mapWidth = 564;
var mapHeight = 300;

var projection = d3.geo.albersUsa()
                          .scale(mapWidth)
                          .translate([0, 0]);   

var path = d3.geo.path()
                 .projection(projection);

var svg = d3.select("#map")
               .append("svg")
               .attr("id", "map-svg")
               .attr("width", mapWidth)
               .attr("height", mapHeight);

   svg.append("rect")
    .attr("id", "map-background")
    .attr("class", "background")
    .attr("width", mapWidth)
    .attr("height", mapHeight)
    .on("click", click);

   // Create placeholders for shapes and labels
   var states = svg.append("g")
                   .attr("transform", "translate(" + mapWidth / 2 + "," + mapHeight / 2 + ")")
                   .attr("id", "states");
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

但是,当单击一个状态,并且单击函数运行时,我的转换似乎已关闭.在我的情况下,点击阿肯色州(用蓝色阴影表示)

function click(d)
{
   var x = 0,
       y = 0,
       k = 1;

   if (d && centered !== d)
   {
      var centroid = path.centroid(d);
      x = -centroid[0];
      y = -centroid[1];
      k = 4;
      centered = d;      
   }
   else
   {
      centered = null;
   }

   d3.select("#states").selectAll("path")
       .classed("active", centered && function (d) { return d === centered; });

   d3.select("#states").transition()
       .duration(1000)
       .attr("transform", "scale(" + k + ")translate(" + x + "," + y + ")")
       .style("stroke-width", 1.5 / k + "px");
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

我唯一的想法是我的质心计算需要针对较小的尺寸或地图的不同位置进行轻微调整,但这似乎也不正确.

如何进行适当的调整?

编辑:我发现如果我在变换的末尾添加一个"负面翻译"(translate("+ -x +","+ -y +")),它会更接近正确的缩放中心,但不是完美

mbo*_*ock 8

您错过了示例中的两个转换之一; 但请继续阅读更简单的解决方案.

您使用的示例有两个嵌套转换.首先,对外部G元素进行静态转换:

var g = svg.append("g")
    .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
  .append("g")
    .attr("id", "states");
Run Code Online (Sandbox Code Playgroud)

此变换与projection.translate通常具有相同的目的,但示例使用translate([0,0]).(正如我所说,将有一个更简单的解决方案...)

第二个变换是在内部 G(带有"状态")上动态设置的,可以放大:

g.transition()
    .attr("transform", "scale(" + k + ")translate(" + x + "," + y + ")");
Run Code Online (Sandbox Code Playgroud)

请注意,var g这里指的是内部G元素,因为在g定义时,有一个append("g")链接了第二个append("g"); 因此,变量被定义为第二个内部G,而不是第一个外部G.

生成的SVG如下所示:

<g transform="translate(480,250)">
  <g id="states" transform="translate(75.746,-439.514)scale(4,4)">
    …
  </g>
</g>
Run Code Online (Sandbox Code Playgroud)

你的派生缺少嵌套的内部G.所以当你在#states G元素上设置"transform"属性时,你会覆盖外部变换,给你:

<g id="states" transform="scale(4)translate(18.936679862557288,-109.8787070159044)">
  …
</g>
Run Code Online (Sandbox Code Playgroud)

所以,你错过了静态变换,"translate(480,250)".

我建议将这些变换组合在一起.那你就不需要外面的G了,你可以:

g.transition()
    .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"
        + "scale(" + k + ")"
        + "translate(" + x + "," + y + ")");
Run Code Online (Sandbox Code Playgroud)

这也消除了将投影的平移设置为[0,0]的需要,因此您可以使用标准平移[width/2,height/2].我已经更新了这个例子!