rAF比setTimeout/setInterval更优,因其由浏览器调度、自动对齐刷新率、后台自动降频;需用递归调用+cancelAnimationFrame控制启停,配合transform/opactity和避免强制同步布局以提升性能。

用 requestAnimationFrame(简称 rAF)替代 setTimeout 或 setInterval 做动画,是提升流畅度最直接有效的方式。它让浏览器决定何时执行动画帧,自动对齐屏幕刷新率(通常是 60Hz),避免丢帧、卡顿和过度绘制。
为什么 rAF 比定时器更合适?
rAF 不是“固定间隔执行”,而是由浏览器统一调度:当页面在前台、标签页可见、动画需要更新时才触发;如果页面被切换到后台、元素不可见或系统资源紧张,rAF 会自动暂停或降频。而 setTimeout(fn, 16) 只是“尽量每 16ms 调一次”,实际执行时间不可控,容易与屏幕刷新不同步,造成撕裂或掉帧。
基础用法:启动一个持续动画循环
核心是写一个“自我递归”的动画函数,每次执行完主动调用下一次:
function animate() {
// 1. 更新动画状态(比如位置、透明度)
element.style.transform = `translateX(${x}px)`;
// 2. 请求下一帧
requestAnimationFrame(animate);
}
// 启动
requestAnimationFrame(animate);
- 不要用
setInterval(animate, 16)—— 它无法感知渲染时机,也不受浏览器节流策略保护 - 首次调用也建议用
requestAnimationFrame启动,保持一致性 - 动画逻辑中应尽量只做必要计算和 DOM 写入,避免同步布局抖动(如读 offsetTop 后立刻改 style)
控制动画启停与状态管理
rAF 本身没有取消机制,需靠布尔标志 + 闭包或变量控制执行流:
let isRunning = true;
let animationId;
function animate() {
if (!isRunning) return;
// 更新逻辑...
element.style.left = `${x}px`;
animationId = requestAnimationFrame(animate);
}
// 开始
function start() {
isRunning = true;
animate();
}
// 暂停(不取消当前帧,但阻止后续帧)
function pause() {
isRunning = false;
}
// 彻底停止(可选:确保无残留调用)
function stop() {
isRunning = false;
cancelAnimationFrame(animationId);
}
-
cancelAnimationFrame(id)用于清理,尤其在组件卸载、路由跳转前必须调用,防止内存泄漏 - 避免在每次 rAF 回调里重复查询 DOM 元素(如
document.getElementById),应在外部缓存引用 - 如需基于时间差做匀速运动,可用 rAF 提供的时间戳参数:
requestAnimationFrame((timestamp) => {...}),其中timestamp是高精度毫秒数(DOMHighResTimeStamp)
配合 CSS 和硬件加速进一步提效
rAF 主要负责“驱动逻辑”,真正影响性能的是如何更新样式:
- 优先使用
transform和opacity动画属性——它们走合成层(compositor),不触发重排(reflow)和重绘(repaint) - 给动画元素加
will-change: transform(慎用,仅对长期动画元素)或transform: translateZ(0)强制启用 GPU 加速 - 避免在 rAF 中读取
offsetLeft、getBoundingClientRect()等会强制同步布局的属性;如需测量,集中放在一帧开始时批量读,修改留到帧末尾
不复杂但容易忽略:rAF 是浏览器优化动画的入口,不是银弹。它解决的是“何时执行”,而“执行什么”仍需你精简逻辑、合理更新 DOM、善用 CSS 合成能力。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











