requestAnimationFrame的执行时机与浏览器渲染流程密切相关。它在事件循环的“渲染”阶段被调用,通常在浏览器准备重绘页面之前触发,确保动画在每一帧开始时高效执行,从而实现流畅的动画效果。详细解释:事件循环中的“渲染”阶段在JavaScript的事件循环中,requestAnimationFrame(简称RAF)会在“渲染”阶段被调用。这个阶段是浏览器处理页面重绘和重排的时机。RAF
时间:2025-08-02 08:20:28 379浏览 收藏
有志者,事竟成!如果你在学习文章,那么本文《requestAnimationFrame 在事件循环的“渲染”阶段执行。它会在浏览器即将重绘页面之前调用,通常在每个动画帧开始时触发,确保动画流畅。》,就很适合你!文章讲解的知识点主要包括,若是你对本文感兴趣,或者是想搞懂其中某个知识点,就请你继续往下看吧~
requestAnimationFrame(rAF)不是宏任务或微任务,而是插在浏览器渲染前执行,顺序为:1. 执行宏任务;2. 清空微任务;3. 执行rAF回调;4. 浏览器渲染,它与屏幕刷新率同步,避免卡顿和撕裂;相比setTimeout,rAF更高效因它不盲目计时、后台可降频省资源、批量处理视觉更新;回调内应做样式/Canvas/WebGL更新并注意轻量计算、状态管理、避免频繁DOM变动及及时取消动画;跨浏览器行为一致核心在于自适应设备刷新率、后台标签页自动降频、执行时机微差可忽略,整体提供可靠高性能动画机制。
requestAnimationFrame
(rAF) 并不像我们平时理解的那些宏任务(比如 setTimeout
)或者微任务(如 Promise
回调)一样,在事件循环的某个固定队列里排队。它更像是一个特殊的“插队者”,或者说,它有自己独特的执行时机:它通常在浏览器的渲染阶段之前执行,具体来说,是在所有宏任务和微任务都处理完毕、浏览器准备绘制新一帧画面之前。它的核心机制是与浏览器的刷新率同步,确保动画的流畅性。

requestAnimationFrame
的执行机制,说白了,就是它会告诉浏览器:“嘿,我这里有个动画更新,你下次重绘屏幕的时候,能不能顺便把我这个函数也跑一下?”浏览器收到这个请求后,并不会立即执行,而是把它安排在下一个渲染周期里。
我们知道,浏览器的事件循环大致是这样的:它会不断地从任务队列(宏任务,比如用户交互、网络请求回调、setTimeout
)中取出任务执行,每执行完一个宏任务,就会清空微任务队列(比如 Promise.then()
、MutationObserver
)。当这些任务都处理得差不多了,浏览器就会进入渲染阶段,计算布局、绘制像素,然后把这些像素显示到屏幕上。

而 requestAnimationFrame
的回调,就恰好插在这个渲染阶段之前。这意味着,当你用 rAF 来做动画时,你的所有 DOM 操作和样式更新都会在浏览器开始计算布局和绘制之前完成。这样一来,浏览器就能一次性地处理所有的视觉变化,避免了多次重绘和回流(layout thrashing),大大提升了动画的流畅度和性能。我个人觉得,这有点像一个高效的团队合作:不是每个人都单独行动,而是等到大家都准备好了,一起把事情办完,效率自然就高了。
为什么 requestAnimationFrame
比 setTimeout
更适合做动画?
在我看来,这是个老生常谈但又至关重要的问题。简单来说,requestAnimationFrame
在动画方面简直是 setTimeout
的“降维打击”。

首先,最核心的区别在于同步性。requestAnimationFrame
是与浏览器的刷新率同步的。这意味着,如果你的显示器是 60Hz,rAF 就会以大约每秒 60 次的频率执行回调,确保你的动画帧率与屏幕刷新率一致,这样就避免了画面撕裂(tearing)和卡顿(jank)。而 setTimeout
只是一个定时器,它只关心你设定的时间到了没有,完全不关心浏览器现在忙不忙,或者屏幕刷新的节奏是怎样的。你设个 setTimeout(..., 16)
毫秒(理论上的 60fps),但如果浏览器此时正在忙着处理其他任务,或者渲染管线还没准备好,你的动画帧可能就错过了最佳的绘制时机,导致画面不连贯。
其次,是性能和资源消耗。浏览器对 requestAnimationFrame
有着天然的优化。比如,当你的网页标签页被最小化或者切换到后台时,浏览器会暂停或大幅度降低 rAF 的执行频率,从而节省 CPU 和电池资源。这对于移动设备尤其重要。而 setTimeout
不管三七二十一,只要时间到了,它就尝试执行,即使你的动画根本没人看,它也在后台默默消耗着资源。这在我看来,是两种完全不同的“职业道德”:一个懂得适时休息,一个则有点“死板”。
再者,requestAnimationFrame
能够有效地批处理视觉更新。由于它在渲染前统一执行,你可以把一帧内所有需要进行的 DOM 操作或 Canvas 绘制都放在 rAF 的回调里。浏览器会一次性地计算布局和绘制,而不是像 setTimeout
那样,可能会在短时间内触发多次不必要的重绘和回流。这种批处理能力对于复杂的动画和高性能应用来说,是不可或缺的。
requestAnimationFrame
的回调函数内部可以做什么?有哪些注意事项?
在 requestAnimationFrame
的回调函数内部,我们最主要、也最推荐做的事情,就是进行与视觉更新相关的操作。这包括但不限于:
- DOM 元素的样式和位置修改:比如移动一个元素、改变它的大小、透明度等。这是最常见的动画场景。
- Canvas 绘图:如果你在使用
canvas
标签进行自定义绘图,rAF 是更新画布内容的最佳时机。 - WebGL 渲染:对于 3D 图形,rAF 也是驱动每一帧渲染的核心。
- 读取布局信息:在修改 DOM 之前,你可能需要读取一些元素的尺寸或位置(比如
getBoundingClientRect()
)。由于 rAF 在渲染前执行,此时读取到的布局信息是当前帧最新的,避免了“强制同步布局”的问题。
不过,也有一些重要的注意事项,我常常提醒自己和团队成员:
- 避免长时间的同步计算:rAF 的回调函数应该尽可能地轻量和快速。任何耗时的同步计算都会阻塞主线程,导致动画卡顿。如果确实有复杂的计算需求,比如物理模拟、大量数据处理,我通常会考虑将其放到 Web Workers 中执行,然后将计算结果传递回主线程,在 rAF 中应用这些结果。
- 注意状态管理:动画往往涉及状态的变化。确保你的动画逻辑能够正确处理连续的 rAF 调用,避免竞态条件或不一致的视觉表现。例如,如果你正在基于一个变量
x
的值来移动元素,确保x
在每次 rAF 调用中都得到正确的更新。有时,我会引入一个“动画状态机”的概念,让状态更新和视觉更新分离。 - 不要在 rAF 内部频繁地创建和销毁大量 DOM 元素:虽然 rAF 优化了渲染,但频繁的 DOM 结构变动本身就比较耗性能。如果可以,尽量通过修改现有元素的样式或属性来达到动画效果。
- 确保动画的结束条件:动画通常有开始和结束。记得在动画完成后调用
cancelAnimationFrame()
来停止不必要的 rAF 回调,避免资源浪费。这就像你跑完步需要停下来一样,不能一直跑下去。
requestAnimationFrame
在不同浏览器或设备上的行为一致吗?
从核心机制上讲,requestAnimationFrame
在主流浏览器(Chrome, Firefox, Safari, Edge)和不同设备(桌面、移动)上的行为是基本一致的。它的设计初衷就是为了提供一个跨浏览器、与渲染周期同步的动画 API。
然而,在实际使用中,仍然存在一些细微的差异或者说,它会根据运行环境的特性而表现出一些“弹性”:
- 帧率的自适应性:rAF 会自动适应设备的刷新率。这意味着在 60Hz 的显示器上,它可能每秒执行 60 次;但在 120Hz 的高刷新率显示器上,它就能达到每秒 120 次。这对于开发者来说是件好事,你不需要手动去适配各种刷新率,rAF 会帮你搞定。但反过来,如果设备性能不佳,或者浏览器负载过高,它也可能无法达到理想的帧率,这是硬件和系统层面的限制,而非 rAF 本身的问题。
- 后台标签页的处理:大多数现代浏览器都会在标签页切换到后台或最小化时,自动暂停或大幅度降低
requestAnimationFrame
的执行频率。这是为了节省资源和电池。这种行为虽然一致,但如果你有需要在后台持续运行的逻辑(比如游戏中的物理模拟),就不能完全依赖 rAF,可能需要结合 Web Workers 或其他定时器来处理。 - 精确的执行时机:虽然 rAF 都是在渲染前执行,但不同浏览器引擎在渲染管线中的具体实现细节可能略有不同。这可能导致在毫秒级别上,rAF 回调的执行与实际绘制之间的间隔会有微小差异。不过,对于绝大多数动画场景来说,这种差异是完全可以忽略不计的,不会影响视觉流畅性。
- DevTools 的支持:现代浏览器的开发者工具通常都提供了强大的性能分析功能,可以清晰地看到
requestAnimationFrame
的调用栈、执行时间以及它如何影响渲染帧率。这对于调试和优化动画性能非常有帮助。我经常用 Chrome 的 Performance 面板来观察 rAF 的表现,看看有没有什么意外的性能瓶颈。
总的来说,requestAnimationFrame
提供了一个非常可靠且高效的动画框架,它在不同环境下的“一致性”更多体现在其核心理念和优化策略上,而非僵硬的、不考虑实际情况的固定行为。它懂得“随机应变”,这才是它真正强大的地方。
以上就是《requestAnimationFrame的执行时机与浏览器渲染流程密切相关。它在事件循环的“渲染”阶段被调用,通常在浏览器准备重绘页面之前触发,确保动画在每一帧开始时高效执行,从而实现流畅的动画效果。详细解释:事件循环中的“渲染”阶段在JavaScript的事件循环中,requestAnimationFrame(简称RAF)会在“渲染”阶段被调用。这个阶段是浏览器处理页面重绘和重排的时机。RAF会在这个阶段之前被调度,确保在下一次重绘前执行回调函数。与浏览器刷新率同步浏览器通常以60Hz的频率进行刷新(即每秒60帧),因此RAF会尽量在每个刷新周期内调用一次回调函数。这样可以保证动画与屏幕刷新同步,避免出现卡顿或撕裂现象。自动优化性能如果页面处于非激活状态(如标签页被隐藏),浏览器会暂停RAF回调的执行,从而节省资源。这种机制有助于提升性能和用户体验。适用于动画和视觉更新RAF最常用于动画、游戏、图表更新等需要频繁修改DOM或Canvas的场景。相比setTimeout或setInterval,RAF更加高效且与浏览器渲染流程保持一致。示例代码:》的详细内容,更多关于的资料请关注golang学习网公众号!
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
268 收藏
-
497 收藏
-
149 收藏
-
347 收藏
-
413 收藏
-
177 收藏
-
325 收藏
-
200 收藏
-
207 收藏
-
277 收藏
-
103 收藏
-
135 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 511次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 498次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习