raf优化动画的关键是嵌入浏览器渲染节奏:对齐重绘时机、避开主线程阻塞、减少无效计算;它不加速代码,但让视觉更新更省、更稳、更准。

用 requestAnimationFrame(rAF)优化页面动画和滚动效果,关键不是“调用了它”,而是让它真正嵌入浏览器的渲染节奏——对齐重绘时机、避开主线程阻塞、减少无效计算。它不加速代码本身,但能让视觉更新更省、更稳、更准。
让动画帧率稳定在 60fps 附近
rAF 回调总在浏览器下一次重绘前执行,天然对齐屏幕刷新周期(如 60Hz ≈ 16.7ms/帧)。而 setTimeout(16) 只是“尽量每 16ms 调一次”,实际可能被 JS 执行阻塞、任务队列延迟拖慢,导致掉帧或卡顿。
- 每次回调开头就调用 requestAnimationFrame(animate),确保下一帧及时注册,避免因当前帧耗时长而断连
- 用传入的时间戳(currentTime)计算真实经过时间(deltaTime),实现基于时间的匀速动画,不受帧率波动影响
- 避免在回调中同步读取布局属性(如 offsetTop、clientWidth)后再修改样式,否则触发强制同步布局(layout thrashing)
滚动监听必须节流到 rAF 周期
scroll 事件高频触发(每秒可达数十次),直接在里面做 DOM 更新极易卡顿。用 rAF 节流,把多次滚动变化合并到单帧内处理,既响应及时又不压垮主线程。
- 用布尔标记(如 ticking = false)控制是否已请求 rAF,防止重复注册
- 在 rAF 回调中统一读取 window.pageYOffset 和元素位置,再批量更新样式(如透明度、位移)
- 典型场景:回到顶部按钮渐显、视差滚动、滚动进度条、元素进入视口动画
严格管理动画生命周期,避免内存泄漏
rAF 不会自动停止,一旦启动就必须手动取消;否则即使元素已移除、页面已跳转,回调仍持续执行,造成资源浪费和潜在错误。
- 用变量保存当前帧 ID(如 let animationId = null),每次 requestAnimationFrame 返回值赋给它
- 停止时调用 cancelAnimationFrame(animationId),并立即将变量置为 null
- 每次回调开头检查元素是否存在且仍在文档中(if (!element || !element.isConnected) return)
- 监听 visibilitychange 事件,在页面隐藏时主动 cancel,恢复时重新启动(增强节能)
配合 GPU 加速与批量更新进一步提效
rAF 解决了“何时执行”,但执行内容是否高效,决定最终体验。需搭配渲染层优化策略:
- 对动画元素启用合成层:添加 transform: translateZ(0) 或 will-change: transform
- 避免频繁操作 layout 触发属性(如 top/left/width/height),优先使用 transform 和 opacity
- 复杂计算逻辑(如粒子位置、路径插值)可抽离到 Web Worker,主线程只负责渲染
- 多个动画共用一个 rAF 回调,统一调度更新,而非每个动画单独 requestAnimationFrame
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











