实现效果:
MyDrag.vue
<template>
<div class="box" id="drag" @mousedown="startDrag" ref="drag">
<div v-for="(handle, index) in resizeHandles" :key="index" :class="['resize-handle', handle]"
@mousedown="startResize"></div>
<slot></slot>
</div>
</template>
<script>
import { throttle } from 'lodash';
export default {
name: 'MyDrag',
data() {
return {
startX: 0,
startY: 0,
startLeft: 0,
startTop: 0,
resizeHandles: [
'top-left',
'top',
'top-right',
'right',
'bottom-right',
'bottom',
'bottom-left',
'left'
]
}
},
computed: {
parentRect() {
return this.$refs.drag.parentElement.getBoundingClientRect();
}
},
methods: {
/**
* 开始拖拽事件处理函数
* @param {MouseEvent} event - 鼠标事件对象
*/
startDrag(event) {
// 获取拖拽元素
const dragElement = this.$refs.drag;
// 检查 dragElement 是否存在
if (!dragElement) return;
// 阻止默认事件行为
event.preventDefault();
// 获取当前操作的句柄
const currentHandle = event.target;
// 检查是否为缩放句柄
const isResizeHandle = currentHandle.classList.contains("resize-handle");
// 如果是缩放句柄,则不执行拖拽操作
if (isResizeHandle) return;
// 记录初始鼠标位置和元素位置
this.startX = event.clientX;
this.startY = event.clientY;
this.startLeft = dragElement.offsetLeft;
this.startTop = dragElement.offsetTop;
// 定义拖拽事件处理函数
const handleDrag = throttle((event) => {
// 计算鼠标移动的距离
const dx = event.clientX - this.startX;
const dy = event.clientY - this.startY;
// 计算新的元素位置
const newLeft = this.startLeft + dx;
const newTop = this.startTop + dy;
// 边界条件处理
// 检查 parentRect 是否存在
if (!this.parentRect) return;
// 计算最大边界位置
const maxLeft = this.parentRect.width - dragElement.offsetWidth;
const maxTop = this.parentRect.height - dragElement.offsetHeight;
// 限制元素位置在边界内
const boundedLeft = Math.max(0, Math.min(newLeft, maxLeft));
const boundedTop = Math.max(0, Math.min(newTop, maxTop));
// 更新元素位置
requestAnimationFrame(() => {
dragElement.style.left = `${boundedLeft}px`;
dragElement.style.top = `${boundedTop}px`;
});
}, 16); // 限制每秒最多调用 60 次
// 定义移除事件监听器的函数
const removeEventListeners = () => {
document.removeEventListener("mousemove", handleDrag);
document.removeEventListener("mouseup", stopDrag);
};
// 定义停止拖拽事件处理函数
const stopDrag = () => {
removeEventListeners();
};
// 添加拖拽和释放事件监听器
document.addEventListener("mousemove", handleDrag);
document.addEventListener("mouseup", stopDrag);
// 组件卸载时移除事件监听器
this.$once('hook:beforeDestroy', removeEventListeners);
},
/**
* 开始调整元素大小的方法
* @param {MouseEvent} event - 鼠标事件对象
*/
startResize(event) {
// 获取需要拖动的元素
const dragElement = this.$refs.drag;
// 如果drag元素不存在,则直接返回
if (!dragElement) return; // 增加异常处理
// 阻止默认事件行为
event.preventDefault();
// 获取当前操作的句柄元素
const currentHandle = event.target;
// 获取拖动方向
const direction = currentHandle.className.split(" ")[1];
// 记录鼠标起始位置
const startX = event.clientX;
const startY = event.clientY;
// 记录元素初始宽度和高度
const startWidth = parseInt(dragElement.offsetWidth); // 初始转换为整数
const startHeight = parseInt(dragElement.offsetHeight); // 初始转换为整数
// 记录元素初始左上角位置
const startLeft = dragElement.offsetLeft;
const startTop = dragElement.offsetTop;
/**
* 调整元素大小的函数
* @param {MouseEvent} event - 鼠标事件对象
*/
const resize = (event) => {
// 使用 requestAnimationFrame 优化性能
requestAnimationFrame(() => {
// 计算鼠标移动的距离
const dx = event.clientX - startX;
const dy = event.clientY - startY;
// 初始化元素的新尺寸和位置
let width = startWidth;
let height = startHeight;
let left = startLeft;
let top = startTop;
// 根据拖动方向调整元素的尺寸和位置
if (direction.includes("left")) {
width = startWidth - dx;
left = startLeft + dx;
// 确保元素不超过父容器的宽度
if (this.parentRect.width - left > this.parentRect.width) return;
}
if (direction.includes("right")) {
width = startWidth + dx;
// 确保元素不超过父容器的宽度
if (startLeft + width > this.parentRect.width) return;
}
if (direction.includes("top")) {
height = startHeight - dy;
top = startTop + dy;
// 确保元素不超过父容器的高度
if (this.parentRect.height - top > this.parentRect.height) return;
}
if (direction.includes("bottom")) {
height = startHeight + dy;
// 确保元素不超过父容器的高度
if (startTop + height > this.parentRect.height) return;
}
// 如果计算出的宽度或高度小于等于0,则不进行调整
if (width <= 0 || height <= 0) return;
// 更新元素的尺寸和位置
dragElement.style.width = width + 'px';
dragElement.style.height = height + 'px';
dragElement.style.left = left + 'px';
dragElement.style.top = top + 'px';
});
};
// 停止调整元素大小的方法
const stopResize = () => {
// 移除事件监听器
document.removeEventListener("mousemove", resize);
document.removeEventListener("mouseup", stopResize);
};
// 添加事件监听器
document.addEventListener("mousemove", resize);
document.addEventListener("mouseup", stopResize);
// 确保在组件销毁时移除事件监听器
this.$once('hook:beforeDestroy', () => {
document.removeEventListener("mousemove", resize);
document.removeEventListener("mouseup", stopResize);
});
}
}
}
</script>
<style scoped>
.box {
position: absolute;
left: 50px;
top: 50px;
width: 100px;
height: 20px;
background-color: #f0f0f0;
cursor: move;
}
.resize-handle {
position: absolute;
width: 5px;
height: 5px;
background-color: #000;
cursor: pointer;
}
.top-left {
top: -5px;
left: -5px;
cursor: nw-resize;
}
.top {
top: -5px;
left: calc(50% - 5px);
cursor: ns-resize;
}
.top-right {
top: -5px;
right: -5px;
cursor: ne-resize;
}
.right {
top: calc(50% - 5px);
right: -5px;
cursor: ew-resize;
}
.bottom-right {
bottom: -5px;
right: -5px;
cursor: se-resize;
}
.bottom {
bottom: -5px;
left: calc(50% - 5px);
cursor: ns-resize;
}
.bottom-left {
bottom: -5px;
left: -5px;
cursor: sw-resize;
}
.left {
top: calc(50% - 5px);
left: -5px;
cursor: ew-resize;
}
</style>
使用组件:
<template>
<div id="app">
<div class="container">
<MyDrag >
<div>你好</div>
</MyDrag>
</div>
</div>
</template>
<script>
import MyDrag from './components/MyDrag.vue'
export default {
name: 'App',
components: {
MyDrag,
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
/* text-align: center; */
color: #2c3e50;
}
.container {
width: 500px;
height: 500px;
border: 1px solid blue;
position: relative;
margin: 0 auto;
}
</style>
标签:vue,const,dragElement,top,event,内容,5px,拖拽,left
From: https://blog.csdn.net/m0_53956340/article/details/143980275