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

如何实现vis-timeline的平滑缓动横向平移(pan-x)导航?

实现vis-timeline触摸横向平移的缓动效果

你的思路方向是对的,但当前代码存在两个核心问题:一是rangechange事件会在调用setWindow时被再次触发,导致循环执行引发卡顿;二是像素距离到时间范围的转换逻辑不准确,导致平移幅度不符合预期。以下是修正后的实现方案:

核心优化点

  • 增加状态标志位避免事件循环
  • 基于时间线缩放比例转换像素偏移为时间偏移
  • 配合原生动画API实现平滑缓动

完整代码实现

// 定义状态标志,防止setWindow触发的rangechange进入循环
let isProgrammaticPan = false;

timeline.on('rangechange', function(properties) {
  // 仅处理用户触摸触发的事件,跳过代码调用setWindow引发的事件
  if (isProgrammaticPan || !properties.event?.deltaX) return;

  // 获取当前时间窗口和缩放比例(每像素对应的毫秒数)
  const { start, end } = timeline.getWindow();
  const scale = timeline.getScale();
  const windowDuration = end - start;

  // 计算触摸偏移对应的时间偏移:deltaX是像素变化,乘以scale转为时间差
  // 乘以velocityX是为了给快速滑动增加惯性效果,可根据需求调整系数
  const timeOffset = properties.event.deltaX * scale * properties.event.velocityX * 0.5;

  // 计算新的时间窗口
  const newStart = start.valueOf() - timeOffset;
  const newEnd = newStart + windowDuration;

  // 设置标志位,避免setWindow触发的rangechange重复执行
  isProgrammaticPan = true;
  timeline.setWindow(newStart, newEnd, {
    animation: {
      easingFunction: 'easeInOutQuad',
      duration: 200 // 调整动画时长,适配触摸手感
    }
  });

  // 动画结束后重置标志位
  setTimeout(() => {
    isProgrammaticPan = false;
  }, 250);
});

额外优化建议

  • 如果内置的easingFunction效果不符合预期,可以自定义缓动函数,比如:
    timeline.setWindow(newStart, newEnd, {
      animation: {
        easingFunction: (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t, // 自定义easeInOutQuad
        duration: 200
      }
    });
    
  • 若触摸时的即时反馈仍然生硬,可以改用requestAnimationFrame手动实现逐帧缓动,替代内置动画:
    let animationFrameId = null;
    const targetStart = newStart;
    const targetEnd = newEnd;
    const currentStart = start.valueOf();
    const currentEnd = end.valueOf();
    const duration = 200;
    const startTime = Date.now();
    
    const animatePan = () => {
      const elapsed = Date.now() - startTime;
      const progress = Math.min(elapsed / duration, 1);
      // 应用缓动函数
      const easedProgress = progress < 0.5 ? 2 * progress * progress : -1 + (4 - 2 * progress) * progress;
      
      const newCurrentStart = currentStart + (targetStart - currentStart) * easedProgress;
      const newCurrentEnd = currentEnd + (targetEnd - currentEnd) * easedProgress;
      
      timeline.setWindow(newCurrentStart, newCurrentEnd, { animation: false });
      
      if (progress < 1) {
        animationFrameId = requestAnimationFrame(animatePan);
      } else {
        isProgrammaticPan = false;
        cancelAnimationFrame(animationFrameId);
      }
    };
    
    animationFrameId = requestAnimationFrame(animatePan);
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 21:30:41