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 组件封装,仅供参考。如果需要更加复杂或特定的功能,可以进行相应的调整和扩展。