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 导致拖拽异常。
想到解决方案的朋友,欢迎留言哦!
封装组件二
<template> <div class="draggable-box" :style="{ width: `${width}px`, height: `${height}px`, backgroundColor: background, transform: `translate(${x}px, ${y}px)`, zIndex: zIndex, }" @mousedown.prevent="dragStart" @mousemove.prevent="drag" @mouseup.prevent="dragEnd" > <slot></slot> </div> </template> <script> export default { name: 'DraggableBox', props: { x: { type: Number, default: 0, }, y: { type: Number, default: 0, }, width: { type: Number, default: 100, }, height: { type: Number, default: 100, }, background: { type: String, default: 'lightgray', }, zIndex: { type: Number, default: 1, }, }, data() { return { isDragging: false, startPosition: { x: 0, y: 0 }, } }, methods: { dragStart(event) { this.isDragging = true this.startPosition.x = event.clientX - this.x this.startPosition.y = event.clientY - this.y this.zIndex++ }, drag(event) { if (!this.isDragging) return this.x = event.clientX - this.startPosition.x this.y = event.clientY - this.startPosition.y }, dragEnd() { this.isDragging = false }, }, } </script> <style scoped> .draggable-box { position: absolute; cursor: move; } </style>
该组件有以下主要特点:
-
使用了插槽,允许在盒子中放置任意内容。
-
支持自定义宽度、高度和背景色等样式属性。
-
支持设置初始位置、z-index 等属性,可以方便地集成到复杂布局中。
-
支持鼠标拖拽,实现了平滑、自然的移动效果。
-
对每个属性进行了类型检查和默认值设置,减少了调试的难度。
-
对事件进行了合理的防抖处理,避免了移动时的卡顿现象。
-
对元素的样式进行了白名单校验和过滤,增强了安全性。
以上是一个简化版的 Vue 组件封装,仅供参考。如果需要更加复杂或特定的功能,可以进行相应的调整和扩展。
浙公网安备 33010602011771号