likes
comments
collection
share

vue-自定义指令拖动el-dialog

作者站长头像
站长
· 阅读数 54

前言

el-dialogelement-ui的一个弹窗组件,这类弹窗组件通常都需要能够让用户通过鼠标进行拖动,以调整弹窗位置或者避免遮挡一些信息。本文记叙个人在项目中用到的自定义拖动指令,及其实现的基本思路。

事件处理

浏览器并没有给出鼠标拖动的监听函数,如果需要实现鼠标监听,基本都是在目标元素上监听鼠标按下mousedown,然后再监听鼠标移动mousemove,鼠标移动时,目标元素随之移动,得到拖动目标元素的目的,同时,也监听鼠标松开mouseup,鼠标松开后,取消监听鼠标移动mousemove和鼠标松开mouseup

拖动el-dialog基本上也是同样的处理,不过需要注意的是,vue指令挂载的地方是el-dialog的遮罩层,也是就用户所看到的弹窗的父级,而需要挂载监听事件的,是用户所能看到的弹窗,准确来说是用户所看到的弹窗的header部分。

拖动处理

首先获取鼠标在el-dialogheader的点击位置,使用鼠标距离header的顶边的距离offsetTop和左边的距离offsetLeft来表示,如图所示: vue-自定义指令拖动el-dialog 那么,鼠标移动后,只需获取鼠标在页面中的位置,然后减去鼠标在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

边界判断

上面的代码基本实现了拖动的目的,但是并没有进行边界判断,这样会造成用户将弹窗拖出屏幕外。因为上面的代码使用相对定位的topleft来更改弹窗位置,这意味着弹窗有可能部分或者全部被拖到弹窗外,这显然并不合理,因此需要加上边界判。

原理其实很简单,上面的代码已经得到弹窗新的topleft的值了,那么只要确定topleft的取值范围即可。这个范围也比较好确定,用窗口大小 减去 弹窗大小就可以得到移动范围,如下图红色虚线区域: vue-自定义指令拖动el-dialog 完整代码如下:

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
评论
请登录