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

JavaScript动画到达目标位置耗时不符预期问题排查求助

问题描述
  • 可视化元素高度由按键时长决定,从-visual_height位置向下掉落,需求是元素碰到琴键时播放音频(琴键高度为key_wrapper的25%,尺寸参考附图)
  • 预期逻辑:元素从top:0移动到key_wrapper顶部时长为3秒,以此推导速度,确保从-visual_height到top:0的时长仍为3秒;音频在3秒的75%时机触发(因琴键位于key_wrapper的25%高度处)
  • 当前问题:动画总时长不符合预期,音频播放时机存在偏差

带尺寸标注的钢琴

原始代码

// visual creation
return ({...prev_state,
          [pb_counter.current]: (
          <div key={`${pb_counter.current}`} ref={ref => playback_visual_refs.current[pb_counter.current] = ref} className='pb-visualizer-instance'
          style={{height: `${(timestamps[1] - timestamps[0]) * 0.3}px`}}></div>
          )
        })

useEffect(() => {
    if (playback_visuals[pb_counter.current] && curr_pb_anim.current[1]) {
      const computed_style = window.getComputedStyle(playback_visual_refs.current[pb_counter.current])
      const height = parseFloat(computed_style.getPropertyValue('height'))
      const duration = 3000 + ((3000 / key_wrapper.current.clientHeight) * height)
     

      // Timer just sets a timeout
      const f_timeout_id = new Timer(() => {
        audio.current.play()
       // ((3000 / key_wrapper.current.clientHeight) * 7.5) for 7.5 pixel line on top of keys
      }, (3000 * .75) + ((3000 / key_wrapper.current.clientHeight) * 7.5))

      
      curr_pb_anim.current[0].push(attribute_animation(playback_visual_refs.current[pb_counter.current], 'top', 
      `-${height}px`,
       `${(key_wrapper.current.clientHeight)}px`, duration))
        curr_pb_anim.current[1] = false
    }
    
  }, [playback_visuals])

// animation function
export const attribute_animation = (object, attribute, start, end, duration, easing) => {
    return object.animate(
        [{[attribute]: start}, { [attribute]: end}],
        {duration: duration, fill: 'forwards', easing: easing}
    )
}

问题排查与修正方案

核心错误点

  1. 动画时长计算逻辑错误
    原公式3000 + ((3000 / key_wrapper.clientHeight) * height)混淆了距离与速度的关系:

    • 元素总移动距离为key_wrapper.clientHeight + height(从-height到key_wrapper.clientHeight)
    • 已知移动key_wrapper.clientHeight需3000ms,速度为key_wrapper.clientHeight / 3000(像素/ms)
    • 正确总时长应为:(key_wrapper.clientHeight + height) / (key_wrapper.clientHeight / 3000),简化后为3000 * (1 + height / key_wrapper.clientHeight)
  2. 音频触发时机计算错误
    原公式额外添加的7.5px偏移无合理依据,且未考虑动画缓动的影响:

    • 琴键顶部位置为key_wrapper.clientHeight * 0.75(琴键占25%,从底部向上计算)
    • 元素底部到达琴键顶部时,移动的距离为key_wrapper.clientHeight * 0.75,对应时间为(key_wrapper.clientHeight * 0.75) / 速度
  3. 动画未使用匀速缓动
    默认easing: ease会导致动画先慢后快,完全破坏基于匀速的时间计算,必须强制设为linear

修正后的代码

useEffect(() => {
    if (playback_visuals[pb_counter.current] && curr_pb_anim.current[1]) {
      const visualEl = playback_visual_refs.current[pb_counter.current];
      const computed_style = window.getComputedStyle(visualEl);
      const height = parseFloat(computed_style.getPropertyValue('height'));
      const keyWrapperHeight = key_wrapper.current.clientHeight;
      
      // 计算匀速移动的速度(像素/毫秒)
      const speed = keyWrapperHeight / 3000;
      // 计算动画总时长
      const totalDuration = (keyWrapperHeight + height) / speed;
      // 计算音频触发的准确时间
      const audioTriggerTime = (keyWrapperHeight * 0.75) / speed;

      // 触发音频播放
      const f_timeout_id = new Timer(() => {
        audio.current.play();
      }, audioTriggerTime);

      // 执行动画,强制使用linear缓动保证匀速
      curr_pb_anim.current[0].push(attribute_animation(
        visualEl, 
        'top', 
        `-${height}px`,
        `${keyWrapperHeight}px`, 
        totalDuration,
        'linear'
      ));
      curr_pb_anim.current[1] = false;
    }
}, [playback_visuals])

// 动画函数默认添加linear缓动,避免遗漏
export const attribute_animation = (object, attribute, start, end, duration, easing = 'linear') => {
    return object.animate(
        [{[attribute]: start}, { [attribute]: end}],
        {duration: duration, fill: 'forwards', easing: easing}
    )
}

额外检查项

  • 确认可视化元素的定位为absolute或fixed,否则top属性无法生效
  • 若key_wrapper存在padding/border,建议用offsetHeight代替clientHeight获取准确高度
  • 测试时可先用原生setTimeout替代自定义Timer类,排除定时器实现的误差

内容的提问来源于stack exchange,提问作者The Canadian

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 02:52:04