ReactJS无第三方包实现图片拖拽排序:移动端Touch事件索引异常问题
移动端图片拖拽排序:Touch事件中handleTouchMove索引异常问题
我正在为移动端实现图片拖拽排序功能,未使用任何第三方包,当前采用Touch事件实现,代码如下:
const [draggedImage, setDraggedImage] = useState(null); const handleTouchStart = (index) => { setDraggedImage(index); }; const handleTouchMove = (e, index) => { e.preventDefault(); if (draggedImage === null || draggedImage === index) { return; } const updatedImages = [...images]; const [movedImage] = updatedImages.splice(draggedImage, 1); updatedImages.splice(index, 0, movedImage); setImages(updatedImages); setDraggedImage(index) }; const handleTouchEnd = (e) => { setDraggedImage(null); };
目前遇到问题:handleTouchMove的index未返回预期结果,它本应返回图片被放置位置的索引。请问是否有更合适的事件来解决该问题?
问题根源与解决方案
你的核心问题在于当前handleTouchMove绑定的是单个图片元素的触摸事件,所以传入的index始终是该元素自身的索引,而非触摸点下方目标元素的索引。原生Touch事件完全够用,不需要更换,只需调整逻辑来获取正确的目标索引。
调整思路:
- 通过触摸坐标定位目标元素:在
handleTouchMove中,利用触摸事件的clientX/clientY坐标,结合document.elementFromPoint()找到当前触摸点下的图片元素,再从元素属性中提取对应的索引。 - 延迟数组修改时机:避免在
handleTouchMove中频繁更新数组(会导致状态频繁刷新,反而打乱索引逻辑),建议在触摸移动时仅记录目标索引,等到handleTouchEnd时再执行数组重排操作。
修改后的示例代码:
const [draggedImage, setDraggedImage] = useState(null); const [targetIndex, setTargetIndex] = useState(null); const handleTouchStart = (index) => { setDraggedImage(index); }; const handleTouchMove = (e) => { e.preventDefault(); if (draggedImage === null) return; // 获取当前触摸点的坐标 const touch = e.touches[0]; // 找到触摸点下方的DOM元素 const targetElement = document.elementFromPoint(touch.clientX, touch.clientY); // 校验是否为目标图片元素(假设图片有统一类名"draggable-image") if (targetElement?.classList.contains('draggable-image')) { // 从自定义属性中读取索引(渲染时需给图片添加data-index) const index = parseInt(targetElement.dataset.index, 10); if (index !== draggedImage && index !== targetIndex) { setTargetIndex(index); } } }; const handleTouchEnd = () => { if (draggedImage === null || targetIndex === null) { setDraggedImage(null); setTargetIndex(null); return; } // 执行图片数组的重排 const updatedImages = [...images]; const [movedImage] = updatedImages.splice(draggedImage, 1); updatedImages.splice(targetIndex, 0, movedImage); setImages(updatedImages); // 重置拖拽状态 setDraggedImage(null); setTargetIndex(null); };
配套渲染逻辑:
渲染图片列表时,给每个图片元素添加data-index属性和统一类名:
{images.map((img, index) => ( <img key={img.id} src={img.url} className="draggable-image" data-index={index} onTouchStart={() => handleTouchStart(index)} onTouchMove={handleTouchMove} onTouchEnd={handleTouchEnd} /> ))}
内容的提问来源于stack exchange,提问作者xavior paul
相关产品推荐
相关产品推荐

