React中如何正确使用touchmove?移动端拖拽滑块失效问题
移动端拖拽滑块touchmove无效问题修复
核心问题分析
- touchmove事件绑定错误:在
handleTouchStart中,你直接调用了handleTouchMove(e, offsetX, cursorXPoss),这会立即执行该函数并将返回值(undefined)绑定给touchmove事件,而非绑定函数本身。 - 拖拽距离计算逻辑错误:当前
handleTouchMove里的offsetX - cursorXPoss是固定值,没有根据触摸移动的实时位置更新滑块偏移量。 - 未清理事件监听:添加的touchmove和touchend事件在touchend后没有移除,可能导致重复绑定或内存泄漏。
修复后的代码示例
import { useState, useEffect, useCallback, useRef } from 'react'; // 状态定义 const [isPressedDown, setIsPressedDown] = useState(false); const [touchMoveDistance, setTouchMoveDistance] = useState(0); const navContainerRef = useRef(null); const navTrackRef = useRef(null); // 存储初始触摸位置和滑块初始偏移量的ref const touchStartData = useRef({ initialCursorPos: 0, initialTrackLeft: 0 }); // 事件处理函数用useCallback包裹,确保引用稳定 const handleTouchStart = useCallback((e) => { setIsPressedDown(true); const rect = e.target.getBoundingClientRect(); const touchX = e.touches[0].clientX - window.pageXOffset; const trackRect = navTrackRef.current.getBoundingClientRect(); // 记录初始触摸位置相对于滑块的偏移,以及滑块当前的left值 touchStartData.current = { initialCursorPos: touchX - trackRect.left, initialTrackLeft: trackRect.left }; // 正确绑定事件:传递函数引用,而非立即执行 navContainerRef.current.addEventListener('touchmove', handleTouchMove); navContainerRef.current.addEventListener('touchend', handleTouchEnd); }, []); const handleTouchMove = useCallback((e) => { e.preventDefault(); if (!isPressedDown) return; const touchX = e.touches[0].clientX - window.pageXOffset; // 根据实时触摸位置计算滑块应该移动的距离 const newTrackLeft = touchX - touchStartData.current.initialCursorPos; setTouchMoveDistance(newTrackLeft - touchStartData.current.initialTrackLeft); }, [isPressedDown]); const handleTouchEnd = useCallback(() => { setIsPressedDown(false); // 移除事件监听,避免重复绑定 navContainerRef.current.removeEventListener('touchmove', handleTouchMove); navContainerRef.current.removeEventListener('touchend', handleTouchEnd); }, []); // 初始化touchstart事件监听 useEffect(() => { const container = navContainerRef.current; container.addEventListener('touchstart', handleTouchStart); // 组件卸载时清理初始事件 return () => { container.removeEventListener('touchstart', handleTouchStart); }; }, [handleTouchStart]); // 滑块渲染部分 <div style={{ left: `${isPressedDown ? touchMoveDistance : mouseHoverEvent}px`, }} className={styles.navTrack} ref={navTrackRef} > {/* 子元素内容保持不变 */} </div>
关键修改点说明
- 事件绑定方式:将
handleTouchMove(e, offsetX, cursorXPoss)改为直接传handleTouchMove,确保事件触发时调用正确的函数。 - 实时位置计算:通过
touchStartData存储初始触摸位置和滑块初始偏移,在touchmove时根据当前触摸位置计算实时偏移量。 - 事件清理:在touchend和组件卸载时移除对应的事件监听,避免内存泄漏。
- useCallback优化:用
useCallback包裹事件处理函数,确保函数引用稳定,避免不必要的重绑定。
内容的提问来源于stack exchange,提问作者Oliver
相关产品推荐
相关产品推荐

