javascript动画如何实现_如何使用requestAnimationFrame?

requestAnimationFrame(rAF)是专为JavaScript动画设计的API,它在下一次屏幕重绘前执行回调,比setInterval更精准节能;需递归调用自身实现持续动画,并推荐基于时间戳计算位移以保障匀速。

JavaScript 动画的核心是让元素在短时间内连续、平滑地更新样式,requestAnimationFrame(简称 rAF)就是为这个目的专门设计的 API —— 它让浏览器决定何时执行动画帧,比 setTimeoutsetInterval 更精准、更节能、更契合屏幕刷新节奏。

为什么用 requestAnimationFrame 而不是 setInterval?

浏览器通常以约 60fps(每秒 60 帧)刷新画面,即每 ~16.7ms 渲染一帧。rAF 会自动把回调函数安排在**下一次屏幕重绘之前**执行,保证动画与刷新率同步;而 setInterval(fn, 16) 只是“尽力每 16ms 调用一次”,实际可能跳帧、卡顿,还可能在页面不可见时继续运行,浪费资源。

requestAnimationFrame 的基本用法

rAF 接收一个回调函数,返回一个 ID(类似 setTimeout),可用于取消动画:

  • 调用 requestAnimationFrame(callback) 启动动画帧
  • 回调函数中更新元素状态(如位置、透明度)
  • 若需持续动画,在回调里**递归调用自身**(这是关键!)
  • cancelAnimationFrame(id) 中止动画

示例:让一个 div 水平移动 200px

const box = document.getElementById('box');
let pos = 0;
let animationId;

function animate() { pos += 2; if (pos >= 200) { box.style.left = '200px'; return; // 动画结束,不继续调用 } box.style.left = pos + 'px'; animationId = requestAnimationFrame(animate); // 下一帧继续 }

animationId = requestAnimationFrame(animate);

// 暂停/取消示例: // cancelAnimationFrame(animationId);

结合时间控制实现匀速动画(推荐)

上面例子是“每帧固定位移”,但不同设备帧率可能波动(比如低端机掉到 30fps),导致速度不一致。更健壮的做法是基于**时间差**计算位移:

  • 记录上一帧的时间戳(由 rAF 回调自动传入)
  • 当前帧减去上一帧,得到真实经过的毫秒数
  • 按目标速度(如 100px/s)乘以该时间,得出本次应移动距离

示例(匀速移动 200px,耗时 2 秒):

const box = document.getElementById('box');
const duration = 2000; // 总时长 ms
const targetX = 200;
let startTime = null;

function animate(timestamp) { if (!startTime) startTime = timestamp; const elapsed = timestamp - startTime; const progress = Math.min(elapsed / duration, 1); // 0~1 const currentX = progress * targetX;

box.style.transform = translateX(${currentX}px);

if (progress < 1) { requestAnimationFrame(animate); } }

requestAnimationFrame(animate);

实用建议和注意事项

  • 优先使用 transformopacity 动画属性,它们能触发 GPU 加速,性能远好于修改 left/topwidth/height
  • 避免在动画帧中频繁读取布局属性(如 offsetTopgetBoundingClientRect),会触发强制同步布局(layout thrashing)
  • rAF 在页面后台标签页中会自动降频或暂停,这是优点;如需后台运行(极少见),得配合 Page Visibility API 判断
  • 现代开发中,复杂动画可考虑 CSS @keyframes + element.animate(),但 rAF 仍是底层控制、物理模拟(如缓动、拖拽、粒子)的首选

基本上就这些。掌握 rAF 的递归调用逻辑和时间驱动思维,就能写出流畅、兼容、可维护的 JS 动画。