requestAnimationFrame(rAF)是实现javaScript动画最推荐的方式,它由浏览器调度、与屏幕刷新率同步(通常60fps),比setTimeout/setInterval更高效流畅省电;其回调自动传入时间戳,需手动递归调用以实现循环动画,后台标签页会自动降频。

javascript 实现动画效果的核心是让元素的样式在短时间内连续、平滑地变化,而 requestAnimationFrame(简称 rAF)是现代 Web 中最推荐的方式——它由浏览器调度,与屏幕刷新率同步(通常 60fps),比 setTimeout 或 setInterval 更高效、更流畅、更省电。
requestAnimationFrame 的基本用法
rAF 接收一个回调函数作为参数,浏览器会在下一次重绘前执行该函数。它返回一个整数 ID,可用于取消动画(配合 cancelAnimationFrame)。
关键点:
- 回调函数会自动传入一个时间戳(DOMHighResTimeStamp),表示当前帧开始的时间(毫秒级,精确到微秒)
- 它不是自动循环的——想实现持续动画,需在回调内部再次调用
requestAnimationFrame - 页面处于后台标签页时,rAF 会自动降频或暂停,节省资源
写一个简单的位移动画
比如让一个
立即学习“Java免费学习笔记(深入)”;
const box = document.getElementById('box'); let startTime = null; const duration = 2000; // 动画总时长 2s const startX = 0; const endX = 400; function animate(currentTime) { if (!startTime) startTime = currentTime; const elapsed = currentTime - startTime; const progress = Math.min(elapsed / duration, 1); // 防止超限 const x = startX + (endX - startX) * progress;
box.style.transform = translateX(${x}px);
if (progress < 1) { requestAnimationFrame(animate); } }
requestAnimationFrame(animate);
封装可复用的动画函数
为避免每次手写时间计算,可以封装一个简易的 animate 工具函数:
支持:起始值、目标值、缓动函数、完成回调
function animate({ from, to, duration = 300, easing = t => t, onUpdate, onComplete }) { const start = performance.now(); function step(current) { const elapsed = current - start; const progress = Math.min(elapsed / duration, 1); const value = from + (to - from) * easing(progress); onUpdate(value); if (progress < 1) { requestAnimationFrame(step); } else if (onComplete) { onComplete(); }
} requestAnimationFrame(step); }
// 使用示例:给 opacity 做淡入 animate({ from: 0, to: 1, duration: 800, easing: t => t * t, // 缓入 onUpdate: val => box.style.opacity = val, onComplete: () => console.log('动画完成') });
和 css 动画/transition 的区别与选择
rAF 适合需要 JavaScript 动态控制的场景,例如:
- 跟随鼠标或滚动实时响应的动画(如视差、拖拽反馈)
- 复杂物理模拟(弹簧、碰撞、惯性滚动)
- 需要逐帧读取/修改多个元素状态的动画序列
而纯展示型、声明式的动画(如按钮悬停、页面切换),优先使用 CSS transition 或 @keyframes——它们由 GPU 加速,性能更好,代码更简洁,且天然支持硬件加速和系统偏好(如减少动画)。