requestAnimationFrame 实现转场动画的核心是将动画逻辑交由浏览器刷新节奏调度,配合 CSS transform/opacity 等 GPU 加速属性避免重排,通过进度计算动态更新样式,并封装控制器管理生命周期,必要时用 CSS transition 降级。

网页端用 requestAnimationFrame 做转场动画,核心是**把动画逻辑交给浏览器的刷新节奏来调度**,避免卡顿、掉帧。它本身不直接做动画,而是提供一个“在下一帧绘制前执行”的时机,配合 CSS 变换(transform/opacity)或 Canvas 绘制,才能实现真正丝滑的转场。
用 requestAnimationFrame 驱动 CSS 转场
这是最常用、性能最好的方式:用 JS 控制动画进度,但把视觉变化交给 GPU 加速的 CSS 属性(如 transform 和 opacity),避免触发重排(reflow)。
- 给要转场的元素设置
will-change: transform, opacity(可选,对复杂场景有帮助) - 用
requestAnimationFrame循环更新元素的style.transform和style.opacity - 动画时间用起始时间 + 持续时间 + 当前帧时间计算进度(0 → 1),再映射到变换值
- 示例:从右向左滑入新页面
left/top 或 width/height 动画,它们会频繁触发重排)封装一个可复用的转场控制器
手动写 rAF 循环容易出错(比如忘记 cancel、没处理暂停/恢复)。建议封装成类或函数,管理状态和生命周期:
- 构造时传入两个 DOM 元素(旧页、新页)、持续时间(如 400ms)和缓动函数(如
t => t * t) - 启动后记录
startTime = performance.now(),在 rAF 回调中计算已过时间占比 - 根据进度分别设置旧页透明度递减 + 向左平移,新页透明度递增 + 从右平移入
- 动画结束时调用
cancelAnimationFrame,并触发完成回调(如清理 class、释放资源)
配合 CSS 过渡做降级与简化
不是所有转场都需要 JS 驱动。简单场景下,可以只用 CSS transition + class 切换,更轻量且自动适配系统性能(如 macOS 的流畅滚动、iOS 的动态刷新率):
- 给容器设
transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94), opacity 0.4s - 切换 class 触发
transform: translateX(-100%)(旧页)和transform: translateX(0)(新页) - JS 只负责 class 切换和监听
transitionend,rAF 仅用于需要精确控制帧或做复杂插值时(如视差、路径动画)
避免常见陷阱
rAF 看似简单,但几个细节不注意就会导致“看起来不丝滑”:
- 别在 rAF 回调里做耗时操作:如 DOM 查询、大量计算、console.log —— 会挤占渲染时间,直接掉帧
-
确保元素已挂载且可见:动画开始前检查
offsetParent !== null,否则 transform 可能无效 -
注意设备像素比和缩放:高 DPR 设备下,小数位 transform 更平滑;可用
Math.round(x * devicePixelRatio) / devicePixelRatio微调 -
移动端慎用 scroll 绑定 rAF:滚动中高频触发需节流,或改用
getScrollInfo+ passive event listener 配合 rAF


















