You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Theatre.js + Three.js 调整动画速率的交互功能异常求助

Theatre.js + Three.js 调整动画速率的交互功能异常求助

嘿,我看了你这段基于Theatre.js和Three.js的交互动画代码,完全能get你想实现的效果:鼠标悬停对象时动画正向播放,移开时先慢慢减速到几乎停止,再平滑切换方向反向播放对吧?现在动画不管怎么操作都要跑完才停,确实挺闹心的,我帮你捋捋可能踩的坑和修改方向!

先再确认下你期望的核心逻辑:

  • 鼠标悬停对象(动画激活):触发InteractiveOn(),动画正向正常播放
  • 鼠标移开对象:触发InteractiveOff(),先让动画速率渐变降到接近0,再切换方向反向播放

现有代码的核心问题

  1. 速率更新没同步到动画实例:你写了TM_Func_ChangeRate()来渐变TM_SQ_Rate变量,但这个变量只是个“影子”,没有实时同步到正在播放的Theatre.js序列上,所以动画还是按初始速率跑到头。
  2. 状态变量逻辑混乱:TM_Fl_playForward、TM_Fl_Direction这些状态的更新时机和判断条件不对,比如超时回调里的if (!TM_Fl_playForward)经常不符合实际交互状态,导致方向切换没触发或者触发错了。
  3. 动画没被实时控制:Theatre.js的sequence.play()一旦启动,除非主动暂停/修改参数,否则会一直执行到序列结束。你现在的代码里移开鼠标后只是改了变量,没中断或调整正在运行的动画,所以它会继续跑完。
  4. 超时时机太突兀: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 18:18:08