requestAnimationFrame嵌套使用致主动画变慢的原因及正确方案
问题原因分析
你的主动画变慢,核心问题出在多个动画循环叠加和错误结合setTimeout与requestAnimationFrame这两点上:
1. 主循环的setTimeout画蛇添足
requestAnimationFrame是浏览器专为动画设计的API,它会自动跟浏览器刷新频率(通常60fps)同步调用回调。但你在main函数里用setTimeout(function(){...}, 1000/60)包裹了requestAnimationFrame,这相当于给每帧动画额外加了一层定时器延迟。
更糟的是,setTimeout精度有限,回调会被放到任务队列等待执行;当辅助动画doSomething也启动独立的requestAnimationFrame循环时,两个循环会同时抢占主线程资源,导致每帧需要执行更多绘制逻辑,浏览器处理不过来,最终表现为主动画卡顿变慢。
2. 辅助动画的实现方式错误
你用setTimeout延迟取消辅助动画,这种方式没有和浏览器刷新周期同步。而且两个独立的requestAnimationFrame循环会让浏览器在每一帧执行两次绘制任务(主循环+辅助循环),进一步加重主线程负担,帧率自然下降。
正确的实现方式
正确的思路是把所有动画逻辑整合到同一个requestAnimationFrame循环里,用状态变量管理辅助动画的启停,基于时间差控制动画时长,完全摒弃独立循环和不合理的setTimeout嵌套。
下面是修改后的完整代码:
const canvas = document.querySelector('canvas'); const context = canvas.getContext('2d'); // 主动画相关变量 let count = 0; let lastTime = 0; // 辅助动画状态管理 let isDoingSomething = false; let doSomethingStartTime = 0; const DO_SOMETHING_DURATION = 2000; // 辅助动画持续2秒 const main = function(now) { // 计算时间差,保证动画速度不受帧率波动影响 const deltaTime = now - lastTime; lastTime = now; // 清空画布 context.clearRect(0, 0, canvas.width, canvas.height); // 执行主动画:矩形增长(基于时间差,每秒稳定增长60) count += deltaTime / 16.67; // 16.67ms对应60fps的单帧时长 context.fillStyle = "#f00"; context.fillRect(10, 30, count, 10); // 检查是否触发辅助动画 const nowSeconds = Math.floor(now / 1000); if (nowSeconds % 5 === 0 && !isDoingSomething) { isDoingSomething = true; doSomethingStartTime = now; } // 执行辅助动画并判断是否结束 if (isDoingSomething) { context.fillStyle = "#f00"; context.font = "20px calibri"; context.fillText('DoSomething', 10, 70); // 基于实际运行时间判断是否结束辅助动画 if (now - doSomethingStartTime >= DO_SOMETHING_DURATION) { isDoingSomething = false; } } // 持续主循环 requestAnimationFrame(main); }; // 启动动画 requestAnimationFrame(main);
<canvas width="800" height="600"></canvas>
关键改进点:
- 移除主循环里的
setTimeout,让requestAnimationFrame自然同步浏览器刷新节奏 - 用
deltaTime(时间差)计算主动画进度,保证不同帧率下动画速度一致(之前的count++会在低帧率下变慢) - 用状态变量
isDoingSomething和时间戳管理辅助动画,所有绘制逻辑统一在主循环完成,避免多循环抢占资源 - 辅助动画的结束判断基于实际运行时间,比
setTimeout更准确且与动画帧同步
这样修改后,主动画和辅助动画都会流畅运行,不会再出现卡顿变慢的问题。
内容的提问来源于stack exchange,提问作者Henderson Lela
相关产品推荐
相关产品推荐

