如何避免Three.js中requestAnimationFrame驱动的动画随消息发送次数增多而加速?
嘿,我看了你的代码,问题其实挺典型的——动画加速的根源是每次发消息调用animateProcessingEffect时,你以为取消了之前的动画循环,但实际上根本没取消掉,导致多个动画帧请求同时在跑,叠加起来自然越来越快。
咱们一步步来解决:
问题核心:动画帧ID的作用域问题
你代码里的animationFrameId看起来没有在全局作用域声明,如果它是animateProcessingEffect内部的局部变量,那每次调用这个函数时,你用cancelAnimationFrame(animationFrameId)取消的都是当前函数里的新变量(初始值是undefined),上一次启动的动画循环完全没被终止,还在后台跑着。新的动画又启动,多个循环同时修改currentProcessing并调用update,速度自然就翻倍、翻几倍了。
修复方案
1. 全局声明动画帧ID
在你的全局变量区域(比如和boostedSpeed、currentProcessing一起)加上:
let animationFrameId = null; // 全局存储动画帧ID,确保每次都能拿到上一次的请求ID
2. 完善动画取消与重置逻辑
修改animateProcessingEffect函数,确保启动新动画前彻底终止旧动画,并且在动画结束后重置ID,避免后续误判:
function animateProcessingEffect(targetProcessing = 1.8) { // 先终止之前的动画,不管有没有在运行 if (animationFrameId) { cancelAnimationFrame(animationFrameId); animationFrameId = null; // 重置ID,防止后续误操作 } const steps = 0.005; const revertSteps = 0.001; const revertDelay = 100; let isReverting = false; function animateStep() { if (!isReverting) { if (currentProcessing < targetProcessing) { currentProcessing = Math.min(currentProcessing + steps, targetProcessing); } else { isReverting = true; setTimeout(() => { animationFrameId = requestAnimationFrame(animateStep); }, revertDelay); return; } } else { if (currentProcessing > 1) { currentProcessing = Math.max(currentProcessing - revertSteps, 1); } else { animationFrameId = null; // 动画完全结束后,重置ID return; } } update(); animationFrameId = requestAnimationFrame(animateStep); } animationFrameId = requestAnimationFrame(animateStep); }
3. 额外优化:避免重复触发(可选)
如果担心用户快速发送消息导致频繁启停动画,还可以加一个“动画锁”,比如全局声明isAnimating,在animateProcessingEffect开头判断:
let isAnimating = false; function animateProcessingEffect(targetProcessing = 1.8) { if (isAnimating) { cancelAnimationFrame(animationFrameId); animationFrameId = null; } isAnimating = true; // ... 原有逻辑 ... // 在动画结束的else分支里加上: isAnimating = false; }
这样就能确保同一时间只有一个动画循环在运行,彻底解决叠加加速的问题。
验证一下
修改后,你再发送消息试试——每次启动新的动画效果前,旧的循环都会被终止,只有当前这一个动画在跑,currentProcessing的修改和update的调用都会保持在正常频率,动画速度自然就稳定了。
备注:内容来源于stack exchange,提问作者EbDev

