requestAnimationFrame 是浏览器重绘前执行一次同步回调的节拍器,非异步调度器;正确用法是 RAF 中仅同步绘制,状态更新完全解耦,多图层通过结构化数组驱动,异步逻辑不得阻塞主线程。

requestAnimationFrame 本身不是异步任务调度器,它不管理“多任务”,也不处理异步逻辑。它的作用很单纯:在浏览器下一次重绘前,执行一次你给的同步回调函数。所谓“多任务异步渲染”,其实是开发者通过结构设计,在单个 RAF 回调里协调多个渲染动作,并把数据更新与绘制彻底分开。
关键不在 RAF 多线程,而在你怎么组织状态、更新和绘制三者的关系。
渲染必须是纯同步的
RAF 回调里不能出现 await、fetch、setTimeout 或任何可能阻塞主线程的操作。
否则整个动画帧会被挂起,导致掉帧甚至冻结。
- ✅ 正确做法:RAF 函数只做两件事——清空画布 + 按当前状态逐层 drawImage 或 strokeRect
- ❌ 错误做法:在 animate() 里写
const data = await api.get(); render(data);
你可以把它想象成一个“快照播放器”:每帧只读取此刻内存里的坐标、颜色、可见性等值,然后画出来。至于这些值怎么变,那是别的函数管的事。
多图层/多对象的绘制靠结构化队列
不要为每个图片或元素单独写一个 RAF 循环。统一用一个主循环,配合配置数组来驱动:
立即学习“前端免费学习笔记(深入)”;
const layers = [
{ id: 'bg', img: images.bg, x: 0, y: 0, opacity: 1, visible: true },
{ id: 'char', img: images.char, x: 120, y: 80, opacity: 0.95, visible: true },
{ id: 'ui', img: images.ui, x: 10, y: 10, opacity: 1, visible: isMenuOpen }
];
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (const layer of layers) {
if (!layer.visible) continue;
ctx.globalAlpha = layer.opacity;
ctx.drawImage(layer.img, layer.x, layer.y);
}
ctx.globalAlpha = 1; // 重置
}这样,新增一个图层只需往 layers 里推一个对象,无需改 RAF 主逻辑。
异步数据更新要完全解耦
图片加载、API 响应、用户输入、定时变化……这些都属于“状态更新源”,它们应该:
- 独立于 RAF 运行(比如用
img.onload、fetch().then()、setInterval()触发) - 只修改共享的状态对象(如
player.x、score、layers[1].visible) - 不操作 Canvas,不调用
drawImage
例如:
// 异步更新角色位置(来自 WebSocket 或定时器)
function updatePlayerPosition() {
player.x += Math.sin(Date.now() * 0.002) * 2;
player.y += 0.5;
}
setInterval(updatePlayerPosition, 32); // 约 30fps,不卡 RAF
// RAF 中只读不写
function animate() {
render(); // 用当前 player.x/player.y 绘制
requestAnimationFrame(animate);
}多个动画节奏可以共存,但不要混在同一帧里算逻辑
如果有的元素要 60fps 更新(如粒子),有的只要 10fps(如背景缓慢滚动),别在 RAF 里加 if 判断帧数——那样会让代码难维护、易出错。
更清晰的做法是:
- 所有 RAF 帧都跑,但用时间戳控制逻辑频率:
let lastScrollTime = 0;
function animate(timestamp) {
// 背景每 200ms 动一次
if (timestamp - lastScrollTime > 200) {
bgOffsetX += 1;
lastScrollTime = timestamp;
}
// 粒子每帧都更新
particles.forEach(p => p.update());
render();
requestAnimationFrame(animate);
}这种方式既保持 RAF 的稳定节拍,又支持不同节奏的逻辑更新。
本质上,RAF 是节拍器,不是调度器;状态是中心,不是附属;绘制是快照,不是过程。



















