requestAnimationFrame 是浏览器提供的让动画更顺滑且节能的调度机制,它对齐屏幕刷新节奏、页面不可见时自动暂停,并需递归调用和 cancelAnimationFrame 正确终止。

requestAnimationFrame 不是“用来做动画的函数”,而是浏览器提供的、**让动画更顺滑且节能的调度机制**。直接用 setTimeout 或 setInterval 做动画,容易掉帧、卡顿、耗电,而 requestAnimationFrame 会把回调对齐到屏幕刷新节奏(通常是 60fps),且在页面不可见时自动暂停。
为什么动画要用 requestAnimationFrame 而不是 setTimeout
核心区别在于调度时机和生命周期管理:
-
setTimeout(fn, 16)是“尽力而为”:系统忙时延迟可能远超 16ms,导致跳帧;空闲时又可能触发多余调用 -
requestAnimationFrame(fn)是“听屏幕指挥”:只在下一帧绘制前执行,且标签页切走后自动暂停,不浪费 CPU 和电量 - 动画逻辑中若涉及 DOM 读写(比如先
getBoundingClientRect()再style.transform),requestAnimationFrame还能避免强制同步布局(forced reflow)
requestAnimationFrame 的基本用法和终止方式
它不是循环 API,每次只调用一次回调,要实现持续动画,必须在回调内部再次调用自己 —— 这是关键习惯。
function animate() {
// 更新元素位置/样式
element.style.transform = `translateX(${x}px)`;
// 关键:递归请求下一帧
if (isAnimating) {
requestAnimationFrame(animate);
}
}
// 启动
requestAnimationFrame(animate);
终止动画不能靠“清空定时器 ID”,因为 requestAnimationFrame 返回的是一个数字 ID,但取消它必须用 cancelAnimationFrame(id):
原生js实现新年倒计时喜庆背景带炫酷雪花飘落动画特效代码下载。基于原生JavaScript+CSS实现,不依靠任何第三方jQuery库,兼容手机移动端,新年倒计时自动获取,可循环使用,非常简单实用的一款新年倒计时js特效代码。
立即学习“Java免费学习笔记(深入)”;
- 保存上一次调用返回的 ID:
let rafId = requestAnimationFrame(animate); - 停止时调用:
cancelAnimationFrame(rafId); - 漏掉
cancelAnimationFrame可能导致内存泄漏或意外重绘(尤其组件卸载时)
实际动画中容易踩的坑
写出来能跑 ≠ 写得对。常见问题集中在时间控制、状态管理和精度上:
- 用
performance.now()计算真实经过时间,别依赖“每帧 16ms”——设备可能降频、高刷屏是 120fps、后台标签页帧率极低 - 不要在
requestAnimationFrame回调里做大量计算或 DOM 查询,否则挤占绘制时间,反而卡顿 - 多个动画共存时,别每个都开独立
requestAnimationFrame循环;应合并到同一个主动画帧中统一更新 - CSS 动画(
transition/@keyframes)比 JS 动画更高效,优先用 CSS;JS 仅用于需要运行时逻辑(如跟随鼠标、物理模拟)的场景
真正难的不是调用 requestAnimationFrame,而是把动画逻辑拆解成“可预测、可中断、与帧率解耦”的小步更新 —— 尤其涉及 easing、暂停/恢复、时间缩放时,很容易陷入手动维护时间戳和状态的泥潭。










