返回
创建于
状态
公开
当主线程(Main Thread)被阻塞时(例如执行了一个死循环或耗时极长的同步任务),JS 的 requestAnimationFrame (rAFA) 必定会停止执行,而 CSS Keyframes 动画是否执行则取决于它作用的属性。
这是由浏览器渲染架构决定的,我们可以从以下两个维度来拆解:
1. JS 的 requestAnimationFrame
结论:会被完全阻塞。
- 原理:
requestAnimationFrame的回调函数是运行在主线程上的。 - 渲染流水线:在浏览器的事件循环(Event Loop)中,rAFA 的触发时机是在“样式计算”和“布局”之前。
- 结果:既然主线程正忙于处理那个阻塞任务,它就没有余力去切换到 Event Loop 的渲染阶段。因此,rAFA 的回调会被推迟,直到阻塞任务完成,这通常表现为动画卡死或直接跳帧。
2. CSS Keyframes 动画
结论:部分执行(取决于属性是否能被“合成器”处理)。
现代浏览器(Chrome/Edge/Safari)拥有合成器线程(Compositor Thread),它独立于主线程运行。
A. 会被阻塞的情况
如果你的 CSS 动画涉及需要主线程参与的属性(即触发了 Layout 或 Paint 的属性):
- 例子:
width,height,margin,top/left(非定位),color。 - 原因:这些属性的改变需要重新计算布局(Reflow)或重新绘制(Repaint),这些工作必须在主线程完成。如果主线程阻塞,动画就会停滞。
B. 不会被阻塞的情况(硬件加速)
如果你使用了合成器属性:
- 例子:
transform(scale, rotate, translate) 和opacity。 - 原因:浏览器会将这些元素提升到独立的合成层(Compositor Layer)。
- 结果:一旦动画开始,主线程会把动画的路径、时间曲线等信息交给合成器线程和 GPU。即使随后主线程死锁,合成器线程依然可以独立地在每一帧计算位移并提交给 GPU 渲染。
总结对比
| 特性 | 是否受主线程阻塞影响 | 运行位置 | 场景建议 |
|---|---|---|---|
| requestAnimationFrame | 受影响(停止) | 主线程 | 需要复杂逻辑计算、与 JS 状态强绑定的动画 |
| CSS 动画 (Layout 属性) | 受影响(停止) | 主线程 | 简单的 UI 状态切换(如宽高改变) |
| CSS 动画 (Transform/Opacity) | 不受影响(流畅) | 合成器线程/GPU | 性能优先的平滑位移、旋转、缩放动画 |
验证方法
你可以做一个简单的实验:
- 写一个
infinite rotate的 CSS 动画(使用transform)。 - 写一个
requestAnimationFrame改变数值的脚本。 - 在控制台运行
while(true){}。 你会发现,JS 计数器停了,但页面上的转圈动画可能依然在丝滑旋转。