为什么我的D3缩放变换不能正确居中?

3
我正在将我的地图升级到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");

enter image description here

然而,当点击一个州并运行点击函数时,我的转换似乎出了问题。在我的情况下,阿肯色州被点击了(用蓝色阴影表示)。

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");
}

enter image description here

我的想法是,我的质心计算需要略微调整,以适应地图的较小大小或不同位置,但这似乎也不正确。

我该如何进行适当的调整?

编辑:我发现如果在变换的末尾添加一个“负翻译”(translate(" + -x + "," + -y + ")),它可以更接近于正确地居中缩放,但并非完美。

1个回答

8
您缺少了示例中的两个变换之一;但请继续阅读以获得更简单的解决方案。
您使用的示例有两个嵌套的变换。首先是外部 G 元素上的静态变换:
var g = svg.append("g")
    .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
  .append("g")
    .attr("id", "states");

这个变换的作用与投影.translate通常相同,但这个例子使用了translate([0, 0])。(正如我所说,会有更简单的解决方案...)
第二个变换是动态设置在内部G(id为 "states")上进行缩放:
g.transition()
    .attr("transform", "scale(" + k + ")translate(" + x + "," + y + ")");

请注意,这里的 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>

您的导出中缺少嵌套的内部 G。因此,当您在 #states G 元素上设置“transform”属性时,会覆盖外部转换,从而得到以下结果:
<g id="states" transform="scale(4)translate(18.936679862557288,-109.8787070159044)">
  …
</g>

所以,你缺失了静态变换 "translate(480,250)"。

我建议将这些变换组合在一起。然后你就不需要外部的G了,并且你可以:

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

这也消除了将投影的平移设置为 [0, 0] 的需要,因此您可以改用标准的平移 [width / 2,height / 2]。 我已经更新了示例来实现这一点!

我知道我的东西有些不对齐!感谢您的更新和简化。这个工作起来就像魔法一样! - Dillie-O
@mbostock,您能否请看一下我的问题:http://stackoverflow.com/questions/30750044/how-to-split-multiple-d3-transform-commands-into-separate-statements 它与这个问题非常相似。 - kwoxer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接