最终效果

vue 组件封装——可自由拖拽移动的盒子_JSON

完整代码

关键性要点,详见注释

组件封装 superBox.vue

<template>
<div
ref="box_Ref"
class="box"
@mousedown="moveStart"
@mousemove="moving"
@mouseup="moveEnd"
:style="{
width: (nodeInfo.width || 40) + 'px',
height: (nodeInfo.height || 60) + 'px',
left: (nodeInfo.x || 0) + 'px',
top: (nodeInfo.y || 0) + 'px',
backgroundColor: nodeInfo.color || 'black',
zIndex: zIndex,
}"
></div>
</template>

<script>
export default {
props: {
nodeInfo: Object,
},
data() {
return {
zIndex: "auto",
// 移动开始时,鼠标的x坐标
moveStart_mouseX: null,
// 移动开始时,鼠标的y坐标
moveStart_mouseY: null,
ifMove: false,
originNodeInfo: null,
};
},

methods: {
// 移动开始
moveStart(e) {
this.moveStart_mouseX = e.clientX;
this.moveStart_mouseY = e.clientY;
this.ifMove = true;
this.originNodeInfo = JSON.parse(JSON.stringify(this.nodeInfo));
// 移动时,临时提升元素至顶层,避免因元素遮挡而无法继续移动
this.zIndex = 99999;
},
// 移动中
moving(e) {
if (this.ifMove) {
let moveing_mouseX = e.clientX;
let moveing_mouseY = e.clientY;
let xChange = moveing_mouseX - this.moveStart_mouseX;
let yChange = moveing_mouseY - this.moveStart_mouseY;
this.$emit("moved", {
newX: this.originNodeInfo.x + xChange,
newY: this.originNodeInfo.y + yChange,
});
}
},
// 移动结束
moveEnd() {
this.ifMove = false;
this.moveStart_mouseX = null;
this.moveStart_mouseY = null;
this.originNodeInfo = null;
this.zIndex = "auto";
},
},
};
</script>

<style scoped>
.box {
position: absolute;
cursor: move;
}
</style>

使用组件 index.vue

<template>
<div>
<SuperBox @moved="moved($event, node1)" :nodeInfo="node1" />
<SuperBox @moved="moved($event, node2)" :nodeInfo="node2" />
</div>
</template>

<script>
import SuperBox from "./superBox.vue";
export default {
components: {
SuperBox,
},
data() {
return {
node1: {
x: 100,
y: 10,
color: "red",
},

node2: {
x: 400,
y: 10,
color: "blue",
},
};
},

methods: {
moved(movedInfo, nodeInfo) {
// < 0 时置为0,避免元素移出浏览器窗口
nodeInfo.x = movedInfo.newX < 0 ? 0 : movedInfo.newX;
nodeInfo.y = movedInfo.newY < 0 ? 0 : movedInfo.newY;
},
},
};
</script>

遗留问题

当鼠标拖拽过快时,元素无法跟随移动,且 ifMove 无法正常恢复 false 导致拖拽异常。

想到解决方案的朋友,欢迎留言哦!