这个拖拽指令需要用到被拖拽的元素上,然后父元素就不需要写定位属性了,因为在指令里面会给父元素添加定位属性的
先写好指令文件
// 第一种
const vDrag = {
mounted(el, binding) {
// 设置目标元素基础属性
el.style.cursor = 'move';
el.style.position = 'fixed';
// 获取容器宽高
let parentNode = el.parentNode || null;
let containerWidth = window.innerWidth - getScrollWidth();
let containerHeight = window.innerHeight;
// 存在父级容器
if (parentNode) {
parentNode.style.position = "relative"
containerWidth = parentNode.offsetWidth;
containerHeight = parentNode.offsetHeight;
// 防止选中移动块上的文字等
parentNode.onmouseover = () => {
document.onselectstart = () => {
return false;
};
};
parentNode.onmouseout = () => {
document.onselectstart = () => {
return true;
};
};
el.style.position = 'absolute';
}
// 鼠标在目标元素上按下
el.addEventListener('mousedown', (e) => {
let { width, height } = el.getBoundingClientRect();
// 当前目标元素的left与top
const left = el.offsetLeft;
const top = el.offsetTop;
// 保存按下的鼠标的X与Y
const mouseX = e.clientX;
const mouseY = e.clientY;
// 计算边界值
const leftLimit = left;
const rightLimit = containerWidth - left - width;
const topLimit = top;
const bottomLimit = containerHeight - top - height;
// 监听鼠标移动
document.onmousemove = (e) => {
// 鼠标移动的距离
let disX = e.clientX - mouseX;
let disY = e.clientY - mouseY;
// 左右边界
if (disX < 0 && disX <= -leftLimit) {
el.style.left = (left - leftLimit) + 'px';
} else if (disX > 0 && disX >= rightLimit) {
el.style.left = (left + rightLimit) + 'px';
} else {
el.style.left = (left + disX) + 'px';
}
// 上下边界
if (disY < 0 && disY <= -topLimit) {
el.style.top = (top - topLimit) + 'px';
} else if (disY > 0 && disY >= bottomLimit) {
el.style.top = (top + bottomLimit) + 'px';
} else {
el.style.top = (top + disY) + 'px';
}
return false;
}
// 监听鼠标抬起
document.onmouseup = () => {
document.onmousemove = null;
document.onmouseup = null;
}
});
// 获取元素的相关CSS
function getStyle(el, attr) {
return el.currentStyle ? el.currentStyle[attr] : window.getComputedStyle(el, false)[attr];
}
// 返回滚动条的宽度, 没有则返回0
function getScrollWidth() {
let noScroll, scroll, oDiv = document.createElement("DIV");
oDiv.style.cssText = "position:absolute; top:-1000px; width:100px; height:100px; overflow:hidden;";
noScroll = document.body.appendChild(oDiv).clientWidth;
oDiv.style.overflowY = "scroll";
scroll = oDiv.clientWidth;
document.body.removeChild(oDiv);
let isExsit = document.body.scrollHeight > (window.innerHeight || document.documentElement.clientHeight);
return isExsit ? noScroll - scroll : 0
}
}
}
// 第二种
const vDragTwo = {
mounted(el, binding, vnode, oldVnode) {
let odiv = el; //获取当前元素
// 获取可移动元素的父节点
let parentNode = el.parentNode;
// 设置父节点定位
parentNode.style.position = "relative";
el.style.position = "absolute";
// 设置鼠标hover效果:移动上前去显示可移动的提示效果,并且禁用页面可选择,离开恢复正常
el.onmouseover = () => {
el.style.cursor = "move";
};
el.onmouseout = () => {
el.style.cursor = "none";
};
// 防止选中移动块上的文字等
parentNode.onmouseover = () => {
document.onselectstart = () => {
return false;
};
};
parentNode.onmouseout = () => {
document.onselectstart = () => {
return true;
};
};
el.onmousedown = (event) => {
//event的兼容,同时得到clientX,的值
var event = event || window.event;
let x = event.clientX - el.offsetLeft;
let y = event.clientY - el.offsetTop; //得到小段的偏移
// 将移动事件绑定到 document 上,防止拖动过快脱离开
document.onmousemove = (event) => {
let xx = event.clientX - x; //当移动的时候,用它的鼠标值减去偏移量
let yy = event.clientY - y;
if (xx <= 0) {
//判定边界值 0,就在最边上了,
xx = 0;
}
if (xx >= parentNode.offsetWidth - el.offsetWidth) {
//大于整个盒子的宽度-小盒子的宽度
xx = parentNode.offsetWidth - el.offsetWidth;
}
if (yy <= 0) {
yy = 0;
}
if (yy >= parentNode.offsetHeight - el.offsetHeight) {
yy = parentNode.offsetHeight - el.offsetHeight;
}
el.style.left = xx + "px";
el.style.top = yy + "px";
};
el.onmouseup = () => {
// 取消事件
document.onmousemove = null;
el.onmouseup = null;
};
}
}
}
export default {
install: (app) => {
app.directive('drag', vDrag)
app.directive('dragtwo', vDragTwo)
}
}
main.js引入这个指令文件
import { createApp } from 'vue'
import App from './App.vue'
import DRAG from './utile/ZhiLing/Drag' //这个就是指令文件
const app = createApp(App)
app.use(DRAG)
app.mount('#app')
到此已经完成了全局的注册指令了,接下来是使用
<template>
<div class="homes">
<div class="box_div" id="box_div">
<div class="box" v-drag>
<div>我是全局</div>
<div>v-drag</div>
</div>
<div class="box2" v-dragarr>
<div>我是局部</div>
<div>v-dragarr</div>
</div>
</div>
666
<div style="width: 100px;height:100px;background-color: cornflowerblue;" v-drag>
<div>我是全局</div>
<div>v-drag</div>
</div>
<div v-for="item in 10" :key="item">{{ item }}</div>
<div style="width: 100px;height:100px;background-color: cornflowerblue;" v-dragtwo>
<div>我是全局</div>
<div>v-dragtwo</div>
</div>
<div v-for="item in 10" :key="item">{{ item }}</div>
<div style="width: 90%;height: 300px;border:1px solid #36d">
<div style="width: 100px;height: 80px;background-color: #fff;" v-drag>
<div>我是全局</div>
<div>v-drag</div>
</div>
</div>
<div v-for="item in 10" :key="item">{{ item }}</div>
</div>
</template>
<script setup>
import { ref, onMounted, nextTick, defineComponent, createVNode } from "vue";
const orignCanvas = ref([])
const pdfUrl = ref(null)
const signUrl = ref(null)
//局部自定义指令
const vDragarr = {
mounted(el, binding, vnode, oldVnode) {
let odiv = el; //获取当前元素
console.log('aa', odiv);
console.log('bb', binding);
console.log('cc', vnode);
console.log('dd', oldVnode);
// 获取可移动元素的父节点
let parentNode = el.parentNode;
// 设置父节点定位
// parentNode.style.position = "relative";
el.style.position = "absolute";
// 设置鼠标hover效果:移动上前去显示可移动的提示效果,并且禁用页面可选择,离开恢复正常
el.onmouseover = () => {
el.style.cursor = "move";
};
el.onmouseout = () => {
el.style.cursor = "none";
};
// 防止选中移动块上的文字等
parentNode.onmouseover = () => {
document.onselectstart = () => {
return false;
};
};
parentNode.onmouseout = () => {
document.onselectstart = () => {
return true;
};
};
el.onmousedown = (event) => {
// console.log('parentNode', parentNode);
// console.log('aa', odiv);
// console.log('bb', binding);
// console.log('cc', vnode);
// console.log('dd', oldVnode);
//event的兼容,同时得到clientX,的值
var event = event || window.event;
let x = event.clientX - el.offsetLeft;
let y = event.clientY - el.offsetTop; //得到小段的偏移
// 将移动事件绑定到 document 上,防止拖动过快脱离开
document.onmousemove = (event) => {
let xx = event.clientX - x; //当移动的时候,用它的鼠标值减去偏移量
let yy = event.clientY - y;
if (xx <= 0) {
//判定边界值 0,就在最边上了,
xx = 0;
}
if (xx >= parentNode.offsetWidth - el.offsetWidth) {
//大于整个盒子的宽度-小盒子的宽度
xx = parentNode.offsetWidth - el.offsetWidth;
}
if (yy <= 0) {
yy = 0;
}
if (yy >= parentNode.offsetHeight - el.offsetHeight) {
yy = parentNode.offsetHeight - el.offsetHeight;
}
el.style.left = xx + "px";
el.style.top = yy + "px";
};
el.onmouseup = () => {
// 取消事件
document.onmousemove = null;
el.onmouseup = null;
};
}
},
}
</script>
<style lang="less">
.homes {
width: 100%;
height: 100%;
overflow: auto;
.box_div {
width: 50%;
height: 500px;
margin-left: 100px;
background-color: #84d496;
position: relative;
.box {
width: 100px;
height: 100px;
background-color: coral;
}
.box2 {
width: 100px;
height: 100px;
top: 150px;
left: 0px;
background-color: rgb(80, 83, 255);
}
}
}
</style>