拖拽看板时元素位置错乱怎么办?

Designer°怡企 阅读 3

我在用 Vue 做一个看板拖拽功能,用的是原生 drag & drop API,但每次拖完卡片,它都会跑到奇怪的位置,有时候还重叠。试过用 getBoundingClientRect 和 offsetTop 都不太准。

下面是我简化后的代码,就一个可拖拽的卡片:

<template>
  <div 
    draggable="true"
    @dragstart="onDragStart"
    @dragover.prevent
    @drop="onDrop"
    class="card">
    拖我试试
  </div>
</template>
我来解答 赞 1 收藏
二维码
手机扫码查看
1 条解答
博主治柯
原生drag&drop确实容易遇到这种坑,特别是元素位置乱飞的问题。我来帮你分析下原因和解决方案。

原理是这样:原生拖拽API默认会修改元素的实际DOM位置,但我们通常需要的是视觉上的位置变化而不是DOM结构变化。解决方法就是用绝对定位+transform来移动元素,而不是依赖原生API的位置计算。

给你一个改进后的代码示例,关键点我都加了注释:

export default {
data() {
return {
posX: 0, // 记录当前位置X
posY: 0, // 记录当前位置Y
dragOffset: {x:0, y:0} // 记录拖拽时的鼠标偏移
}
},
methods: {
onDragStart(e) {
// 计算鼠标相对于卡片左上角的偏移
this.dragOffset = {
x: e.clientX - this.posX,
y: e.clientY - this.posY
}

// 必须设置这个才能让拖拽生效
e.dataTransfer.setData('text/plain', null)
},
onDrop(e) {
// 计算新位置时要减去之前的偏移量
this.posX = e.clientX - this.dragOffset.x
this.posY = e.clientY - this.dragOffset.y

// 更新元素位置
this.updatePosition()
},
updatePosition() {
const card = this.$el
card.style.transform = translate(${this.posX}px, ${this.posY}px)
}
}
}


对应的CSS要这样写:
.card {
position: absolute; /* 关键 */
cursor: grab;
touch-action: none; /* 防止移动端触摸冲突 */
user-select: none; /* 防止文字被选中 */
}


几个关键点解释:
1. 用transform代替top/left移动元素,性能更好且不会影响文档流
2. 记录鼠标偏移量是为了拖拽时元素不会突然跳到鼠标位置
3. position:absolute让元素脱离文档流,避免影响其他元素
4. 记得阻止dragover的默认行为,不然drop事件不会触发

如果要做成看板多卡片拖拽,原理类似,只要给每个卡片维护自己的位置数据就行。我之前做这个功能时也踩过坑,原生API确实有点反直觉,建议后面可以试试SortableJS这类库会省事很多。
点赞
2026-03-05 15:00