我希望转换绿色圆圈然后转换为红色圆圈。转换绿色圆圈有效但当我转换红色圆圈时,绿色圆圈会恢复到原始位置。我怎么能避免这个?
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>
答案 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
}
});
以下是演示:
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;