条件转换不符合预期

时间:2018-01-17 16:28:05

标签: javascript d3.js svg

我希望转换绿色圆圈然后转换为红色圆圈。转换绿色圆圈有效但当我转换红色圆圈时,绿色圆圈会恢复到原始位置。我怎么能避免这个?

https://jsfiddle.net/jtr13/cwckLv29/1/

<svg width="300" height="300">
    <circle cx="50" cy="100" r="20" fill="green">
    </circle>
    <circle cx="100" cy="150" r="20" fill="red">
    </circle>
  </svg>
<script>
    d3.selectAll("circle")
        .transition().duration(1000)
        .attr("cx", function() {
        if (d3.select(this)
            .style('fill') === "rgb(0, 128, 0)") {  // green circle gets new cx
            return 150;
        } else {
            return d3.select(this).attr("cx")  // red circle keeps cx
        } } );

    d3.selectAll("circle")
        .transition().duration(1000).delay(1000)
        .attr("cx", function() {
        if (d3.select(this)
            .style('fill') === "rgb(255, 0, 0)") { // red circle gets new cx
            return 200;
        } else {
            return d3.select(this).attr("cx")  // green circle reverts to 
                                  // original cx, not changed cx... why???
        } } );

  </script>  

2 个答案:

答案 0 :(得分:4)

转换中attr和attrTween之间存在重要但微妙的区别。你遇到这个问题的原因源于这种区别,虽然可能有更简单的方法来解决这个问题,而不用担心它的机制,我的答案会看看问题的原因并提供与之相关的解决方案。

首先,通过对转换的一些修改,我们可以更清楚地看到问题:

	var svg = d3.select("svg");
	

 d3.selectAll("circle")
   .transition().duration(1000)
   .on("start",function() { console.log("start transition1"); })
   .attr("cx",150);

 d3.selectAll("circle")
   .transition()
   .delay(1000)
   .on("start",function() { return console.log("start transition2"); })
   .attr("cx", function() { 
       console.log("transition2 cx value: ", d3.select(this).attr("cx")); 
       return d3.select(this).attr("cx") 
	});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>


<svg width="300" height="300">
    <circle cx="50" cy="100" r="20" fill="green">
    </circle>
</svg>

在转换2开始之前确定用于转换2的结束点的值,实际上它是在转换1开始之前确定的!由于transition.attr()中使用的值是在转换开始之前确定的,因此在转换初始化时,在第一次转换之后不会更新该值。如果使用.transition()。transition()(因为它在功能上与您使用的方法不同),也是如此。

从d3的转换文档的生命周期开始:

  

指定目标值的方法(例如transition.attr)是   同步评估;但是,需要启动的方法   插值的值,例如transition.attrTween和   transition.styleTween必须延迟到转换开始。 (link)。

因此,一种解决方案是使用attrTween。这将允许在转换开始时计算cx值,而不是初始化(我在下面的片段中使用填充转换来显示第二个转换正在工作,因为cx未被修改):

var svg = d3.select("svg");
	

 d3.selectAll("circle")
   .transition().duration(1000)
   .on("start",function() { console.log("start transition1"); })
   .attr("cx",150)
   
   
 d3.selectAll("circle")
   .transition()
   .delay(1000)
   .duration(1000)
   .on("start",function() { return console.log("start transition2"); })
   .attr("fill","steelblue")
   .attrTween("cx", function() { 
       var x = d3.select(this).attr("cx");
       console.log("transition2 cx value: ", x);
       return function() { return  x };
	});
  
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>


<svg width="300" height="300">
    <circle cx="50" cy="100" r="20" fill="green">
    </circle>
</svg>

根据控制台,您可以看到cx的值,该值是转换开始时的当前值。

或者,您可以选择不进行初始化转换,直到转到上一个转换结束。使用转换的结束事件,我们可以创建一个使用cx的当前值初始化的新转换:

var svg = d3.select("svg");
	

 d3.selectAll("circle")
   .transition().duration(1000)
   .on("start",function() { console.log("start transition1"); })
   .attr("cx",150) 
   .on("end", function() {
     d3.select(this).transition()
       .duration(1000)
       .on("start",function() { return     console.log("start transition2"); })
       .attr("fill","steelblue")
       .attr("cx", function() { 
         var x = d3.select(this).attr("cx");
         console.log("transition2 cx value: ", x);
         return x;
	     });
   
   
   })
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>


<svg width="300" height="300">
    <circle cx="50" cy="100" r="20" fill="green">
    </circle>
</svg>

答案 1 :(得分:2)

您尝试做的不是惯用的D3,而且由于Andrew in his answer已经解释过的原因,它会失败。只要做每个人都做的事:.on("end", etc...

然而,只是为了好奇和完整,你正在尝试做的事情(几乎)是可能的!只需绑定&#34;首先&#34;转换并使用绑定数据:

d3.selectAll("circle")
  .transition().duration(1000)
  .attr("cx", function(d) {
    if (d3.select(this)
      .style('fill') === "rgb(0, 128, 0)") {
      return d.x = 150;
    } else {
      return d.x = d3.select(this).attr("cx")
    }
  });

d3.selectAll("circle")
  .transition().duration(1000).delay(1000)
  .attr("cx", function(d) {
    if (d3.select(this)
      .style('fill') === "rgb(255, 0, 0)") {
      return 200;
    } else {
      return d.x
    }
  });

以下是演示:

&#13;
&#13;
d3.selectAll("circle").each(function() {
  d3.select(this).datum({
    x: 0
  })
});

d3.selectAll("circle")
  .transition().duration(1000)
  .attr("cx", function(d) {
    if (d3.select(this)
      .style('fill') === "rgb(0, 128, 0)") {
      return d.x = 150;
    } else {
      return d.x = d3.select(this).attr("cx")
    }
  });

d3.selectAll("circle")
  .transition().duration(1000).delay(1000)
  .attr("cx", function(d) {
    if (d3.select(this)
      .style('fill') === "rgb(255, 0, 0)") {
      return 200;
    } else {
      return d.x
    }
  });
&#13;
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="300" height="300">
  <circle cx="50" cy="100" r="20" fill="green">
  </circle>
  <circle cx="100" cy="150" r="20" fill="red">
  </circle>
</svg>
&#13;
&#13;
&#13;