基于React State调整拖放功能的Ghost Image显示问题
解决拖拽时Ghost Image显示错误的问题
你当前的问题根源在于:在dragStart事件触发时立刻从数组中移除了拖拽元素,导致DOM瞬间重新渲染。浏览器生成的ghost image是基于拖拽开始瞬间的DOM状态,但此时元素已经被移除,后续元素移位,就出现了ghost image显示成下一个元素的情况。
正确的做法是不要在拖拽开始时修改数组,而是通过状态标记拖拽中的元素,仅在UI上隐藏它,等拖拽完成后再重新排序数组。这样既能让页面上的拖拽元素消失,又能保证浏览器生成正确的ghost image。
修改后的完整代码
import styles from '../../styles/test.module.css' import { useState } from 'react' export default function TestDragDrop() { const [tiles, setTiles] = useState([ { APPID: 1, APPLICATIONNAME: 'App 1' }, { APPID: 2, APPLICATIONNAME: 'App 2' }, { APPID: 3, APPLICATIONNAME: 'App 3' }, { APPID: 4, APPLICATIONNAME: 'App 4' }, { APPID: 5, APPLICATIONNAME: 'App 5' }, { APPID: 6, APPLICATIONNAME: 'App 6' }, { APPID: 7, APPLICATIONNAME: 'App 7' }, { APPID: 8, APPLICATIONNAME: 'App 8' }, { APPID: 9, APPLICATIONNAME: 'App 9' }, ]) const [appBeingDragged, setAppBeingDragged] = useState(null) const [startIndex, setStartIndex] = useState(null) const [draggingId, setDraggingId] = useState(null) const [endIndex, setEndIndex] = useState(null) const handleDragStart = (e, index) => { e.dataTransfer.setData('id', e.target.id) const thisAppId = parseInt(e.target.id) const draggedApp = tiles.find(tile => tile.APPID === thisAppId) setAppBeingDragged(draggedApp) setStartIndex(index) setDraggingId(thisAppId) } const handleDragEnter = (i) => { setEndIndex(i) } const handleDragLeave = () => { setEndIndex(null) } const handleDrop = (e) => { e.preventDefault() reorderTiles(startIndex, endIndex) setDraggingId(null) } const reorderTiles = (startIndex, endIndex) => { if (endIndex === null) return const newTiles = [...tiles] const [draggedItem] = newTiles.splice(startIndex, 1) newTiles.splice(endIndex, 0, draggedItem) setTiles(newTiles) setAppBeingDragged(null) setStartIndex(null) setEndIndex(null) } const cards = tiles.map((app, index) => { return ( <div key={app.APPID} id={app.APPID} className={`${styles.card} ${draggingId === app.APPID ? styles.dragging : ''}`} draggable onDragStart={(e) => handleDragStart(e, index)} onDragOver={(e) => e.preventDefault()} onDragEnter={() => handleDragEnter(index)} onDragLeave={handleDragLeave} onDrop={(e) => handleDrop(e)} > {app.APPLICATIONNAME} </div> ) }) return ( <div style={{ display: 'flex', flexFlow: 'row wrap', margin: '1rem 5rem' }}> {cards} </div> ) }
配套CSS修改(test.module.css)
在样式文件中添加拖拽元素的隐藏样式:
.dragging { opacity: 0; pointer-events: none; }
关键改动说明
- 用
APPID作为元素key:避免使用索引作为key,防止拖拽时DOM元素识别混乱 - 新增
draggingId状态:仅在UI层面隐藏拖拽元素(设为透明),不修改原数组,保证浏览器能基于原始DOM生成正确的ghost image - 拖拽完成后再排序数组:等drop事件触发后,再对数组进行剪切插入操作,此时DOM更新不会影响已经生成的ghost image
- 添加
pointer-events: none:防止隐藏的拖拽元素干扰后续的拖拽事件触发
内容的提问来源于stack exchange,提问作者Stephenk
相关产品推荐
相关产品推荐

