requestanimationframe 替代 setinterval 实现动画,核心是与屏幕刷新率同步、自动暂停,避免掉帧卡顿;setinterval 固定间隔执行,不感知渲染状态,易导致跳帧或资源浪费。

用 requestAnimationFrame 替代 setInterval 做动画,核心是让浏览器按自身刷新节奏驱动帧绘制,避免掉帧、卡顿或过度渲染。它不是“更快”,而是“更准”——与屏幕刷新率(通常是 60Hz)同步,且在页面不可见时自动暂停。
为什么 setInterval 不适合动画
setInterval 按固定毫秒间隔执行,不关心浏览器是否完成上一帧、是否在后台、甚至是否被节流。例如设为 16ms(≈60fps),但若某次计算+渲染耗时 22ms,下一帧就会被挤压或跳过,造成卡顿;若页面切到后台,它仍可能运行,浪费资源。
requestAnimationFrame 的基本用法
它接收一个回调函数,在下一次重绘前调用,返回一个 ID(类似 setTimeout)。关键点:它不接受时间参数,只承诺“尽快在下一帧执行”。需手动控制逻辑节奏:
- 用
performance.now()记录起始时间,每次回调中计算已过去的时间差(delta) - 根据 delta 更新动画状态(如位置、透明度),而不是依赖“第几帧”
- 在回调末尾再次调用
requestAnimationFrame形成循环
一个平滑位移动画示例
假设让一个 div 在 2 秒内从 left=0px 移动到 left=400px:
const el = document.getElementById('box');
const startTime = performance.now();
const duration = 2000; // 2秒
const from = 0;
const to = 400;
<p>function animate(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1); // 防止超限
const value = from + (to - from) * progress;</p><p>el.style.left = value + 'px';</p><p>if (progress </p><h3>进阶要点:暂停、取消与性能优化</h3><p><strong>暂停/恢复</strong>:保存上一次调用的时间戳和当前状态,暂停时停止递归调用;恢复时用新 <code>requestAnimationFrame</code> 启动,并更新起始时间参考。<br><strong>取消动画</strong>:不需要像 <code>clearInterval</code> 那样管理 ID;直接不再调用 <code>requestAnimationFrame</code> 即可,浏览器会自动停止该链路。<br><strong>避免布局抖动</strong>:不要在动画帧中反复读取 <code>offsetLeft</code> 等触发回流的属性;把读取操作移到帧外,或用 <code>getComputedStyle</code> 批量缓存。<br><strong>节流复杂计算</strong>:如果每帧要处理大量数据,可结合 <code>setTimeout</code> 或 <code>queueMicrotask</code> 把非渲染任务延后,确保主线程及时交还给渲染。</p>











