requestAnimationFrame 比 setTimeout 更适合动画,因其对齐屏幕刷新、后台暂停、自动降帧且协同渲染;正确使用需用变量保存帧ID、循环调用自身、停止时调用 cancelAnimationFrame 并检查元素存在性。

requestAnimationFrame 是浏览器原生支持的动画驱动机制,比 setTimeout 或 setInterval 更精准、更省电、更贴合屏幕刷新节奏。直接用它写动画,不加节流或状态管理,反而容易出错——比如重复调用、帧率失控、动画未取消导致内存泄漏。
为什么 requestAnimationFrame 比 setTimeout 适合动画
浏览器知道你正在做视觉更新,会自动把回调对齐到下一次重绘(通常 60fps),并能在页面不可见时暂停执行;而 setTimeout 只是“尽力而为”,实际帧率可能飘忽不定,还可能在后台持续消耗 CPU。
- 页面切到后台时,
requestAnimationFrame回调默认被挂起(Chrome/Firefox/Edge 均支持) - 系统高负载时,它会自动降帧(比如从 60fps 降到 30fps),而非丢帧+卡顿
- 与 CSS 动画、WebGL 渲染天然协同,避免强制同步布局(layout thrashing)
如何正确启动和停止一个 requestAnimationFrame 动画循环
关键不是“怎么调用 requestAnimationFrame”,而是“怎么控制它的生命周期”。漏掉取消会导致动画持续运行,哪怕 DOM 元素已移除。
- 用一个变量保存当前帧 ID:
let animationId = null - 在动画函数末尾主动调用
requestAnimationFrame(animate)实现循环 - 停止时必须调用
cancelAnimationFrame(animationId),且要确保只取消一次 - 如果动画依赖某个元素存在,应在每次回调开头检查
if (!element || !element.isConnected) { return; }
示例:
立即学习“Java免费学习笔记(深入)”;
云模块_YunMOK网站管理系统采用PHP+MYSQL为编程语言,搭载自主研发的模块化引擎驱动技术,实现可视化拖拽无技术创建并管理网站!如你所想,无限可能,支持创建任何网站:企业、商城、O2O、门户、论坛、人才等一块儿搞定!永久免费授权,包括商业用途; 默认内置三套免费模板。PC网站+手机网站+适配微信+文章管理+产品管理+SEO优化+组件扩展+NEW Login界面.....目测已经遥遥领先..
let animationId = null;
function animate() {
// 更新逻辑:比如 element.style.transform = `translateX(${x}px)`
if (x < 100) {
x++;
animationId = requestAnimationFrame(animate);
}
}
// 启动
animationId = requestAnimationFrame(animate);
// 停止(比如组件卸载时)
if (animationId) {
cancelAnimationFrame(animationId);
animationId = null;
}requestAnimationFrame 本身不处理 easing 和 timing,得自己补
requestAnimationFrame 只负责调度,不提供缓动函数、持续时间、延迟等能力——它连“过了多少毫秒”都不告诉你。你需要手动记录上一帧时间戳,算出真实经过时间(delta),再喂给 easing 函数。
- 传给回调的参数是系统时间戳(
DOMHighResTimeStamp),精度达微秒级 - 不要用
Date.now(),它受系统时钟调整影响,会导致 delta 跳变 - 简单线性动画可忽略 delta,但涉及物理模拟、变速过渡时,必须基于 delta 计算
示例(带时间差的缓动):
let lastTime = 0;
function animate(timestamp) {
if (!lastTime) lastTime = timestamp;
const delta = timestamp - lastTime;
lastTime = timestamp;
// 每秒移动 100px,则每帧位移 = 100 (delta / 1000)
x += 100 (delta / 1000);
element.style.transform = translateX(${x}px);
if (x < 100) {
animationId = requestAnimationFrame(animate);
}
}
和 CSS transition / @keyframes 冲突时怎么办
JavaScript 动画和 CSS 动画同时操作同一个属性(如 transform 或 opacity),会相互覆盖、抖动甚至失效。常见于“JS 控制开始,CSS 控制过程”的混合写法。
- 避免在 JS 动画进行中,用
element.style.transition = 'transform 0.3s'动态加 transition - 如果要用 CSS 动画收尾,建议 JS 动画结束后清空内联样式:
element.style.transform = '',再加 class 触发 CSS 动画 - 检测是否正在执行 CSS 动画:监听
transitionend或animationend,但注意事件可能被频繁触发或冒泡干扰
真正难的不是写一帧,而是让多套机制共存时不打架——尤其是当动画需要响应用户交互(拖拽中暂停、松手后惯性滑动)时,时间戳对齐、状态同步、取消时机,都得手工兜住。










