Theatre.js + Three.js 调整动画速率的交互功能异常求助
Theatre.js + Three.js 调整动画速率的交互功能异常求助
嘿,我看了你这段基于Theatre.js和Three.js的交互动画代码,完全能get你想实现的效果:鼠标悬停对象时动画正向播放,移开时先慢慢减速到几乎停止,再平滑切换方向反向播放对吧?现在动画不管怎么操作都要跑完才停,确实挺闹心的,我帮你捋捋可能踩的坑和修改方向!
先再确认下你期望的核心逻辑:
- 鼠标悬停对象(动画激活):触发
InteractiveOn(),动画正向正常播放- 鼠标移开对象:触发
InteractiveOff(),先让动画速率渐变降到接近0,再切换方向反向播放
现有代码的核心问题
- 速率更新没同步到动画实例:你写了
TM_Func_ChangeRate()来渐变TM_SQ_Rate变量,但这个变量只是个“影子”,没有实时同步到正在播放的Theatre.js序列上,所以动画还是按初始速率跑到头。 - 状态变量逻辑混乱:
TM_Fl_playForward、TM_Fl_Direction这些状态的更新时机和判断条件不对,比如超时回调里的if (!TM_Fl_playForward)经常不符合实际交互状态,导致方向切换没触发或者触发错了。 - 动画没被实时控制:Theatre.js的
sequence.play()一旦启动,除非主动暂停/修改参数,否则会一直执行到序列结束。你现在的代码里移开鼠标后只是改了变量,没中断或调整正在运行的动画,所以它会继续跑完。 - 超时时机太突兀:100ms的超时太短,还没等减速效果显现就触发方向切换,视觉上根本看不到减速过程。
针对性修复建议
1. 实时把速率同步到动画实例
在TM_Func_GradualChange里,每次更新TM_SQ_Rate后,直接修改Theatre.js序列的速率属性,让动画跟着速率变化走:
function TM_Func_GradualChange(time) { if (!TM_Fl_RateChange) return; // 把减速时长拉长到300ms,让效果更明显 let timeFraction = (time - start) / 300; if (timeFraction > 1) timeFraction = 1; let progress = TM_Fl_Direction === 'forward' ? timing(timeFraction) : timing(1.01 - timeFraction); TM_SQ_Rate = Math.max(progress, 0.01); // 关键:实时同步速率到正在播放的动画 if (TM_Sequence && TM_Sequence.isPlaying) { TM_Sequence.rate = TM_SQ_Rate; } if (timeFraction < 1) { TM_changeRateId = requestAnimationFrame(TM_Func_GradualChange); } else { TM_Fl_RateChange = false; TM_changeRateId = null; // 减速完成后自动触发方向切换(仅在鼠标移开时) if (TM_Fl_Direction === 'back' && !TM_Fl_isHovered) { TM_Fl_playForward = false; TM_SQ_Direction = 'reverse'; TM_SQ_Rate = 1; // 先停止当前动画,再启动反向播放 if (TM_Sequence.isPlaying) TM_Sequence.stop(); start_TM_Animation({ direction: TM_SQ_Direction, rate: TM_SQ_Rate }); } } }
2. 清理交互逻辑,重置状态
在TM_InteractiveOn里,鼠标重新悬停时要把之前的异步操作(动画帧请求、超时)全部清理,避免状态冲突:
function TM_InteractiveOn() { if (!TM_Fl_isHovered) { TM_Fl_isHovered = true; // 清理所有遗留的异步任务 if (TM_changeRateId) { cancelAnimationFrame(TM_changeRateId); TM_changeRateId = null; } if (TM_stopTimeout) { clearTimeout(TM_stopTimeout); TM_stopTimeout = null; } TM_Fl_RateChange = false; if (TM_Sequence.position === 0) { TM_Fl_playForward = true; TM_SQ_Direction = 'normal'; TM_SQ_Rate = 1; if (TM_Sequence.isPlaying) TM_Sequence.stop(); start_TM_Animation({ direction: TM_SQ_Direction, rate: TM_SQ_Rate }); } else { // 动画在中间状态时,直接恢复正向速率播放 TM_Fl_playForward = true; TM_SQ_Direction = 'normal'; TM_SQ_Rate = 1; if (TM_Sequence.isPlaying) { TM_Sequence.rate = TM_SQ_Rate; TM_Sequence.direction = TM_SQ_Direction; } else { start_TM_Animation({ direction: TM_SQ_Direction, rate: TM_SQ_Rate }); } } } }
3. 简化start_TM_Animation参数
把参数改成更通用的形式,避免变量名混淆:
function start_TM_Animation({ direction = 'normal', rate = 1 }) { TM_Sequence.play({ range: [0, 1.5], iterationCount: 1, direction: direction, rate: rate, }); }
4. 调整TM_InteractiveOff的逻辑
去掉原来的超时,直接触发减速,让减速完成后自动切换方向:
function TM_InteractiveOff() { if (TM_Fl_isHovered) { TM_Fl_isHovered = false; if (TM_Sequence.position === 1.5) { TM_Fl_playForward = false; TM_SQ_Direction = 'reverse'; TM_SQ_Rate = 1; if (TM_Sequence.isPlaying) TM_Sequence.stop(); start_TM_Animation({ direction: TM_SQ_Direction, rate: TM_SQ_Rate }); } else { // 清理遗留任务,触发减速流程 if (TM_changeRateId) cancelAnimationFrame(TM_changeRateId); if (TM_stopTimeout) clearTimeout(TM_stopTimeout); TM_Fl_RateChange = false; TM_changeRateId = null; TM_Fl_Direction = 'back'; TM_Func_ChangeRate(); } } }
这样调整后,应该就能实现你想要的:悬停时正向播放,移开时先减速到接近停止,再平滑反向播放的效果啦!
备注:内容来源于stack exchange,提问作者P. Y. B. SAVAGE
相关产品推荐
相关产品推荐

