偶然在`translate()`(v4.0.0-alpha40)

时间:2016-05-19 14:26:26

标签: javascript d3.js babeljs

问题:Jerks

我有一个D3 v4.0.0-alpha.40 图表,它定期(不是每一帧):

  1. 获取新数据点
  2. 删除旧数据点
  3. 重置转场并设置translate(null)
  4. 在获取新值时开始新的transition()个新滚动。
  5. // 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:改进使用中断

    Improved jsFiddle

    在阅读文档时(正如@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帧)口吃被引入的地方。所以我现在就把它打开。

1 个答案:

答案 0 :(得分:3)

在Mike Bostock的帖子Working with Transitions中,他写道:

  

对于给定元素,转换是独占的:只有一个转换   可以同时在元素上运行。开始新的   元素上的转换会停止已经发生的任何转换   运行

现在,我在现有代码中发现了两个问题:

  1. 路径转换重置正在动画化(即使持续时间为0)。这一新的转变正在取消之前的过渡。这可以通过更改:

    来解决
    path
      .attr("d", line)
      .transition()
        .attr("transform", null)
        .duration(0)
    

    为:

    path
      .attr("d", line)
      .attr("transform", null)
    
  2. 调用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);
    }
    
  3. 这2个变化应解决jank问题。尽管如此,每隔80毫秒更新一次折线图总会对某人的电脑或智能手机造成负担。我建议你每200毫秒才更新一次。

    编辑:我做了一些实验,发现Firefox上还有一些东西。所以,还需要考虑几点:

    1. transform属性设置为null实际上会创建一个新的layer。这可以通过更改:

      来解决
      .attr("transform", null)
      

      为:

      .attr("transform", "translate(0)")
      
    2. 每次调用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)