JavaScript requestAnimationFrame 怎么用
想让网页动画丝滑不卡顿?很多人第一反应是用setTimeout或setInterval,但你可能不知道,这两个“老朋友”在做动画时很容易掉链子——比如页面切到后台,它们还在偷偷跑,占着性能;帧率波动时,动画会突然“卡一下”。这时,浏览器自带的requestAnimationFrame(下面简称RAF)就是更合适的选择,它就像动画的“专属导演”,能跟着浏览器的渲染节奏走,让动画丝滑度拉满。
RAF到底是什么?和setTimeout有啥不一样?
其实RAF就是浏览器提供的一个“动画调度工具”,作用是告诉浏览器:“我要做动画了,请你按屏幕的刷新节奏来安排我的执行时间”。现在大部分电脑、手机屏幕的刷新频率是60Hz,也就是每秒刷60次,大概16.6毫秒一次;要是屏幕是120Hz,刷新间隔就变成8.3毫秒——RAF会精准贴合这个节奏,所以做出来的动画不会忽快忽慢。

对比setTimeout的问题就很明显了:比如你用setInterval做一个元素移动的动画,设置间隔16毫秒,看起来和60帧一样,但如果浏览器刚好在处理其他任务(比如加载图片、解析代码),setTimeout的回调就会被“插队”,错过一次屏幕刷新,动画就会卡顿;而且当页面最小化、切到后台,setInterval还会继续执行,既浪费电量,还可能导致页面崩溃。而RAF会自动跟着页面状态走,后台页面会暂停,切回来再继续,完全不浪费资源。
怎么用RAF做一个简单的动画?
用RAF做动画的逻辑很简单:它是一个“递归”的方法——每次执行动画逻辑后,再用RAF把下一次动画安排上,就像动画循环一样。我们来做一个“方块从左往右移动”的小例子,跟着步骤走就懂:
第一步,先在HTML里加一个要动的方块,给它一个初始的左边距:
`html
<div id="box" style="width:50px; height:50px; background:blue; position:absolute; left:0;"></div>
`
第二步,写JS逻辑,核心是用RAF调度动画:
`javascript
// 拿到要移动的方块元素
const box = document.getElementById('box');
// 定义移动的速度,每帧移动3px
let position = 0;
function moveBox() {
// 每帧给位置加3,更新方块的左边距
position += 3;
box.style.left = position + 'px';
// 当位置没超过屏幕宽度时,继续调度下一次RAF
if (position < window.innerWidth - 50) {
requestAnimationFrame(moveBox);
}
}
// 页面加载完就启动动画
window.onload = moveBox;
`
你运行这段代码就会发现,方块移动得特别丝滑——因为每一次moveBox的执行,都刚好卡在屏幕的刷新节点上,不会出现“跳帧”的情况。
用RAF要注意这2个小细节
RAF虽然好用,但踩过这两个小坑,才能让它发挥最大作用:
第一,不要在RAF里做太耗时的操作。RAF是跟着屏幕刷新走的,如果你在回调里加了一个需要100毫秒的计算,那浏览器就会错过本次刷新,动画直接卡100毫秒,相当于从60帧掉到10帧,反而不如不用。所以RAF的回调里只放“改变动画属性”的逻辑,复杂计算提前算好,别堆在里面。
第二,手动停止动画要记得取消调度。如果你的动画需要中途停止,比如用户点击按钮就让方块停下,不能只停变量,要调用cancelAnimationFrame方法,把之前安排的RAF取消。比如给停止按钮加个事件:
`javascript
const stopBtn = document.getElementById('stop');
stopBtn.onclick = () => {
cancelAnimationFrame(animationId); // animationId是之前调用RAF返回的ID
};
`
这个ID要存起来,不然没法取消之前的调度,动画可能还会偷偷跑。
其实不止移动元素,做渐隐效果、滚动监听、甚至Canvas动画,RAF都是基础工具——它本质是让开发者和浏览器的渲染节奏“同频”,既能保证动画流畅,又不会浪费系统性能。下次做网页动画,不妨试试用RAF代替setTimeout,就能感受到丝滑度的差别。