ReactJS时间轴缩放时保持鼠标相对元素位置的问题
解决React时间轴缩放时鼠标指向元素偏移的问题
你当前的时间轴缩放逻辑存在两个核心问题,导致鼠标指向的元素无法固定、右侧补偿失效:
- 缩放因子的计算方式依赖
scrollWidth和滚轮像素值,导致缩放比例不稳定,且和时间轴的timeScale逻辑不匹配 - 位移补偿没有正确计算鼠标在时间轴内容中的相对位置,忽略了容器的视口偏移
下面是修正后的实现方案:
1. 重构缩放因子计算
放弃用scrollWidth + deltaY的方式,改用固定的缩放倍率(比如每次滚轮滚动缩放1.1倍),这样缩放逻辑更可控,也和timeScale的语义匹配:
// 定义缩放步长,可根据需求调整 const ZOOM_STEP = 1.1; function handleZoom(event: WheelEvent) { if (event.deltaY === 0) return; event.preventDefault(); const timelineEl = timeline.current!; // 根据滚轮方向确定缩放方向:向上滚放大,向下滚缩小 const factor = event.deltaY < 0 ? ZOOM_STEP : 1 / ZOOM_STEP;
2. 正确计算鼠标的相对位置
把视口的clientX转换成时间轴内容内部的相对坐标,需要考虑容器的视口偏移和自身的页面位置:
// 鼠标相对于时间轴容器左侧的视口坐标 const mouseInViewportX = event.clientX - timelineEl.getBoundingClientRect().left; // 鼠标相对于时间轴内容左侧的绝对坐标(包含已滚动的部分) const mouseInContentX = mouseInViewportX + leftOffset;
3. 修正位移补偿公式
核心逻辑是:缩放后,鼠标指向的内容点应该保持在视口的同一位置。公式推导后得到新的leftOffset计算方式:
setLeftOffset(prev => { // 缩放后,该内容点对应的新绝对位置 const newMouseInContentX = mouseInContentX * factor; // 新的滚动偏移 = 新内容位置 - 鼠标在视口的位置(保证点不动) const newLeft = newMouseInContentX - mouseInViewportX; // 加边界限制,避免滚动超出内容范围 const maxLeft = timelineEl.scrollWidth - timelineEl.clientWidth; return Math.max(0, Math.min(Math.floor(newLeft), maxLeft)); }); setTimeScale(prev => prev * factor); }
完整修正后的handleZoom函数
const ZOOM_STEP = 1.1; function handleZoom(event: WheelEvent) { if (event.deltaY === 0) return; event.preventDefault(); const timelineEl = timeline.current!; const factor = event.deltaY < 0 ? ZOOM_STEP : 1 / ZOOM_STEP; const mouseInViewportX = event.clientX - timelineEl.getBoundingClientRect().left; const mouseInContentX = mouseInViewportX + leftOffset; setLeftOffset(prev => { const newMouseInContentX = mouseInContentX * factor; const newLeft = newMouseInContentX - mouseInViewportX; const maxLeft = timelineEl.scrollWidth - timelineEl.clientWidth; return Math.max(0, Math.min(Math.floor(newLeft), maxLeft)); }); setTimeScale(prev => prev * factor); }
额外优化点
- 可以给
timeScale设置上下限,避免缩放过大或过小导致内容异常 - 在
useEffect中计算positions时,建议给timeline.current做非空判断,避免报错
内容的提问来源于stack exchange,提问作者Samuel E.
相关产品推荐
相关产品推荐

