rAF比定时器更适合动画,因其与屏幕刷新同步、避免丢帧卡顿、页面不可见时自动暂停;需递归调用并用cancelAnimationFrame控制启停,结合时间戳实现设备无关的匀速动画。

用 requestAnimationFrame(简称 rAF)替代 setTimeout 或 setInterval 做动画,核心在于“跟屏幕刷新节奏同步”,而不是“固定时间间隔执行”。它让浏览器决定何时执行帧更新,从而避免丢帧、卡顿和过度渲染。
为什么 rAF 比定时器更适合动画
定时器(如 setInterval(animate, 16))只是粗略按毫秒触发,不感知屏幕刷新率(可能是 60Hz、120Hz,甚至动态变化),容易出现:帧未完成就触发下一帧、帧空跑、与屏幕垂直同步(VSync)错位。而 rAF 由浏览器调度,在下次重绘前调用回调,天然匹配刷新节奏,且在页面不可见时自动暂停,省电节能。
基础用法:替换 setInterval 实现平滑移动
把循环动画逻辑从“定时触发”改为“帧触发”,关键点是手动递归调用 rAF:
- 不要写成
setInterval(() => { rAF(draw) }, 16)—— 这反而失去意义 - 正确方式是在动画函数内部最后再调用一次
rAF(animate) - 用一个变量控制启停(比如
let animationId = null),调用cancelAnimationFrame(animationId)停止
示例:让一个 div 水平匀速移动
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
立即学习“Java免费学习笔记(深入)”;
let x = 0;
const box = document.getElementById('box');
let animationId = null;
<p>function animate() {
x += 2; // 每帧移动像素数(非时间驱动,需结合时间戳做匀速)
box.style.transform = <code>translateX(${x}px)</code>;
animationId = requestAnimationFrame(animate);
}</p><p>// 启动
animationId = requestAnimationFrame(animate);</p><p>// 停止(例如点击按钮时)
// cancelAnimationFrame(animationId);</p>进阶:用时间戳实现真正的时间驱动动画
上面例子中每帧移动固定像素,但不同设备帧率不同(60fps vs 120fps),会导致速度不一致。更合理的方式是记录上一帧时间,计算已过去毫秒数,再按目标速度换算位移:
- 在 rAF 回调中接收时间戳参数(DOMHighResTimeStamp),单位为毫秒,精度高于
Date.now() - 保存上一帧时间,用差值(delta)乘以速度,得到本次应移动距离
- 这样动画速度与设备帧率无关,只取决于真实流逝时间
let startTime = performance.now();
const duration = 3000; // 动画总时长 3s
let animationId = null;
<p>function animate(timestamp) {
const elapsed = timestamp - startTime;
const progress = Math.min(elapsed / duration, 1); // 归一化进度 [0, 1]</p><p>box.style.transform = <code>translateX(${progress * 400}px)</code>; // 移动 400px</p><p>if (progress < 1) {
animationId = requestAnimationFrame(animate);
}
}
animationId = requestAnimationFrame(animate);</p>实用技巧与注意事项
-
别在 rAF 里做重计算或 DOM 批量读写:先批量读取(如
getBoundingClientRect),再批量写入(如统一设style.transform),避免强制同步布局(layout thrashing) - 动画元素优先用 transform 和 opacity:这两者能走合成层(composite layer),不触发重排重绘,性能最优
- 需要精确控制节奏?配合 CSS 自定义属性 + transition:rAF 更新 CSS 变量,交由浏览器原生过渡处理,比 JS 插值更稳
-
调试时可用
performance.now()打点测帧率,或直接看浏览器 DevTools 的 Rendering 面板中的 FPS 指示器


















