Tab容器触摸滚动异常问题排查及替代实现方案咨询
问题描述
我有一个包含若干Tab的容器div,在桌面端通过onMouseMove事件实现了拖拽滚动,对应的处理函数如下:
function dragging(e: React.MouseEvent<HTMLDivElement, MouseEvent>) { if (!filterTabsContainer.current) return; filterTabsContainer.current.scrollLeft -= e.movementX; }
但需要在移动端也实现该功能,而触摸事件对象中不存在movementX属性,因此我编写了如下触摸事件处理逻辑:
let previousTouch: number = 0; let movementX: number = 0; let prevScrollLeft: number = 0; function draggingTouch(e: React.TouchEvent<HTMLDivElement>) { if (!filterTabsContainer.current) return; const touch = e.touches[0]; /* 此处保存上次scrollLeft的值,否则再次触摸拖拽时会回到0,prevScrollLeft将在onTouchEnd的处理函数中赋值;*/ if (prevScrollLeft > 0) { filterTabsContainer.current.scrollLeft = prevScrollLeft - movementX; } else { filterTabsContainer.current.scrollLeft -= movementX; } if (previousTouch) { movementX = touch.pageX - previousTouch; } previousTouch = touch.pageX; }
该逻辑首次拖拽时正常,但从上次拖拽后的位置再次拖拽时,仅能滚动约5px后便停止,无法正常工作。请问是否有其他可行的实现方案?
可行实现方案
核心思路是在touchstart阶段记录触摸初始位置和容器当前滚动值,touchmove阶段基于初始值计算偏移量,直接更新滚动位置,避免状态混乱。
完整代码实现
import { useRef, useEffect } from 'react'; // 用ref保存状态,避免组件重渲染影响值的稳定性 const filterTabsContainer = useRef<HTMLDivElement>(null); const touchStartX = useRef<number>(0); const startScrollLeft = useRef<number>(0); // 触摸开始:记录初始基准值 const handleTouchStart = (e: React.TouchEvent<HTMLDivElement>) => { if (!filterTabsContainer.current) return; const touch = e.touches[0]; touchStartX.current = touch.pageX; startScrollLeft.current = filterTabsContainer.current.scrollLeft; }; // 触摸移动:计算偏移并更新滚动 const handleTouchMove = (e: React.TouchEvent<HTMLDivElement>) => { if (!filterTabsContainer.current) return; const touch = e.touches[0]; // 计算触摸移动的横向偏移量 const deltaX = touch.pageX - touchStartX.current; // 和桌面端逻辑对齐:初始滚动位置减去偏移量 filterTabsContainer.current.scrollLeft = startScrollLeft.current - deltaX; }; // 触摸结束:重置状态(非必填,但能避免异常场景的状态残留) const handleTouchEnd = () => { touchStartX.current = 0; startScrollLeft.current = 0; }; // 绑定/解绑事件监听器 useEffect(() => { const container = filterTabsContainer.current; if (!container) return; // 桌面端拖拽逻辑 const mouseMoveHandler = (e: MouseEvent) => { container.scrollLeft -= e.movementX; }; container.addEventListener('mousemove', mouseMoveHandler); container.addEventListener('touchstart', handleTouchStart); container.addEventListener('touchmove', handleTouchMove); container.addEventListener('touchend', handleTouchEnd); return () => { container.removeEventListener('mousemove', mouseMoveHandler); container.removeEventListener('touchstart', handleTouchStart); container.removeEventListener('touchmove', handleTouchMove); container.removeEventListener('touchend', handleTouchEnd); }; }, []);
关键说明
- 每次触摸开始时重新记录基准值,彻底解决二次拖拽时的状态混乱问题
- 移动端逻辑和桌面端行为完全对齐,保证跨端体验一致
- 使用
useRef保存状态,避免组件重渲染导致的变量重置或错误覆盖 - 组件卸载时清理事件监听器,防止内存泄漏
内容的提问来源于stack exchange,提问作者DogZoro28
相关产品推荐
相关产品推荐

