Vue 3中移除拖放成功时的ghost元素默认回弹动画
解决Vue原生拖放成功后Ghost元素回弹问题
核心原因
浏览器原生拖放的ghost元素回弹是默认行为,当拖拽结束后如果没有通过Vue响应式数据更新驱动DOM结构变化,浏览器会默认将ghost元素放回原位置。
具体解决方案
1. 完善拖放事件流程,阻止默认行为
通过规范拖放事件逻辑,结合Vue响应式数据迁移,从根源上避免回弹:
步骤1:补充模板中的drop事件监听
给列容器添加@drop事件监听:
<div @dragover="dragOverHandler($event, col)" @drop="dropHandler($event, col)" id="board-cols" :ref="col" class="whitespace-nowrap w-72 mx-2 px-2" :class="toStatus === col ? 'bg-gray-200' : ''" v-for="(col, i) in columns" :key="i" >
步骤2:更新拖拽事件处理逻辑
在Vue组件的methods中完善以下方法:
data() { return { draggedItem: null, sourceCol: null, toStatus: null } }, methods: { dragStartHandler(event, item, col) { // 记录拖拽元素和源列 this.draggedItem = item; this.sourceCol = col; // 设置拖拽行为为移动 event.dataTransfer.effectAllowed = 'move'; }, dragOverHandler(event, col) { // 必须调用preventDefault才能触发drop事件 event.preventDefault(); this.toStatus = col; }, dropHandler(event, targetCol) { // 阻止浏览器默认拖放行为(包括ghost回弹) event.preventDefault(); event.stopPropagation(); if (!this.draggedItem || this.sourceCol === targetCol) return; // 迁移元素:从源列移除,添加到目标列(Vue响应式自动更新DOM) const sourceList = this.collections[this.sourceCol]; const targetList = this.collections[targetCol]; const itemIndex = sourceList.findIndex(item => item === this.draggedItem); if (itemIndex !== -1) { sourceList.splice(itemIndex, 1); targetList.push(this.draggedItem); } this.toStatus = null; }, dragEndHandler() { // 重置拖拽状态 this.draggedItem = null; this.sourceCol = null; } }
2. 隐藏Ghost元素(兜底方案)
如果仍有回弹视觉残留,可在dragStartHandler中设置透明拖拽镜像,让用户看不到ghost元素:
dragStartHandler(event, item, col) { this.draggedItem = item; this.sourceCol = col; event.dataTransfer.effectAllowed = 'move'; // 创建透明图片作为拖拽镜像 const emptyImg = new Image(); emptyImg.src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='; event.dataTransfer.setDragImage(emptyImg, 0, 0); }
3. 临时隐藏原拖拽元素(优化视觉体验)
拖拽过程中给原元素添加透明类,避免ghost与原元素重叠或回弹时的视觉冲突:
修改拖拽元素模板:
<div :draggable="true" @dragstart="dragStartHandler($event, item, col)" @dragend="dragEndHandler()" v-for="(item, j) in collections[col]" id="data" :key="j" class="flex flex-col my-2 rounded-xl overflow-hidden select-none cursor-move transition-opacity" :class="draggedItem === item ? 'opacity-0' : ''" > <EventPreviewCard :event="item" /> </div>
内容的提问来源于stack exchange,提问作者Bob Bass
相关产品推荐
相关产品推荐

