如何暂停TWEEN动画且恢复时不跳转至最终位置?解决排序可视化问题
问题解决思路与代码示例
1. 修复TWEEN动画暂停跳转问题
TWEEN.js没有原生暂停API,但可以手动记录动画进度实现无跳转的暂停/继续:
- 给每个Tween实例扩展
pause()和resume()方法,记录暂停时的已流逝时间 - 暂停时从全局更新队列移除实例,恢复时重新计算起始时间并加回队列
代码示例:
// 给Tween原型扩展暂停/继续方法 TWEEN.Tween.prototype.pause = function() { if (!this._isPaused) { this._elapsedTime = Date.now() - this._startTime; this._isPaused = true; // 从全局队列移除当前动画 const index = TWEEN._tweens.indexOf(this); if (index !== -1) TWEEN._tweens.splice(index, 1); } return this; }; TWEEN.Tween.prototype.resume = function() { if (this._isPaused) { this._isPaused = false; // 重新计算起始时间,保证动画从暂停点继续 this._startTime = Date.now() - this._elapsedTime; TWEEN.add(this); } return this; }; // 批量暂停所有动画 function pauseAllTweens() { TWEEN._tweens.forEach(tween => tween.pause()); } // 批量恢复所有动画 function resumeAllTweens() { TWEEN._tweens.forEach(tween => tween.resume()); }
2. 控制排序算法与动画同步
排序逻辑不能同步硬跑,必须改成异步分步执行,让每一步都等待动画完成/暂停状态解除后再继续:
- 用全局开关
isPaused控制流程,暂停时进入等待状态 - 把排序拆成单个步骤,用Promise包裹动画操作,确保动画完成后再执行下一步
代码示例:
let isPaused = false; let resumeResolve; // 暂停时等待恢复信号的函数 function waitForResume() { return new Promise(resolve => { resumeResolve = resolve; }); } // 恢复排序流程 function resumeSort() { isPaused = false; resumeResolve?.(); } // 异步冒泡排序示例 async function bubbleSort(elements) { const len = elements.length; for (let i = 0; i < len; i++) { for (let j = 0; j < len - i - 1; j++) { // 检查是否暂停,暂停则等待 while (isPaused) { await waitForResume(); } // 触发对比可视化:改变元素颜色 elements[j].material.color.set(0xff0000); elements[j+1].material.color.set(0xff0000); // 等待对比可视化完成 await new Promise(resolve => setTimeout(resolve, 1000)); if (elements[j].value > elements[j+1].value) { // 执行交换动画,用Promise包裹确保动画完成后再继续 await swapElements(elements[j], elements[j+1]); } // 恢复元素默认颜色 elements[j].material.color.set(0xffffff); elements[j+1].material.color.set(0xffffff); } } } // 交换动画的Promise封装 function swapElements(el1, el2) { return new Promise(resolve => { const pos1 = el1.position.x; const pos2 = el2.position.x; new TWEEN.Tween(el1.position) .to({x: pos2}, 500) .onComplete(() => resolve()) .start(); new TWEEN.Tween(el2.position) .to({x: pos1}, 500) .start(); }); } // 播放/暂停按钮绑定逻辑 document.getElementById('pauseBtn').addEventListener('click', () => { isPaused = !isPaused; if (isPaused) { pauseAllTweens(); } else { resumeAllTweens(); resumeSort(); } });
3. 添加同步旁白语音
用浏览器原生Web Speech API实现,和排序步骤绑定触发:
const synth = window.speechSynthesis; // 播放旁白的工具函数 function speak(text) { if (synth.speaking) { synth.cancel(); } const utterance = new SpeechSynthesisUtterance(text); synth.speak(utterance); } // 在排序步骤中插入旁白调用 async function bubbleSort(elements) { // ... while (isPaused) { await waitForResume(); } speak(`正在对比第${j+1}个和第${j+2}个元素`); elements[j].material.color.set(0xff0000); elements[j+1].material.color.set(0xff0000); await new Promise(resolve => setTimeout(resolve, 1000)); if (elements[j].value > elements[j+1].value) { speak(`交换第${j+1}个和第${j+2}个元素`); await swapElements(elements[j], elements[j+1]); } // ... }
要不要更换动画库?
如果觉得手动扩展TWEEN.js太繁琐,可以换用GSAP:它原生支持暂停/继续、时间线管理,能更轻松地控制复杂动画序列,还能和异步逻辑更好地结合。但如果上面的方法能解决你的问题,继续用TWEEN.js也完全没问题——它轻量且足够覆盖当前需求。
内容的提问来源于stack exchange,提问作者sam_xp
相关产品推荐
相关产品推荐

