requestAnimationFrame 在另一个 requestAnimationFrame 里面

requestAnimationFrame inside another requestAnimationFrame

假设我在另一个 rAF.

中安排了 rAF

组件 1:

  update() {
    requestAnimationFrame(() => {
      this.div.addClass('animate1');
      component2.update();
    }
  }

组件 2:

  update() {
    requestAnimationFrame(() => {
      this.div.addClass('animate2');
    }
  }

它会 运行 在第一个重绘周期之后立即,还是在下一个重绘周期?

在下一个重绘周期。
requestAnimationFrame 视为 setTimeout(cb, time_until_next_monitor_refresh)

在您的回调被调用之前,在下次监视器刷新时,此 time_until_next_monitor_refresh 将再次重置为完整 frame-duration,因此无法从另一个调度动画帧回调动画帧回调在同一绘画帧中触发*。

请注意,无论如何,这就是基本的 requestAnimationFrame 循环的工作方式,它们会安排一个回调,该回调本身会安排一个新的回调。这个调度是从其他函数完成的,这不会改变任何事情(只要它在回调中全部同步)。

*嗯,浏览器确实有 bugs...

我想请求动画帧会在下一个重绘周期之前被触发,它会触发 2 个请求动画帧,样式会一个接一个地应用,但就在下一个重绘周期之前,所以假设你正在改变每个 rAf 中相同 div 的颜色然后会看到颜色闪烁。

在下面的两张图片中我们可以清楚地看到调用了多个动画帧,动画帧正在触发另一个raf,然后在下一帧之前被调用

这个问题的答案大多是:“是的,你可以。requestAnimationFrame 按加法顺序执行函数。”

但是,您可能会发现有趣的是,您的 rAF 的执行只是请求,并不能保证。如果您的 rAF 函数调用超过渲染下一帧所需的时间,它将无法及时完成。同样,如果您注册的 rAF 函数中只有一部分完成,则只有一部分函数有机会渲染。

在以下情况下,您应该使用一个 requestAnimationFrame 函数:

  • 性能是您应用中最重要的事情。
  • 如果您的应用正在放弃 rAF 函数调用:饱和导致帧无法完全呈现,或者您担心它可能会在您的路线图上的某个时刻出现。

如果满足以下条件,您可以使用多个 requestAnimationFrame 函数:

  • 你的任务很简单。
  • 您完全信任您的任务 运行 在 rAF 和渲染本身之间。又名:不是 'saturated'

因为 运行 更多功能需要更多时间(假设相同的任务)你会看到人们建议只使用 1 个功能作为 requestAnimationFrame 'rendering function',但你现在知道为什么并且可以自己决定。

解决一些评论:

@kaiido 说:“即使进行 rAF 调用的回调需要 20 秒,新的 rAF 回调也会在单独的帧中发生。”

让我们试试看:

首先,让我们制作一些工具来测试超时:

function timeout(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

现在我们可以使用async/await在代码中创建延迟:

requestAnimationFrame(async ()=>{
  await timeout(200);
  console.log('Non blocking first');
})
requestAnimationFrame(async ()=>{
  await timeout(100);
  console.log('Non blocking second');

})

在这种情况下,它们是 100%:输出是:

non blocking second
Non blocking first

较长的函数执行,等待,较短的函数运行s等待,但随后较短的先恢复,较长的在后。

但是,我认为这不是标准用例。您编写的代码更有可能阻止执行:

requestAnimationFrame(async ()=>{
  while (Date.now() < start + 3000) {}
  console.log('blocking first');
})
requestAnimationFrame(async ()=>{
  while (Date.now() < start + 1000) {}
  console.log('blocking second');
})

在这种情况下,正如我最初所说,函数是按顺序调用的,结果是

blocking first
blocking second

1 和 2 之间有 1000 毫秒的延迟。如果您的队列超出当前帧,则第二个函数启动的延迟将为 3000 毫秒 + 'the wait until next frame'。