vue-自定义指令拖动el-dialog
前言
el-dialog
是element-ui
的一个弹窗组件,这类弹窗组件通常都需要能够让用户通过鼠标进行拖动,以调整弹窗位置或者避免遮挡一些信息。本文记叙个人在项目中用到的自定义拖动指令,及其实现的基本思路。
事件处理
浏览器并没有给出鼠标拖动的监听函数,如果需要实现鼠标监听,基本都是在目标元素上监听鼠标按下mousedown
,然后再监听鼠标移动mousemove
,鼠标移动时,目标元素随之移动,得到拖动目标元素的目的,同时,也监听鼠标松开mouseup
,鼠标松开后,取消监听鼠标移动mousemove
和鼠标松开mouseup
。
拖动el-dialog
基本上也是同样的处理,不过需要注意的是,vue
指令挂载的地方是el-dialog
的遮罩层,也是就用户所看到的弹窗的父级,而需要挂载监听事件的,是用户所能看到的弹窗,准确来说是用户所看到的弹窗的header
部分。
拖动处理
首先获取鼠标在el-dialog
中header
的点击位置,使用鼠标距离header
的顶边的距离offsetTop
和左边的距离offsetLeft
来表示,如图所示:
那么,鼠标移动后,只需获取鼠标在页面中的位置,然后减去鼠标在
header
中的位置,即可获得弹窗新的坐标位置。
代码
const vueElementDialogDraggable = {}
vueElementDialogDraggable.install = function (Vue, options) {
Vue.directive('draggable', {
bind: function (el, binding, vnode) {
// 获取弹窗元素
const dlg = el.queryselector('.el-dialog')
// 全局变量,用于记录鼠标在header中位置
let offsetTop = 0
let offsetLeft = 0
// 鼠标移动处理函数
const move = function (e) {
e.stopPropagation()
e.preventDefault()
// 计算弹窗的新位置,将鼠标的在页面中的位置 减去 鼠标在header中的位置
const left = e.clientX - offsetLeft
const top = e.clientY - offsetTop
// 设置弹窗的位置
dlg.style.marginLeft = '0px'
dlg.style.marginTop = '0px'
dlg.style.left = left + 'px'
dlg.style.top = top + 'px'
}
// 鼠标松开函数
const up = function (e) {
e.stopPropagation()
e.preventDefault()
// 取消监听鼠标移动、鼠标松开监听
removeEventListener('mousemove', move)
removeEventListener('mouseup', up)
}
// 鼠标按下事件
const down = function (e) {
e.stopPropagation()
e.preventDefault()
// 计算 鼠标在header中位置
offsetLeft = (e.clientX - dlg.offsetLeft)
offsetTop = (e.clientY - dlg.offsetTop)
// 挂载鼠标移动、鼠标松开事件
addEventListener('mousemove', move)
addEventListener('mouseup', up)
}
// 获取弹窗的header
const header = el.queryselector('.el-dialog__header')
// 挂载鼠标按下事件
header.addEventListener('mousedown', down)
}
})
}
module.exports = vueElementDialogDraggable
边界判断
上面的代码基本实现了拖动的目的,但是并没有进行边界判断,这样会造成用户将弹窗拖出屏幕外。因为上面的代码使用相对定位的top
和left
来更改弹窗位置,这意味着弹窗有可能部分或者全部被拖到弹窗外,这显然并不合理,因此需要加上边界判。
原理其实很简单,上面的代码已经得到弹窗新的top
和left
的值了,那么只要确定top
和left
的取值范围即可。这个范围也比较好确定,用窗口大小 减去 弹窗大小就可以得到移动范围,如下图红色虚线区域:
完整代码如下:
const vueElementDialogDraggable = {}
vueElementDialogDraggable.install = function (Vue, options) {
Vue.directive('draggable', {
bind: function (el, binding, vnode) {
// 获取弹窗元素
const dlg = el.queryselector('.el-dialog')
// 全局变量,用于记录鼠标在header中位置
let offsetTop = 0
let offsetLeft = 0
// left 的最大值
let maxLeftPosition = 0
// // top 的最大值
let maxTopPosition = 0
// 鼠标移动处理函数
const move = function (e) {
e.stopPropagation()
e.preventDefault()
// 计算弹窗的新位置,将鼠标的在页面中的位置 减去 鼠标在header中的位置
const left = e.clientX - offsetLeft
const top = e.clientY - offsetTop
// left 边界判断
if ( left < 0) {
left = 0
} else if (left > maxLeftPosition) {
left = maxLeftPosition
}
// top 边界判断
if (top < 0) {
top = 0
} else if (top > maxTopPosition) {
top = maxTopPosition
}
// 设置弹窗的位置
dlg.style.marginLeft = '0px'
dlg.style.marginTop = '0px'
dlg.style.left = left + 'px'
dlg.style.top = top + 'px'
}
// 鼠标松开函数
const up = function (e) {
e.stopPropagation()
e.preventDefault()
// 取消监听鼠标移动、鼠标松开监听
removeEventListener('mousemove', move)
removeEventListener('mouseup', up)
}
// 鼠标按下事件
const down = function (e) {
e.stopPropagation()
e.preventDefault()
// 计算 鼠标在header中位置
offsetLeft = (e.clientX - dlg.offsetLeft)
offsetTop = (e.clientY - dlg.offsetTop)
// 计算left和top取值范围
maxLeftPosition = document.body.clientWidth - dlg.offsetWidth
maxToPosition = document.body.clientHeight - dlg.offsetHeight
// 挂载鼠标移动、鼠标松开事件
addEventListener('mousemove', move)
addEventListener('mouseup', up)
}
// 获取弹窗的header
const header = el.queryselector('.el-dialog__header')
// 挂载鼠标按下事件
header.addEventListener('mousedown', down)
}
})
}
module.exports = vueElementDialogDraggable
转载自:https://juejin.cn/post/7103696591176859661