使用 setTimeout 延迟 d3 转换

Using setTimeout to delay d3 transition

我正在尝试沿着螺旋曲线创建一个简单的平面动画

但是,我在尝试使用 d3 的延迟功能延迟转换时遇到问题,动画似乎没有正确进行

所以我一直在考虑使用setTimeout来延迟触发转换函数,但似乎无法正确实现。有人可以给我建议吗?

Link to the fiddle。这包括没有 setTimeout 或任何 .delay 的原始转换; v15 显示使用 .delay 的错误; v16 有我用 setTimeout

尝试过的东西

这是我在没有 setTimeout 的情况下进行转换的代码(在没有 .delay 的情况下工作正常)

function transitionThis(d,i) {
d3.select(this).transition()
.duration(3000)
//.delay(3000) //causes error in the animation
.ease("exp")
.each("start", function() { d3.select(this).style("opacity", "1"); })    
  .attrTween("transform", translateAlong(path.node()))
  .styleTween("opacity", function () {return d3.interpolate("1", "0");});}

plane.each(transitionThis);

// Returns an attrTween for translating along the specified path element.
function translateAlong(path) {
  var l = path.getTotalLength();
    var t0 = 0;
    return function(i) {
  return function(t) {
    var p0 = path.getPointAtLength(t0 * l);//previous point
    var p = path.getPointAtLength(t * l);////current point
    var angle = Math.atan2(p.y - p0.y, p.x - p0.x) * 180 / Math.PI;//angle for tangent
    t0 = t;
  return "translate(" + p.x + "," + p.y +  ")scale(" + (0.2+t*4) + ")rotate(" + angle +"15"+")";
};
  };
}

这是我尝试添加 setTimeout

function transitionThis(d,i) {
d3.select(this).transition()
.duration(3000)
//.delay(3000) //causes error in the animation
.ease("exp")
.each("start", function() { d3.select(this).style("opacity", "1"); })    
  .attrTween("transform", translateAlong(path.node()))
  .styleTween("opacity", function () {return d3.interpolate("1", "0");});
}

function delayedFlight() {
var tOut = setTimeout(transitionThis(),3000);
}
plane.each(delayedFlight);

事情是这样的:出于某种原因,将 .delay() 添加到转换中使得每隔一段时间插值函数(returns 转换值)被称为两个连续多次具有相同的值 t。例如,这是我插入 console.log(t):

时得到的输出的一个子集
0.006692062648206693
0.006707542472169953
0.007238969233518517
0.007255714140926161
0.0077049430038543705
0.0077049430038543705 // <- same as previous
0.008568945153864268
0.008588766510821856
0.00899496468906235
0.00899496468906235   // <- same as previous
0.009529833123126597
0.00955187716946928
0.01107410052517391
0.011099716711945125
0.012516716837337842

我不是为什么会这样,但从技术上讲,这不是错误,只是一个怪癖。但是,它会导致插值函数出现某种错误:当 t0t 相等时,p0p 也相等,因此计算出的 angle 对于那些间歇性的情况是 0。那就是当你看到飞机断断续续的时候——只要它的旋转设置为 0。

最简单的修复方法是使 t0t 永远不相等,这可以通过拦截该条件并稍微修改 t0 来实现。像这样:

    // Happens every once in a while
    if (t == t0) {
        // Move t0 "back in time" a bit
        t0 -= .00001;
    }

它不是很漂亮,但可能仍然比进入 setTimeouts 更好....

Here's the modified fiddle