请求不断调用的动画帧
Request animation frame called constantly
试图了解 RequestAnimationFrame 及其内部工作原理。
浏览器有一个主线程,它是一个事件循环。事件循环可以填充各种异步事件,例如用户交互、定时器被触发、网络调用完成以及触发布局和绘制的事件,例如输入或JS。
因此,当 JS 函数使 DOM 的布局无效或导致重新绘制时,浏览器的主线程重新绘制需要更新的图层,合成器线程将更新后的纹理上传到 GPU,最终进行合成并将生成的图像显示在屏幕上。
因此,我的印象是浏览器仅在实际需要时才执行绘制。如果您在静态页面上捕获 Chrome Dev Tools 时间轴上的事件而没有发生任何事情,则绝对不会捕获任何事件(没有布局、没有绘制、没有触发动画帧)。说得通。
但是你 运行 在控制台上输入以下代码,
function onBeforeNextRepaint() {
requestAnimationFrame(onBeforeNextRepaint);
console.log('About to paint ...');
}
onBeforeNextRepaint();
现在,您再次捕获时间线事件并注意到 'Animation Frame Fired' 事件并且您的控制台被记录为 'About to paint ...'。
根据MDN,
The Window.requestAnimationFrame() method tells the browser that you wish to perform an animation and requests that the browser call a specified function to update an animation before the next repaint.
这意味着浏览器一直在绘制,因此在每次重新绘制之前调用我的函数来记录消息。我猜浏览器会维护一个调度程序,该调度程序会以与屏幕刷新率相匹配的速率进行绘制调用。
现在我的困惑在于:
- 浏览器的主线程是否不断地重新绘制、生成纹理并将纹理上传到 GPU 并进行绘制调用(节流以匹配屏幕的刷新率)? (听起来效率低下)
- 这就是我的 'onBeforeNextRepaint' 回调不断被调用的原因吗?
- 如果 1 和 2 为真,在我的第一次时间线捕捉中,为什么我没有捕捉到 'Update Layer Trees' 和 'Composite Layers' 事件?
我认为你误解了MDN给出的描述。让我分解一下。
The Window.requestAnimationFrame() method tells the browser that you wish to perform an animation
这意味着它会告诉浏览器您希望预制需要重新绘制的动画。但为此,我们需要 运行 一些代码。
and requests that the browser call a specified function to update an animation before the next repaint.
这意味着在动画需要的下一次重绘之前,调用我的回调函数。
requestAnimationFrame
不是绘制事件回调,而是一种在浏览器现在必须执行的下一次绘制之前触发回调的方法。
以下 blog post 确实帮助我理解了整个事情的运作方式。
来自:
从本质上讲,整个事件循环功能可以用这段代码来说明:
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()
}
}
然后这样描绘:
试图了解 RequestAnimationFrame 及其内部工作原理。
浏览器有一个主线程,它是一个事件循环。事件循环可以填充各种异步事件,例如用户交互、定时器被触发、网络调用完成以及触发布局和绘制的事件,例如输入或JS。
因此,当 JS 函数使 DOM 的布局无效或导致重新绘制时,浏览器的主线程重新绘制需要更新的图层,合成器线程将更新后的纹理上传到 GPU,最终进行合成并将生成的图像显示在屏幕上。
因此,我的印象是浏览器仅在实际需要时才执行绘制。如果您在静态页面上捕获 Chrome Dev Tools 时间轴上的事件而没有发生任何事情,则绝对不会捕获任何事件(没有布局、没有绘制、没有触发动画帧)。说得通。
但是你 运行 在控制台上输入以下代码,
function onBeforeNextRepaint() {
requestAnimationFrame(onBeforeNextRepaint);
console.log('About to paint ...');
}
onBeforeNextRepaint();
现在,您再次捕获时间线事件并注意到 'Animation Frame Fired' 事件并且您的控制台被记录为 'About to paint ...'。
根据MDN,
The Window.requestAnimationFrame() method tells the browser that you wish to perform an animation and requests that the browser call a specified function to update an animation before the next repaint.
这意味着浏览器一直在绘制,因此在每次重新绘制之前调用我的函数来记录消息。我猜浏览器会维护一个调度程序,该调度程序会以与屏幕刷新率相匹配的速率进行绘制调用。
现在我的困惑在于:
- 浏览器的主线程是否不断地重新绘制、生成纹理并将纹理上传到 GPU 并进行绘制调用(节流以匹配屏幕的刷新率)? (听起来效率低下)
- 这就是我的 'onBeforeNextRepaint' 回调不断被调用的原因吗?
- 如果 1 和 2 为真,在我的第一次时间线捕捉中,为什么我没有捕捉到 'Update Layer Trees' 和 'Composite Layers' 事件?
我认为你误解了MDN给出的描述。让我分解一下。
The Window.requestAnimationFrame() method tells the browser that you wish to perform an animation
这意味着它会告诉浏览器您希望预制需要重新绘制的动画。但为此,我们需要 运行 一些代码。
and requests that the browser call a specified function to update an animation before the next repaint.
这意味着在动画需要的下一次重绘之前,调用我的回调函数。
requestAnimationFrame
不是绘制事件回调,而是一种在浏览器现在必须执行的下一次绘制之前触发回调的方法。
以下 blog post 确实帮助我理解了整个事情的运作方式。
来自:
从本质上讲,整个事件循环功能可以用这段代码来说明:
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()
}
}
然后这样描绘: