vue实现弹框拖动
Vue 实现弹框拖动功能
核心思路
通过监听鼠标事件(mousedown、mousemove、mouseup)计算弹框的偏移量,结合 CSS 的 transform 或 position 属性实现拖动效果。
基础实现代码
<template>
<div
class="draggable-dialog"
ref="dialog"
:style="{ left: position.x + 'px', top: position.y + 'px' }"
@mousedown="startDrag"
>
<!-- 弹框内容 -->
<div class="dialog-content">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
data() {
return {
position: { x: 0, y: 0 },
isDragging: false,
startPos: { x: 0, y: 0 }
};
},
methods: {
startDrag(e) {
this.isDragging = true;
this.startPos = {
x: e.clientX - this.position.x,
y: e.clientY - this.position.y
};
document.addEventListener('mousemove', this.onDrag);
document.addEventListener('mouseup', this.stopDrag);
},
onDrag(e) {
if (!this.isDragging) return;
this.position = {
x: e.clientX - this.startPos.x,
y: e.clientY - this.startPos.y
};
},
stopDrag() {
this.isDragging = false;
document.removeEventListener('mousemove', this.onDrag);
document.removeEventListener('mouseup', this.stopDrag);
}
}
};
</script>
<style>
.draggable-dialog {
position: absolute;
cursor: move;
user-select: none;
}
</style>
优化方案
限制拖动范围
在 onDrag 方法中添加边界检测:
onDrag(e) {
if (!this.isDragging) return;
const maxX = window.innerWidth - this.$refs.dialog.offsetWidth;
const maxY = window.innerHeight - this.$refs.dialog.offsetHeight;
this.position = {
x: Math.min(maxX, Math.max(0, e.clientX - this.startPos.x)),
y: Math.min(maxY, Math.max(0, e.clientY - this.startPos.y))
};
}
使用 transform 优化性能 修改样式和计算方式:
.draggable-dialog {
position: fixed;
transform: translate(0, 0);
}
position: {
x: 0,
y: 0
},
// 在 onDrag 中:
this.position = {
x: e.clientX - this.startPos.x,
y: e.clientY - this.startPos.y
};
组件化方案
创建可复用的 Draggable 高阶组件:
// Draggable.js
export default {
props: ['x', 'y'],
data() {
return {
position: { x: this.x || 0, y: this.y || 0 },
// ...其他拖动逻辑
};
},
// ...其他方法
};
使用时:
<Draggable :x="100" :y="100">
<YourDialogComponent />
</Draggable>
注意事项
- 移动端需要额外处理
touchstart/touchmove事件 - 复杂场景建议使用第三方库如:
vuedraggableinteract.js
- 拖动时禁用文本选择:
.draggable-dialog { user-select: none; }







