前言
代码来自使用 vue3 的自定义指令给 element-plus 的 el-dialog 增加拖拽功能,用的时候发现一些问题,我进行了二次修改。
变化
- 限制边缘
- 全屏效果
- 限制在弹窗的
header
拖动 - 增加用户体验🤣
效果
使用
- 新建一个 js 文件,内容为如下代码
- 在
main.js
中引入并use
// ...... import dialogDrag from './assets/js/dialogDrag.js' var app = createApp(App) app.use(dialogDrag) // ......
- 使用的时候
el-dialog
标签用一个div
包起来,div
加上v-dialogdrag
<div v-dialogdrag> <el-dialog v-model="show" :title="title"> 内容 </el-dialog> </div>
代码
const bus = require("vue3-eventbus")
const dialogDrag = (app) => {
app.directive('dialogdrag', {
// 渲染完毕
mounted(el) {
// 可视窗口的宽度
const clientWidth = document.documentElement.clientWidth
// 可视窗口的高度
const clientHeight = document.documentElement.clientHeight
// 记录坐标
let domset = {
x: clientWidth / 4, // 默认width 50%
y: clientHeight * 15 / 100 // 根据 15vh 计算
}
// 弹窗的容器
const domDrag = el.firstElementChild.firstElementChild
// 超出部分隐藏,不显示滚动条
el.firstElementChild.style.overflow = "hidden"
// 重新设置上、左距离
domDrag.style.marginTop = domset.y + 'px'
domDrag.style.marginLeft = domset.x + 'px'
// 记录拖拽开始的光标坐标,0 表示没有拖拽
let start = {
x: 0,
y: 0
}
// 移动中记录偏移量
let move = {
x: 0,
y: 0
}
// 限制拖动弹窗的 header 才可以移动弹窗
const dialogHeader = el.getElementsByClassName('el-dialog__header')[0]
// 添加右上角全屏显示按钮, i 标签是阿里图标库里面的
let div = document.createElement('div')
div.style.fontSize='.8rem'
div.style.position='absolute'
div.style.right='45px'
div.style.top='20px'
let full_button = document.createElement('span')
full_button.style.cursor='pointer'
full_button.innerHTML='<i class="iconfont icon-full"></i>'
full_button.addEventListener('click', full_dia) // 单击全屏
div.appendChild(full_button)
dialogHeader.appendChild(div)
let full = false
/**
* 双击全屏
*/
dialogHeader.addEventListener('dblclick',full_dia)
function full_dia(){
if (full) {
domset = {
x: clientWidth / 4,
y: clientHeight * 15 / 100
}
domDrag.style.width = "50%"
domDrag.style.height = "auto"
domDrag.style.marginLeft = domset.x + 'px'
domDrag.style.marginTop = domset.y + 'px'
full_button.innerHTML='<i class="iconfont icon-full"></i>'
// 由于我弹窗里面有 echarts 图表,要让图表修改大小,使用的是vue3 bus
bus.bus.off('domResize')
bus.bus.emit('domResize', {
width: domDrag.clientWidth,
height: domDrag.clientHeight
})
full = false
} else {
domDrag.style.width = "100%"
domDrag.style.height = "100%"
domDrag.style.marginLeft = '0'
domDrag.style.marginTop = '0'
full_button.innerHTML='<i class="iconfont icon-fullscreen-exit"></i>'
bus.bus.off('domResize')
bus.bus.emit('domResize', {
width: domDrag.clientWidth,
height: domDrag.clientHeight
})
full = true
}
}
/**
* 鼠标按下,开始拖拽,添加 mousemove、mouseup 事件
*/
dialogHeader.onmousedown = (e) => {
// 判断对话框是否重新打开
if (domDrag.style.marginTop === '15vh') {
// 重新打开,设置 domset.y top
domset.y = clientHeight * 15 / 100
}
start.x = e.clientX
start.y = e.clientY
domDrag.style.cursor = 'move' // 改变光标形状
// 鼠标拖出浏览器外部的时候会选中文本,需要禁用
document.body.style.userSelect = 'none'
// 使用 document 的 mousemove ,这样鼠标脱离 header 区域还是可以正常拖动(贴着窗口边缘移动)
// 鼠标移动,实时跟踪
document.addEventListener('mousemove', dia_mousemove)
// 鼠标抬起,结束拖拽,once: 只执行一次
document.addEventListener('mouseup', dia_mouseup, {
once: true
})
}
/**
* 鼠标移动,实时跟踪
*/
function dia_mousemove(e) {
if (start.x === 0) { // 不是拖拽状态
return
}
move.x = e.clientX - start.x
move.y = e.clientY - start.y
// 弹窗 左、上、右、下 到窗口边缘的距离
let left = domset.x + move.x,
top = domset.y + move.y
let right = left + domDrag.clientWidth,
bottom = top + domDrag.clientHeight
if (left <= 0) {
domDrag.style.marginLeft = '0px'
} else if (right >= clientWidth) {
domDrag.style.marginLeft = (clientWidth - domDrag.clientWidth) + 'px'
} else {
domDrag.style.marginLeft = left + 'px'
}
if (top <= 0) {
domDrag.style.marginTop = '0px'
} else if (bottom >= clientHeight) {
domDrag.style.marginTop = (clientHeight - domDrag.clientHeight) + 'px'
} else {
domDrag.style.marginTop = top + 'px'
}
}
/**
* 鼠标抬起,结束拖拽,移除 mousemove 事件
*/
function dia_mouseup(e) {
move.x = e.clientX - start.x
move.y = e.clientY - start.y
let left = domset.x + move.x,
top = domset.y + move.y
let right = left + domDrag.clientWidth,
bottom = top + domDrag.clientHeight
domDrag.style.cursor = '' // 恢复光标形状
document.body.style.userSelect = 'text' // 恢复body可选中
// 结束拖拽
start.x = 0
if (left <= 0) {
domDrag.style.marginLeft = '0px'
domset.x = 0
} else if (right >= clientWidth) {
domDrag.style.marginLeft = (clientWidth - domDrag.clientWidth) + 'px'
domset.x = clientWidth - domDrag.clientWidth
} else {
domDrag.style.marginLeft = left + 'px'
domset.x = left
}
if (top <= 0) {
domDrag.style.marginTop = '0px'
domset.y = 0
} else if (bottom >= clientHeight) {
domDrag.style.marginTop = (clientHeight - domDrag.clientHeight) + 'px'
domset.y = clientHeight - domDrag.clientHeight
} else {
domDrag.style.marginTop = top + 'px'
domset.y = top
}
// 结束拖拽
document.removeEventListener('mousemove', dia_mousemove)
}
}
})
}
export default dialogDrag