我有一个D3 v4.0.0-alpha.40 图表,它定期(不是每一帧):
translate(null)
transition()
个新滚动。 // Animation
function animateOnce(period) {
// Data: Add new, and remove old
data.unshift(Math.random());
data.pop();
// Do 2 transitions...
path
.attr("d", line)
.transition() /* ...reset */
.attr("transform", null)
.duration(0)
.transition()/* ...scroll across */
.duration(period)
.ease(d3.easeLinear)
.attr("transform", `translate(${x(1)})`)
};
// Animation looper
function animate() {
setTimeout(_=> {
animateOnce(PERIOD);
animate();
},
PERIOD);
}
然而,转换似乎没有干净地执行 - 每隔几秒就会有 jerk 。
我之前遇到过 D3 v3 这个问题,但我相信我通过添加重置转换(上面的步骤3)修复了它。不幸的是,我没有D3的经验,我不知道如何解决这个问题。
This jsFiddle是我的图表的近似值,您应该能够看到偶尔的混蛋。
注意:小提琴使用setTimeout
,而我的实际图表是反应组件,使用componentDidUpdate()
更新。
编辑1:改进使用中断
在阅读文档时(正如@Ashitaka建议的那样) - 我找到了interrupt()。这会正确杀死转换,可能是实现上述步骤3 的“ v4 方式”(重置转换)。
// Animation
function animateOnce(period) {
// Data: Add new, and remove old
data.unshift(Math.random());
data.pop();
// Do 2 transitions...
path
.attr("d", line)
.interrupt() /* ...reset */
.attr("transform", null)
.transition()/* ...scroll across */
.duration(period)
.ease(d3.easeLinear)
.attr("transform", `translate(${x(1)})`)
};
这改善了 jerks (由竞争过渡引起的假设),将它们变成小的口吃。
我想了解(我假设1帧)口吃被引入的地方。所以我现在就把它打开。
答案 0 :(得分:3)
在Mike Bostock的帖子Working with Transitions中,他写道:
对于给定元素,转换是独占的:只有一个转换 可以同时在元素上运行。开始新的 元素上的转换会停止已经发生的任何转换 运行
现在,我在现有代码中发现了两个问题:
路径转换重置正在动画化(即使持续时间为0
)。这一新的转变正在取消之前的过渡。这可以通过更改:
path
.attr("d", line)
.transition()
.attr("transform", null)
.duration(0)
为:
path
.attr("d", line)
.attr("transform", null)
调用animateOnce
函数与D3的转换和a transition tick lasts for ~17 ms的周期相同。这一新的转变也取消了之前的过渡。这可以通过更改:
function animate() {
setTimeout(_=> {
animateOnce(PERIOD);
animate();
},
PERIOD);
}
为:
function animate() {
setTimeout(_=> {
animateOnce(PERIOD);
animate();
},
PERIOD + 20);
}
可以使用setInterval进一步重构为:
function animate() {
setInterval(animateOnce, PERIOD + 20, PERIOD);
}
这2个变化应解决jank问题。尽管如此,每隔80毫秒更新一次折线图总会对某人的电脑或智能手机造成负担。我建议你每200毫秒才更新一次。
编辑:我做了一些实验,发现Firefox上还有一些东西。所以,还需要考虑几点:将transform
属性设置为null
实际上会创建一个新的layer。这可以通过更改:
.attr("transform", null)
为:
.attr("transform", "translate(0)")
每次调用animateOnce
函数时都会重新创建转换字符串。我们可以在animateOnce
之外预先计算它们,然后重复使用它们。这可以通过更改:
.attr("transform", "translate(0)")
.attr("transform", `translate(${x(1)})`)
为:
// outside the animateOnce function:
let startStepAttrs = { transform: "translate(0)" },
endStepAttrs = { transform: `translate(${x(1)})` };
// inside the animateOnce function:
.attr(startStepAttrs)
.attr(endStepAttrs)