首页 > 解决方案 > 滚动事件上带有 clearAnimationFrame 的 requestAnimationFrame

问题描述

关于 JavaScript 已经有很多问题了,(我认为)我理解这个概念,但是在这种情况下requestAnimationFrame,有和没有之间有什么性能差异吗?cancelAnimationFrame

// Setup a timer
var timeout;

// Listen for resize events
window.addEventListener('scroll', function () {

    console.log( 'no debounce' );

    // Cancel last animation, if there's one
    if (timeout) {
        window.cancelAnimationFrame(timeout);
    }

    // Setup the new requestAnimationFrame()
    timeout = window.requestAnimationFrame(function () {

        // Run our scroll functions
        console.log( 'debounced' );

    });

}, false);

没有cancelAnimationFrame

// Setup a timer
var timeout;

// Listen for resize events
window.addEventListener('scroll', function () {

    console.log( 'no debounce' );


    // Setup the new requestAnimationFrame()
    window.requestAnimationFrame(function () {

        // Run our scroll functions
        console.log( 'debounced' );

    });

}, false);

我在每个代码上得到相同的结果。

但我想知道如果我不取消动画帧会发生什么。请求的函数是否堆积在内存中的某个地方或其他地方?

标签: javascriptrequestanimationframecancelanimationframe

解决方案


var isRafLogged = false;

function rafCb(now) {
  if (isRafLogged) {
    console.log('rAF callback executed at: ', now, 'ms');
  }
  requestAnimationFrame(rafCb);
}

function onWindowScroll() {
  // when in scroll, log aforescheduled rAF() only when in scroll
  isRafLogged = true;
  const now = performance.now();

  console.log('scroll callback executed at: ', now, 'ms');

  // when out of scroll, stop logging the rAF
  setTimeout(function() {
    isRafLogged = false;
  });
}

requestAnimationFrame(rafCb);

window.addEventListener('scroll', onWindowScroll);
html,
body {
  height: 10000px;
}

p {
  font-size: 200px;
  writing-mode: vertical-lr;
}
<p>.....................................................................................................................................................................................................................................................................................................................................................................</p>

如果我们同时安排一个连续的单独requestAnimationFrame循环,我们将清楚地scroll看到每个VSync 事件最多发生一次回调。rAFscroll

因此回到你的主要问题

在这种情况下,使用和不使用 cancelAnimationFrame 是否有任何性能差异?

通常不会,因为您的requestAnimationFrame()调用阻止了下一个scroll回调,并且执行的滚动回调不能多于请求的帧回调,因此存在 1 比 1 的相关性,因为它们都在每帧渲染的最大值处发生。

但我想知道如果我不取消动画帧会发生什么。请求的函数是否堆积在内存中的某个地方或其他地方?

所有请求的动画帧回调都堆叠在内部回调池中,在最近的 Vsync 事件之前被刷新。所以是的,从技术上讲,删除预定回调的唯一方法是cancelAnimationFrame(),但同样与您的情况无关,因为您的回调与窗口回调requestAnimationFrame()“同时”发生。scroll

希望这是有道理的。


推荐阅读