js动画
requestAnimationFrame
requestAnimationFrame(简称 rAF)是浏览器专门为动画提供的 API,它告诉浏览器在下次重绘之前调用指定的回调函数。
为什么使用 rAF?
- 与显示器刷新率同步:浏览器会在每次重绘前调用回调,通常为 60Hz(约 16.6ms 一帧),因此动画更流畅,不会出现丢帧或卡顿。
- 节能省电:在后台标签页或隐藏的 iframe 中,浏览器会自动暂停 rAF 调用,避免无意义的 CPU/GPU 消耗。
- 更精确的时间戳:回调会收到一个
DOMHighResTimeStamp参数,表示触发的时间,便于计算帧间隔,保证动画速度与实际时间一致。
与 setTimeout / setInterval 的对比:
setTimeout/setInterval的回调 会在定时器到期时放入宏任务队列,实际执行时机受事件循环影响,无法保证对齐刷新帧,容易掉帧。setInterval还可能因为回调执行时间过长而出现"堆积",导致多次连续触发。- rAF 由浏览器统一调度,保证一帧只执行一次,更贴合渲染时机。
基本用法:
let start = null;
const box = document.getElementById("box");
function step(timestamp) {
if (!start) start = timestamp;
const progress = timestamp - start; // 已经过去的毫秒
box.style.transform = `translateX(${Math.min(progress / 5, 200)}px)`;
if (progress < 1000) {
requestAnimationFrame(step); // 继续下一帧
}
}
requestAnimationFrame(step);
取消动画:
const id = requestAnimationFrame(step);
cancelAnimationFrame(id);
注意点:
- 回调里再调用
requestAnimationFrame才能持续动画,单次调用只执行一帧。 - 避免在回调中做耗时同步操作,否则会拖慢整帧。
- 处理平滑帧率波动时,可以结合
performance.now()或回调时间戳计算 delta time。 - 如果动画与 DOM 重排无关(纯 CSS)则可以直接用 CSS 动画/transition,性能更好;涉及 JS 计算时再用 rAF。