requestAnimationFrame 是浏览器原生API,用于在下一次重绘前执行动画回调,与屏幕刷新率同步、省电流畅;而 setTimeout 是定时器,接受毫秒参数,易掉帧、堆叠回调且后台不暂停。

requestAnimationFrame 是什么,和 setTimeout 有什么区别?
requestAnimationFrame 是浏览器提供的原生 API,用于在下一次重绘前执行动画回调。它不是定时器,不接受毫秒参数,也不保证固定间隔——而是由浏览器决定何时调用,通常与屏幕刷新率(如 60Hz)同步,因此更省电、更流畅。
相比 setTimeout 或 setInterval,它不会在页面后台或 tab 非激活时持续运行,避免资源浪费;也不会因 JS 主线程阻塞而“堆叠”回调(setTimeout 在卡顿时可能集中触发多次)。
常见错误现象:
– 用 setTimeout(..., 16) 模拟 60fps,但实际帧率不稳定,尤其在低端设备或高负载时掉帧严重;
– 动画卡顿、跳帧,或在滚动/交互中明显滞后。
基础用法:如何写一个最简动画循环?
核心是「递归调用」+「时间戳控制」。浏览器传入的 timestamp(DOMHighResTimeStamp)是自页面加载以来的毫秒数,精度达微秒级,比 Date.now() 更可靠。
- 必须手动调用
requestAnimationFrame再次注册下一次帧,否则只执行一次 - 不要在回调里直接修改 DOM 属性再立刻读取(如
el.style.left = '100px'; console.log(el.offsetLeft)),会强制同步布局,拖慢帧率 - 若需控制动画进度,用时间差(
timestamp - startTime)计算,而非帧数计数
let startTime = 0;
function animate(timestamp) {
if (!startTime) startTime = timestamp;
const elapsed = timestamp - startTime;
const progress = Math.min(elapsed / 2000, 1); // 2s 动画
el.style.transform = `translateX(${progress * 200}px)`;
if (progress < 1) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
cancelAnimationFrame 怎么配合使用?
动画中途停止、组件卸载、响应用户中断(如鼠标移出)时,必须调用 cancelAnimationFrame 清除待执行帧,否则回调仍会继续排队,造成内存泄漏或意外更新。
云模块_YunMOK网站管理系统采用PHP+MYSQL为编程语言,搭载自主研发的模块化引擎驱动技术,实现可视化拖拽无技术创建并管理网站!如你所想,无限可能,支持创建任何网站:企业、商城、O2O、门户、论坛、人才等一块儿搞定!永久免费授权,包括商业用途; 默认内置三套免费模板。PC网站+手机网站+适配微信+文章管理+产品管理+SEO优化+组件扩展+NEW Login界面.....目测已经遥遥领先..
立即学习“Java免费学习笔记(深入)”;
关键点:
– requestAnimationFrame 返回一个整数 ID,必须保存下来供后续取消
– 多次调用 requestAnimationFrame 会产生多个独立 ID,每个都要单独取消(不能靠「最后一次 ID 覆盖」来管理)
– React/Vue 等框架中,在 useEffect 的 cleanup 或 beforeUnmount 钩子中取消
let animationId = 0;
function start() {
function step() {
// ...动画逻辑
animationId = requestAnimationFrame(step);
}
animationId = requestAnimationFrame(step);
}
function stop() {
if (animationId) {
cancelAnimationFrame(animationId);
animationId = 0;
}
}
为什么动画卡顿?几个真实踩坑点
即使用了 requestAnimationFrame,动画仍可能卡顿。根本原因常不在 API 本身,而在操作方式:
- 频繁读写同一元素的几何属性(如
offsetTop+style.left),触发 Layout Thrashing - 在帧回调中执行大量计算、DOM 查询或未优化的 canvas 绘图
- 用
style.left/top而非transform或opacity,导致每次重排(reflow) - 监听了
scroll或resize并直接在里面调用requestAnimationFrame,但没做节流,导致 ID 泛滥
性能敏感场景建议:只对 transform 和 opacity 做动画;用 getBoundingClientRect() 批量读取;复杂逻辑移到 Web Worker;滚动动画优先用 CSS scroll-behavior 或 IntersectionObserver 替代手动监听。










