如何实现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
相关产品推荐
相关产品推荐

