61

如果我正在做多个动画,添加多个requestAnimationFrame回调是否可以提高性能?前:

function anim1() {
    // animate element 1
}

function anim2() {
    // animate element 2
}

function anim3() {
    // animate element 3
}

requestAnimationFrame(anim1);
requestAnimationFrame(anim2);
requestAnimationFrame(anim3);

或者它被证明比使用单个回调更糟糕:

(function anim() {
    requestAnimationFrame(anim);
    anim1();
    anim2();
    anim3();
}());

我问是因为我真的不知道幕后发生了什么,requestAnimationFrame当你多次调用它时排队回调?

4

4 回答 4

12

我认为这些答案中的任何一个都不能真正解释我正在寻找的内容:“不要调用 requestAnimationFrame”得到去抖动(即每帧出列 1 个)或全部在下一帧中被调用。

当 requestAnimationFrame() 排队的回调开始在单个帧中触发多个回调时 ( mdn )

这表明后者,可以在同一帧中调用多个回调。

我通过以下测试确认。60 赫兹的刷新率转换为 17 毫秒的周期。如果是前者,则没有 2 个时间戳彼此相差 17 毫秒,但事实并非如此。

let sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

let update = async timestamp => {
  console.log('update called', timestamp)
  await sleep(10);
  requestAnimationFrame(update);
}

requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);

于 2020-04-22T14:22:23.120 回答
8

您应该只使用一个requestAnimationFrame调用作为调用requestAnimationFrame堆栈。因此,单个回调版本的性能更高。

于 2013-06-14T08:10:16.870 回答
7

有人对此进行了基准测试。让我们谈谈...

https://jsperf.com/single-raf-draw-calls-vs-multiple-raf-draw-calls

我查看了性能比较(你也应该这样做)。欢迎你不同意。这些是在画布元素上绘制图元。

        function timeStamp() {
          return window.performance && window.performance.now ? window.performance.now() : new Date().getTime();
        }

        function frame() {
            drawCircle();
            drawLines();
            drawRect();
        }

        function render() {
            if (timeStamp() >= (time || timeStamp())) {
                time = timeStamp() + delayDraw;
                frame();
            } 
            requestAnimationFrame(render);
        }

        function render1() {
            if (timeStamp() >= (time || timeStamp())) {
                time = timeStamp() + delayDraw;
                drawCircle();
            } 
            requestAnimationFrame(render1);
        }

        function render2() {
            if (timeStamp() >= (time || timeStamp())) {
                time = timeStamp() + delayDraw;
                drawRect();
            } 
            requestAnimationFrame(render2);
        }

        function render3() {
            if (timeStamp() >= (time || timeStamp())) {
                time = timeStamp() + delayDraw;
                drawLines();
            } 
            requestAnimationFrame(render3);
        }

我认为这段代码实际上是对 7 次 timestamp() 调用和 2 次 timestamp() 调用进行基准测试。看看 Chrome 46 和 47 的区别。

  • Chrome 46: 12k / 秒(一次调用)vs 12k / 秒(3 次调用)
  • Chrome 47: 270k /秒(一次调用)vs 810k /sec(3次调用)

我认为这是非常优化的,它没有任何区别。这只是在这一点上测量噪声。

我的收获是这不需要为我的应用程序手动优化。

如果您担心性能,请查看 Chrome 59 (1.8m ops/sec) 与 Chrome 71 (506k ops/sec) 之间的差异。

于 2019-03-24T17:03:35.293 回答
-1

requestAnimationFrame 绑定一个函数调用并返回 frameID。请求多个帧与向一个事件添加多个事件侦听器不同——您的每个函数都在另一个帧中调用。因此,如果您连续(每个函数递归调用自身)请求多个帧,您将失去所有更新都在一帧内呈现的好处。所以即使有高帧率的动画也可能看起来不那么流畅。

但是:您只能对所有方法使用 cancelAnimationFrame(frameID),并且可能需要一些额外的代码来取消单个动画

于 2018-03-25T22:18:15.820 回答