【问题标题】:What happens if we manipulate DOM in requestAnimationFrame?如果我们在 requestAnimationFrame 中操作 DOM 会发生什么?
【发布时间】:2021-12-31 06:37:07
【问题描述】:

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

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

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

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

【问题讨论】:

  • 如果您在 rAF 回调中进行 DOM 操作,将会有操作 → 重绘 → 操作 → 重绘 → 等等。JSConf talk by Jake Archibald 很好地解释了这一点。
  • 哦,是的,我的错。在操作结束时,我正在排队另一个 rAF。在问题中错过了它。让我添加它。

标签: javascript dom repaint requestanimationframe reflow


【解决方案1】:

只要有一些 DOM 操作,比如插入 DOM 元素,就会触发重排,并且很可能会触发重绘

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

如果我们以某种方式设法在这个 rAF 中进行 DOM 操作(编辑:并在最后排队另一个 rAF),每次都会触发回流并因此重新绘制,我们将陷入无限循环而实际上没有渲染任何东西屏幕。

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

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

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

简化示例

假设你有这个代码:

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 步重新开始,但现在不再有排队的背景更改。其余的将是相同的过程。

【讨论】:

  • “4. 浏览器开始其布局/重绘工作”,这是一个相当令人困惑的表述,我认为说“浏览器开始更新渲染”会更容易混淆。布局和重绘是分开的,你可以很好地从用户空间代码中同步地强制布局,你不能强制重绘,这永远是渲染步骤的最后一步。另外,我觉得通过从一开始就提醒raf(()=>raf(fn2)) 将安排fn2下一个 帧触发,我觉得第一点的答案会简单得多。否则这个答案是正确的。
  • @Kaiido,感谢您的评论。 “你可以很好地从用户空间代码中强制同步布局”:你的意思是用户可感知的布局变化吗?你能给出一个代码示例吗?
  • 无论如何,我删除了对布局的引用。
  • gist.github.com/paulirish/5d52fb081b3570c81e3a 这是触发布局的列表,是的,它是“用户可感知的”:stackoverflow.com/questions/55134528/…
  • 好的,凯多!
猜你喜欢
  • 2016-09-22
  • 2012-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-26
  • 2016-07-30
  • 1970-01-01
  • 2021-11-22
相关资源
最近更新 更多