requestAnimationFrame(简称 RAF)是浏览器提供的高精度动画 API,专门用于优化网页动画渲染,相比 setTimeout/setInterval 更流畅、更省性能。其核心思想是让动画与浏览器的刷新频率同步(通常为 60Hz,即每 16.67ms 刷新一次),避免动画卡顿。
一、核心作用与优势
1. 解决 setTimeout/setInterval 的痛点
- 定时器的
delay是 “最小延迟”,无法与浏览器刷新同步,可能导致动画跳帧(如 60Hz 下延迟 10ms,会与刷新周期错位)。 - RAF 由浏览器主动调度,每次刷新屏幕前执行回调,确保动画帧与刷新频率一致(无跳帧)。
2. 关键优势
- 同步刷新:与浏览器重绘周期同步,动画更流畅(60fps 最优体验)。
- 自动节流:后台标签页或隐藏元素时,RAF 会暂停执行,节省 CPU/GPU 资源(定时器仍会执行)。
- 精准计时:回调函数接收一个 “当前时间戳” 参数,可精确计算动画进度(避免定时器累积误差)。
二、基本语法
// 注册动画帧回调,返回一个唯一 ID(用于取消)
const requestId = requestAnimationFrame(callback);
// 取消动画帧(类似 clearTimeout)
cancelAnimationFrame(requestId);
callback:每次刷新前执行的函数,接收一个参数timestamp(DOMHighResTimeStamp 类型,高精度时间戳,单位 ms)。- 返回值
requestId:动画帧的唯一标识,用于后续取消。
三、基础示例:简单动画
用 RAF 实现一个 “方块向右移动” 的动画:
<div id="box" style="width: 50px; height: 50px; background: red; position: absolute;"></div>
<script>
const box = document.getElementById('box');
let position = 0; // 方块初始位置
// 动画回调函数
function animate(timestamp) {
// 更新位置(每次移动 2px)
position += 2;
box.style.left = position + 'px';
// 边界判断:位置 < 500px 时继续执行
if (position < 500) {
requestAnimationFrame(animate); // 递归注册下一次动画帧
}
}
// 启动动画
requestAnimationFrame(animate);
</script>
- 动画会以 60fps 流畅执行,方块匀速向右移动,直到 left 达到 500px。
- 若切换到后台标签页,动画会暂停,切回后继续执行(节省资源)。
四、进阶:控制动画速度与进度
RAF 的 timestamp 参数可用于精确控制动画速度(避免受刷新频率影响),例如实现 “2 秒内移动 500px” 的匀速动画:
<div id="box" style="width: 50px; height: 50px; background: blue; position: absolute;"></div>
<script>
const box = document.getElementById('box');
let startTimestamp; // 动画开始时间戳
function animate(timestamp) {
// 记录动画开始时间(仅第一次执行时)
if (!startTimestamp) startTimestamp = timestamp;
// 计算动画已执行时间(ms)
const elapsed = timestamp - startTimestamp;
// 动画总时长(2000ms),总距离(500px),计算当前位置
const duration = 2000;
const totalDistance = 500;
const progress = Math.min(elapsed / duration, 1); // 进度(0~1)
const currentPosition = progress * totalDistance;
// 更新样式
box.style.left = currentPosition + 'px';
// 进度 < 1 时继续执行(未结束)
if (progress < 1) {
requestAnimationFrame(animate);
}
}
// 启动动画
requestAnimationFrame(animate);
</script>
- 无论浏览器刷新频率是 60Hz 还是 120Hz,动画都会在 2 秒内完成 500px 移动(速度统一)。
Math.min(elapsed / duration, 1)确保进度不超过 1(避免动画超出目标位置)。
五、与 setTimeout/setInterval 的对比
| 特性 | requestAnimationFrame | setTimeout/setInterval |
|---|---|---|
| 执行时机 | 浏览器刷新前(同步重绘周期) | 延迟指定时间后(异步,不同步) |
| 动画流畅度 | 高(60fps 无跳帧) | 可能卡顿(延迟错位导致跳帧) |
| 后台运行 | 暂停执行(省资源) | 继续执行(浪费资源) |
| 计时精度 | 提供高精度时间戳(无累积误差) | 依赖延迟参数(有累积误差) |
| 适用场景 | 网页动画、Canvas/SVG 动画 | 非动画类延迟任务(如倒计时提示) |
六、常见应用场景
- DOM 动画:元素位移、缩放、透明度变化等(替代
setTimeout控制样式)。 - Canvas/SVG 动画:游戏画面、数据可视化图表(如动态折线图)。
- 滚动动画:平滑滚动、滚动触发的动画(比
scrollTo更流畅)。 - 性能优化:批量 DOM 操作(将 DOM 修改放入 RAF 回调,避免强制重绘)。
示例:用 RAF 优化批量 DOM 操作(避免多次重绘):
// 批量修改多个元素样式(放入 RAF 回调,仅触发一次重绘)
function batchUpdate() {
const elements = document.querySelectorAll('.item');
elements.forEach((el, index) => {
el.style.opacity = index / elements.length;
});
}
// 确保 DOM 修改在同一帧完成,减少重绘次数
requestAnimationFrame(batchUpdate);
七、兼容性与降级方案
- 支持所有现代浏览器(Chrome 24+、Firefox 23+、Safari 6.1+、Edge 12+)。
- IE9 及以下不支持,需降级为
setTimeout:
// 兼容方案:优先使用 RAF,否则用 setTimeout 模拟(16ms 接近 60Hz)
const requestAnimFrame = window.requestAnimationFrame ||
function(callback) {
return setTimeout(callback, 16);
};
const cancelAnimFrame = window.cancelAnimationFrame ||
function(requestId) {
clearTimeout(requestId);
};
// 使用兼容版 API
requestAnimFrame(animate);
八、总结
requestAnimationFrame 是网页动画的 “最优解”,核心特点:
- 与浏览器刷新同步,动画流畅(60fps)。
- 自动节流,节省资源。
- 提供高精度时间戳,便于控制动画进度。
适用场景:所有需要视觉流畅的动画(DOM 动画、Canvas 游戏、数据可视化等);非动画类延迟任务(如延迟提示)仍可用 setTimeout。
实际开发中,建议优先使用 RAF 替代定时器实现动画,提升用户体验和性能。