每个函数和 this 关键字如何在 d3 中协同工作?[链接转换]

How does each function and this keyword work together in d3?[chaining transitions]

我正在使用 d3 库并且很难理解一些结构,希望有经验的人给我一些指导!

<!DOCTYPE html>
<html lang="en">
  <head>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>

  </head>
  <body>
  <script>
var svg=d3.select("body").append("svg").attr("width",800).attr("height",1000);
svg.selectAll("circle")
.data(d3.range(10)).enter().append("circle")
.attr("cx",function(d){return d;})
.attr("cy",function(d){return d*50;})
.attr("r",function(d){return d;})
.transition().duration(4000).style("fill","red")
.each("end",svg.selectAll(this).transition().duration(4000)
.attr("cx",function(d){return d*10;}));
</script>   
  </body>
  </html>

据我所知,这应该创建圆圈,然后过渡颜色以用红色填充它们,然后更改每个圆圈的 "cx" 属性。至少,这就是我想做的。有人可以解释一下方法链接的确切工作原理以及如何使用 each() 函数和 this 关键字吗?

在这种情况下,您的 'this' 将不会引用任何 d3 对象,因为 this 是在与您的 <script> 标记相同的范围内计算的,后者在非严格模式下引用到 window 对象。

我建议您尝试从不过渡开始,然后在基础工作正常后逐渐添加。

你的代码的基本轮廓是这样的:

  1. 根据范围1-10生成数据集
  2. 如果这些数据对象分别创建一个圆圈
  3. 所有的对象都引用了它们各自的值,所以它们实际上是数字,你用它来计算它们的位置(x,y)和大小(r)
  4. 然后你转换他们的背景
  5. 然后你告诉它监听一个"end"事件并执行一段代码。

第 5 步失败了。我认为这基本上就是您想要的:

var svg=d3.select("body").append("svg").attr("width",800).attr("height",1000);

svg.selectAll("circle")
   .data(d3.range(10))
   .enter()
      .append("circle") 
         .attr("cx",function(d){return d;})
         .attr("cy",function(d){return d*50;})
         .attr("r",function(d){return d;})
         .transition()
             .duration(4000)
             .style("fill","red")
             // attach a handler to all affected objects and listen
             // to the "end" event:
             .each("end", function() { console.log(this, arguments); })
         .transition()
             .duration(4000)
             .attr("cx",function(d){return d*10;})
             // do it again
             .each("end", function() { console.log(this, arguments); })
;

默认情况下 transition 效果是链接的(参见 https://github.com/mbostock/d3/wiki/Transitions#transition

根据我的经验,很多 为 d3 考虑一个合理的缩进会有所帮助,因为它可以帮助您了解 'scope' 目前正在做什么。

顺便说一句,您的代码中还没有随机组件,所以不要指望它会做任何您不告诉它做的事情;)

您在调试器中回答此类问题的方式。为了完全确定答案,我提取了可以设置断点的内联函数:

function resize() {
    svg.selectAll(this).transition().duration(4000) //set breakpoint here
        .attr("cx",function(d){return d*10;})
}  

var svg=d3.select("body").append("svg")
          .attr("width",800)
          .attr("height",1000);

svg.selectAll("circle")
    .data(d3.range(10)).enter().append("circle")
    .attr("cx",function(d){return d;})
    .attr("cy",function(d){return d*50;})
    .attr("r",function(d){return d;})
    .transition().duration(4000).style("fill","red")
    .each("end",resize);

当您设置断点时,您会看到 this 的值可能是您期望的值:当前 DOM 元素,一个 '[object SVGCircleElement]'。您可能还会检查 arguments 并发现传递给 resize 的参数是 di。这可能不是一个令人满意的答案,因为您的代码不起作用。

您的下一个问题可能是如何调整大小才能正常工作。我不知道为什么 svg.selectAll(this) 不起作用,但如果我们调用 d3.select(this) ,该方法确实可以正常工作。

function resize(d,i) {
    d3.select(this).transition().duration(4000)
        .attr("cx",function(d){return d*10;})
}

如果您想内联调整大小,请不要忘记将您的参数设置为回调,而不是声明:

.each("end",function (d) { d3.select(this).transition().duration(4000).attr("cx",d*10) });

另外,您可以看到 transition.each([type],listener) 的文档说应该首选链式转换 (transition.transition)。实现所需功能的标准方法只是链接转换:

.transition()
    .duration(4000)
    .style("fill","red")
.transition()
    .duration(4000)
    .attr("cx", function (d) { return d*10});