请求不断调用的动画帧

Pra*_*ant 8 javascript browser rendering requestanimationframe

试图理解RequestAnimationFrame及其内部工作原理.

浏览器有一个主线程,它是一个事件循环.事件循环可以填充各种异步事件,例如用户交互,定时器被触发,网络调用完成以及事件触发布局和绘制,例如输入或JS.

因此,当JS函数使DOM的布局无效或导致重新绘制时,浏览器的主线程重新绘制需要更新的图层,合成器线程将更新的纹理上传到GPU,最终合成发生,并将生成的图像显示在屏幕上.

所以,我的印象是浏览器只在实际需要时执行绘制.如果您在静态页面上的Chrome开发工具时间轴上捕获事件而没有任何事情发生,则绝对不会捕获任何事件(没有布局,没有绘制,也没有触发动画帧).说得通.

但是,然后在控制台上运行以下代码,

function onBeforeNextRepaint() {
    requestAnimationFrame(onBeforeNextRepaint);

    console.log('About to paint ...');
}

onBeforeNextRepaint();
Run Code Online (Sandbox Code Playgroud)

现在,您再次捕获时间轴事件,并注意到"动画帧触发"事件,并且您的控制台将使用"关于绘制..."进行记录.

onBeforeNextRepaint被调用

动画帧被触发调用

根据MDN,

Window.requestAnimationFrame()方法告诉浏览器您希望执行动画并请求浏览器调用指定的函数以在下次重绘之前更新动画.

这意味着浏览器不断绘画,因此调用我的函数在每次重绘之前记录消息.我猜测浏览器维护一个调度程序,使绘制调用的速率与屏幕的刷新率相匹配.

现在我的困惑在于以下几点:

  1. 浏览器的主线程是否经常重新绘制,生成纹理并将纹理上传到GPU并进行绘制调用(限制以匹配屏幕的刷新率)?(听起来效率低下)
  2. 这就是我的'onBeforeNextRepaint'回调被不断调用的原因吗?
  3. 如果1和2为真,在我的第一个时间线捕获时,为什么我不捕获"更新图层树"和"复合图层"事件?

Ale*_*ara 8

我认为您误解了 MDN 给出的描述。让我分解一下。

Window.requestAnimationFrame() 方法告诉浏览器您希望执行动画

这意味着它会告诉浏览器您希望执行需要重新绘制的动画。但要做到这一点,我们需要运行一些代码。

并请求浏览器在下一次重绘之前调用指定的函数来更新动画。

这意味着就在动画需要的下一次重绘之前,调用我的回调函数。

requestAnimationFrame 不是绘制事件回调,而是一种在浏览器现在必须执行的下一次绘制之前触发回调的方法。


Izh*_*aki 7

以下博客文章真的帮助我理解了整个过程的运作方式.

从中:

本质上,整个事件循环函数可以用以下代码说明:

while (eventLoop.waitForTask()) {  
    const taskQueue = eventLoop.selectTaskQueue()
    if (taskQueue.hasNextTask()) {
        taskQueue.processNextTask()
    }

    const microtaskQueue = eventLoop.microTaskQueue
    while (microtaskQueue.hasNextMicrotask()) {
        microtaskQueue.processNextMicrotask()
    }

    if (shouldRender()) {
        applyScrollResizeAndCSS()
        runAnimationFrames()
        render()
    }
}
Run Code Online (Sandbox Code Playgroud)

并描绘如下:

显示任务/微任务/和动画帧队列的图表