写个人博客时,想实现一个雪花飘落的动画效果,一开始用的是setInterval来不断执行动画,但是发现动画会
卡顿,后来找资料发现有个更好用的函数,于是写下这篇比较详细的学习博客
一、什么是 requestAnimationFrame
?
requestAnimationFrame
是浏览器提供的一个 原生 API,用于在浏览器下一次重绘(repaint)之前执行 JavaScript 回调函数。
它是进行 高性能动画渲染 的首选方式。
const id = requestAnimationFrame(callback);
-
浏览器每次屏幕刷新时都会调用一次回调(大多数显示器是 60Hz,即每秒 60 帧,约 16.7ms 一次)
-
它不会主动控制时间间隔,而是将你注册的回调交由浏览器统一调度
-
自动与浏览器渲染时机同步,防止掉帧
二、浏览器渲染流程简析
理解 rAF 的最佳方式是先理解浏览器的 渲染帧机制:
一次完整的帧流程(16.7ms)如下: 事件处理 -> requestAnimationFrame -> 样式计算 -> 布局 -> 绘制 -> 合成 -> 显示
💡
requestAnimationFrame
的调用时机:在样式计算和布局之前执行!
这意味着:你可以在回调中修改 DOM,浏览器会使用这些最新变动来进行后续布局和绘制,从而实现平滑过渡。
三、requestAnimationFrame的参数
requestAnimationFrame
是一个使用非常简单但功能强大的 API。它本身只接收一个参数,但是这个参数是一个 带有内置传参的回调函数,但是它的回调函数会有一个参数
requestAnimationFrame(function(timestamp) {
console.log(timestamp);
});
timestamp
是什么?
名称 | 类型 | 含义 |
---|---|---|
timestamp | DOMHighResTimeStamp | 当前帧执行回调时的时间戳(以毫秒为单位,高精度小数) |
使用例子:
let startTime;
function step(timestamp) {
if (!startTime) startTime = timestamp;
const progress = timestamp - startTime;
box.style.left = Math.min(progress / 10, 200) + 'px';
if (progress < 2000) {
requestAnimationFrame(step);
}
}
requestAnimationFrame(step);
是不是有点看不懂,其实这段代码的整体作用是:
在 2 秒内,让一个 DOM 元素 box
从当前位置平滑地向右移动 最多 200px。
-
控制动画持续时间:
progress < 2000
-
控制动画移动距离:
Math.min(..., 200)
-
使用
requestAnimationFrame
实现高性能、掉帧补偿的自然动画
四、requestAnimationFrame
vs setTimeout
/ setInterval
特性 | requestAnimationFrame | setTimeout / setInterval |
---|---|---|
是否跟屏幕刷新同步 | ✅ 是(与显示器刷新率同步) | ❌ 否(固定间隔,可能跳帧) |
节能优化 | ✅ 是(处于后台标签页时自动暂停) | ❌ 否(后台仍继续执行) |
平滑度 | ✅ 动画自然,防止卡顿 | ❌ 容易卡顿、动画不连贯 |
调度顺序 | ✅ 每帧绘制前调用,紧跟渲染周期 | ❌ 调度不固定,可能在绘制后执行 |
控制方式 | 基于浏览器调度,无需手动设置间隔 | 需手动设置延迟时间,如 setTimeout(fn, 16) |
五.相比之下requestAnimationFrame 的优点
优点 | 说明 |
---|---|
同步刷新率 | 与屏幕刷新一致(一般 60FPS),动画更流畅 |
节能优化 | 标签页不活跃时自动暂停,节省资源 |
高精度时间戳 | 内置 timestamp ,便于控制动画进度 |
性能更好 | 优先级高,避免掉帧,浏览器可优化渲染 |
自动调帧率 | 不需手动设置间隔,帧率智能控制 |