新建dialogDrag.js文件
/*
* v-dialogDrag="{
DragButton: '.window-header', 拖拽的按钮,或者拖拽鼠标的元素,move
DragVindow: '.window', 拖拽的主窗口
DragContainer: '.move-con', 拖拽元素的移动区域,默认为body
custom: true 自定义,true的话默认移动区域(DragContainer)内居中显示
}"
*/
// dialogDrag.js
export default (app) => {
app.directive('dialogDrag', (el, binding) => {
// 没有规定默认移动窗口的时候,默认使用body作为窗口
let bodyEl = document.body
if (binding.value.DragContainer) {
bodyEl = el.closest(binding.value.DragContainer) || document.body
}
// 移动区域可视宽度
const screenWidth = bodyEl.clientWidth
// 移动区域可视高度
const screenHeight = bodyEl.clientHeight
// 移动区域距离可视区域左侧的距离
const containerLeft = bodyEl.getBoundingClientRect().left
// 移动区域距离可视区域顶部的距离
const containerTop = bodyEl.getBoundingClientRect().top
// 拖拽窗口 DragVindow
let DragVindow=el.querySelector(binding.value.DragVindow)
// 以下两个判断是为了找到元素,也可以不需要
if (!DragVindow) {
DragVindow = el.closest(binding.value.DragVindow)
}
if(!DragVindow){
DragVindow = document.querySelector(binding.value.DragVindow)
}
const dragDomWidth = DragVindow.offsetWidth // 对话框宽度
const dragDomheight = DragVindow.offsetHeight // 对x话框高度
// 拖拽按钮
let DragButton=el.querySelector(binding.value.DragButton)
if (!DragButton) {
DragButton = el.closest(binding.value.DragButton)
}
if (!DragButton) {
DragButton = document.querySelector(binding.value.DragButton)
}
DragButton.style.cssText += ';cursor:move;'
// 如果是自定义组件 设置窗口默认居中
if (binding.value.custom) {
const [left, top] = [
(screenWidth - dragDomWidth) / 2 + containerLeft,
(screenHeight - dragDomheight) / 2 + containerTop
]
DragVindow.style.cssText += `;left:${left}px;top:${top}px;`
}
// 获取元素的left或者top属性
const sty = (function () {
if (window.document.currentStyle) {
return (dom, attr) => dom.currentStyle[attr]
} else {
return (dom, attr) => getComputedStyle(dom, false)[attr]
}
})()
// 按下鼠标处理事件
DragButton.onmousedown = (e) => {
// 鼠标按下,计算当前元素距离可视区的距离 也就是鼠标的按下时候的起点位置
const disX = e.pageX
const disY = e.pageY
// 元素left的最大值、最小值 最小值等于移动区域距离页面的距离 最大值等于移动区域的宽度减去移动元素的宽度加上移动区域距离页面的距离
const minDragDomLeft = containerLeft
const maxDragDomLeft = screenWidth - dragDomWidth + containerLeft
const minDragDomTop = containerTop
const maxDragDomTop = screenHeight - dragDomheight + containerTop
// 记录移动前的初始left 只尝试了px,未尝试%
let styL = sty(DragVindow, 'left')
let styT = sty(DragVindow, 'top')
if (styL.includes('%')) {
styL = +bodyEl.clientWidth * (+styL.replace(/%/g, '') / 100)
styT = +bodyEl.clientHeight * (+styT.replace(/%/g, '') / 100)
} else {
styL = +styL.replace(/px/g, '')
styT = +styT.replace(/px/g, '')
}
document.onmousemove = (e) => {
// 通过事件委托,计算鼠标移动的时候,移动元素的left值
// e.pageX - disX未鼠标移动的距离 鼠标移动的距离加上初始left位置为移动后的left距离
let left = e.pageX - disX + styL
let top = e.pageY - disY + styT
// 边界处理 left必须位于最小值和最大值之内
if (left < minDragDomLeft) {
left = minDragDomLeft
} else if (left > maxDragDomLeft) {
left = maxDragDomLeft
}
if (top < minDragDomTop) {
top = minDragDomTop
} else if (top > maxDragDomTop) {
top = maxDragDomTop
}
// 设置当前元素
DragVindow.style.cssText += `;left:${left}px;top:${top}px;`
}
document.onmouseup = () => {
document.onmousemove = null
document.onmouseup = null
}
}
})
}
在main.js中引用
// main.js
import { createApp } from 'vue'
import dialogDrag from '@/directive/dialogDrag.js'
import App from './App.vue'
const app = createApp(App)
app.use(dialogDrag)
组件中使用
<template>
<div class="ss move-con">
<div @click="showComponentModal = true">打开拖拽组件</div>
<!-- first是一个可滚动的容器 -->
<div class="first">
<div class="second"></div>
</div>
<div
class="custom"
v-if="showComponentModal"
v-dialogDrag="{
DragButton: '.window-header',
DragVindow: '.window',
DragContainer: '.move-con',
custom: true
}"
>
<div class="window">
<div class="window-header">
按住拖拽 标题
<div class="close" @click.stop="showComponentModal = false">关闭</div>
</div>
<div class="window-body">窗口默认</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const showComponentModal = ref(false)
onMounted(() => {})
</script>
<style lang="less" scoped>
.first {
overflow: auto;
width: 100%;
height: 300px;
background: #fff;
}
.ss {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
}
.second {
width: 2000px;
height: 200px;
background: #eee;
}
.window {
width: 400px;
height: 300px;
border: 1px solid #cccccc;
/* 此处采用的是固定定位,和原作者有差别 */
position: fixed;
z-index: 99999;
background: #e1eafc;
.window-header {
position: relative;
height: 50px;
line-height: 50px;
text-align: center;
background: #beceeb;
user-select: none;
.close {
position: absolute;
right: 20px;
top: 10px;
cursor: pointer;
}
}
}
</style>
在move-con区域内进行拖拽
dragDialog_1
在body区域内进行拖拽
dragDialog_2