如果我们在 requestAnimationFrame 中操作 DOM 会发生什么?

What happens if we manipulate DOM in requestAnimationFrame?

我的理解是,只要有一些 DOM 操作,例如插入 DOM 元素,就会触发重排,并且很可能随后会重绘。如果我错了,请纠正我。引用 MDN Web Docs,

The window.requestAnimationFrame() method tells the browser that you wish to perform an animation and requests that the browser calls a specified function to update an animation before the next repaint

在浏览器即将重绘之前调用 requestAnimationFrame (a.k.a.aAF) 回调。那么这是否意味着如果我们以某种方式设法在这个 rAF 中进行 DOM 操作(编辑:并且还在最后排队另一个 rAF)每次都会触发回流并因此重新绘制,我们将陷入无限循环实际上没有在屏幕上渲染任何东西。

或者一旦浏览器决定重绘,它会坚持下去并在下一次重绘中应用 RAF 回调中发生的任何更新?

whenever there is some DOM manipulation like inserting a DOM element would trigger a reflow and most likely followed by a repaint

绘画动作是异步发生的,所以“触发”应该这样理解。首先,您的 JavaScript 代码将在实际发生之前完成。

if we somehow manage to do a DOM manipulation inside this rAF (edit: and also queue another rAF at the end) which triggers a reflow everytime and thus a repaint, we would be stuck in an infinite loop without actually rendering anything on the screen.

重绘需求累积,未同步完成。首先,您的代码必须完成,直到调用堆栈为空。所以这里没有无限循环。

Or is it the case that once the browser had decided to do a repaint, it will stick with it and apply any updates that happened in the RAF callback in the next repaint?

是的。当调用 RAF 回调时,该代码获得最后一次更新 DOM 的机会,这可能会进一步积累绘画需求。如果在该回调中您还在 RAF 上注册了另一个回调,它不会在那个时候执行,但稍后:在 next 时间浏览器将准备其重绘任务 - 所以不是当前的。

简化示例

假设您有以下代码:

requestAnimationFrame(update);

myElement.style.backgroundColor = "silver"; // This queues a need for repaint

function update() {
    // This queues a need for repaint
    myElement.style.width = Math.floor(Math.random() * 100) + "px";
    requestAnimationFrame(update);
}

执行时,我们得到以下序列:

  1. update 注册为回调
  2. 背景变化安排了重新粉刷的需要
  3. 调用栈变空
  4. 浏览器开始重绘作业,但考虑到已注册回调。因此它会删除此注册(因为它应该只 运行 一次)并在执行任何其他操作之前执行 update
  5. 宽度变化安排了重新粉刷的需要。更改列表现在包括背景更改和此宽度更改以及已计算的任何级联效果。 (如何表示取决于浏览器)
  6. update 函数再次注册为回调。
  7. 浏览器现在检查作为重绘作业的一部分需要执行的操作,并执行所有需要的操作以可视化背景和宽度更改的效果。
  8. 油漆工作结束。剩下的就是注册的 update 回调。
  9. 当浏览器执行下一个绘制周期时,我们从第 4 步重新开始,但现在不再有排队的背景更改。其余的将是相同的过程。