requestAnimationFrame

requestAnimationFrame (通常缩写为 rAF) 是一个浏览器提供的核心 API,用于高效地执行动画。它允许开发者将动画更新函数(重绘操作)安排在浏览器下一次重绘之前执行

核心目的

实现流畅、高效、与屏幕刷新率同步的动画。相比于使用 setTimeoutsetIntervalrAF 具有以下显著优势:

  1. 同步刷新率rAF 的回调函数会在浏览器下一次重绘周期执行。在标准的 60Hz 显示器上,这大约每 16.7ms (1000ms / 60) 执行一次,能充分利用显示器的刷新能力,达到 60 FPS 的流畅效果。
  2. 自动节流:当标签页不可见(如切换到其他标签页或最小化)时,浏览器会自动暂停 rAF 的执行,节省 CPU、GPU 和电池资源。而 setTimeout 在后台仍会继续执行。
  3. 减少卡顿:浏览器会优化 rAF 的调用时机,尽量确保在每一帧的开始执行,避免与浏览器自身的渲染工作冲突,从而减少掉帧和卡顿。
  4. 更精确的时间戳rAF 的回调函数会接收一个高精度的时间戳(DOMHighResTimeStamp),表示动画开始的时间,便于计算精确的动画进度。

基本语法

const handle = window.requestAnimationFrame(callback);
  • callback:一个函数,它会在浏览器下一次重绘之前被调用。
  • 返回值:一个 long 类型的整数句柄 (handle),可用于取消动画请求。

回调函数参数

callback 函数会接收一个参数:

  • time (或 timestamp): 一个 DOMHighResTimeStamp 类型的数值,表示从页面加载(或 performance.timing.navigationStart)到 rAF 回调触发时的毫秒数。这个时间非常精确,是计算动画进度的理想依据。

如何使用 requestAnimationFrame 创建动画

rAF 本身只执行一次。要创建持续的动画,需要在 callback 函数内部递归地再次调用 requestAnimationFrame

// 1. 定义动画起始状态
let startTime = null;
const duration = 2000; // 动画持续时间,2秒
const startValue = 0;
const endValue = 100;

function animate(currentTime) {
  // 2. 第一次调用时记录开始时间
  if (startTime === null) {
    startTime = currentTime;
  }

  // 3. 计算已过去的时间
  const elapsed = currentTime - startTime;

  // 4. 计算动画进度 (0 到 1)
  const progress = Math.min(elapsed / duration, 1); // Math.min 确保不超过 1

  // 5. 根据进度计算当前值 (这里用线性插值)
  const currentValue = startValue + (endValue - startValue) * progress;

  // 6. 更新元素 (执行视觉变化)
  document.getElementById('myElement').style.transform = `translateX(${currentValue}px)`;

  // 7. 如果动画未完成,继续请求下一帧
  if (progress < 1) {
    requestAnimationFrame(animate);
  } else {
    console.log('Animation completed!');
  }
}

// 8. 启动动画
requestAnimationFrame(animate);

取消动画

使用 cancelAnimationFrame(handle) 可以取消一个已请求但尚未执行的 rAF 回调。

let animationHandle = null;

function startAnimation() {
  function frame(currentTime) {
    // ... 动画逻辑
    animationHandle = requestAnimationFrame(frame);
  }
  animationHandle = requestAnimationFrame(frame);
}

function stopAnimation() {
  if (animationHandle !== null) {
    cancelAnimationFrame(animationHandle);
    animationHandle = null;
  }
}

setTimeout/setInterval 的对比

特性requestAnimationFramesetTimeout / setInterval
执行时机在浏览器下一次重绘之前执行。在指定的延迟后执行,不考虑重绘周期
刷新率同步,自动与屏幕刷新率同步 (如 60 FPS)。,时间间隔固定,可能导致跳帧或卡顿。
后台行为自动暂停,节省资源。继续执行,消耗资源。
性能更高,减少不必要的重绘,更流畅。较低,可能在非重绘时机执行,浪费。
时间精度提供高精度时间戳 (DOMHighResTimeStamp)。使用 Date.now(),精度较低。
适用场景首选用于视觉动画需要流畅更新的场景。适用于非视觉的定时任务、轮询、或对时间精度要求不高的简单动画。

最佳实践

  1. 用于动画rAF 是创建平滑 CSS/JS 动画、游戏循环、滚动效果等的首选方法
  2. 避免复杂计算:确保 rAF 回调中的代码执行时间远小于 16ms(60 FPS 下),否则会导致掉帧。将耗时计算移到 Web Workers 或使用 scheduler.postTask
  3. 组合使用:对于非动画的定时任务,优先使用 setTimeout/setIntervalrAF 专为视觉更新而生。
  4. 利用时间戳:始终使用回调参数 time 来计算动画进度,而不是依赖固定的间隔。
  5. 及时清理:在动画结束或组件销毁时,使用 cancelAnimationFrame 清理未完成的请求,防止内存泄漏。

总结

requestAnimationFrame 是现代 Web 开发中实现高性能动画的基石。它通过将动画更新与浏览器的渲染循环紧密耦合,确保了动画的流畅性、高效性和节能性。理解并正确使用 rAF 是创建卓越用户体验的关键。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值