• 使用 vue3 的自定义指令给 element-plus 的 el-dialog 增加拖拽功能


    element-plus 提供的 el-dialog 对话框功能非常强大,只是美中不足不能通过拖拽的方式改变位置,有点小遗憾,那么怎么办呢?我们可以通过 vue 的自定义指令来实现一个可以拖拽的对话框(el-dialog)。

    拖拽演示

    https://www.zhihu.com/zvideo/1380450791975731200

    vue3 的自定义指令 directive

    为啥选择自定义指令的方式来实现呢?一个是可以方便的获得 dom 便于操作,另一个是方便使用和封装。

    自定义指令有两种注册方式,一个是全局注册,一个是局部注册。

    • 全局注册自定义指令
    app.directive('focus', {
      // 当被绑定的元素插入到 DOM 中时……
      mounted(el) {
        // Focus the element
        el.focus()
      }
    })
    

    来自于官网。后面做插件的时候需要用到。

    • 局部注册自定义指令
    directives: {
      focus: {
        // 指令的定义
        mounted(el) {
          el.focus()
        }
      }
    }
    

    在开发测试的阶段可以用这种方式,便于调试。插件每次修改的时候都会重新加载,而局部注册的只需要局部更新即可。

    我们可以定义一个 dialogdrag,然后在 mounted 里面实现拖拽的功能。

    分析 element-plus 的 Dialog 对话框

    想要实现拖拽功能,首先要了解 Dialog 对话框渲染出来的结构,然后才好针对性下手改造。
    通过分析可见如下结构:

    对话框结构

    简单的说,一个 div 里面放了三个 div,通过 margin(top、left) 来实现“居中”的效果。
    那么也就是说我们想要实现拖拽功能的话,可以通过改变 margin-left、margin-top 的方式来。

    鼠标的三个函数

    提到拖拽功能,那么鼠标的三个事件 onmousedown、onmousemove、onmouseup 就必不可少了。

    • onmousedown
      鼠标按下的时候记录光标的坐标,进入拖拽状态。

    • onmouseup
      鼠标抬起的时候记录光标的坐标,结束拖拽状态。

    • onmousemove
      按住鼠标拖动的时候触发,计算光标的偏移量,修改对话框的 margin 实现拖拽的效果。

    实现代码

    本来想写一个通用一点的,但是对话框渲染出来的结构比较复杂,似乎也不够通用,所以先针对 el-dialog 实现拖拽功能。

      app.directive('dialogdrag', {
        // 渲染完毕
        mounted(el, binding) {
          // binding.arg
          // binding.value
          // 可视窗口的宽度
          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
          // 重新设置上、左距离
          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 }
    
          // 鼠标按下,开始拖拽
          domDrag.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' // 改变光标形状
          }
    
          // 鼠标移动,实时跟踪
          domDrag.onmousemove = (e) => {
            if (start.x === 0) { // 不是拖拽状态
              return
            }
            move.x = e.clientX - start.x
            move.y = e.clientY - start.y
    
            // 初始位置 + 拖拽距离
            domDrag.style.marginLeft = ( domset.x + move.x )  + 'px'
            domDrag.style.marginTop = ( domset.y + move.y )  + 'px'
          }
          // 鼠标抬起,结束拖拽
          domDrag.onmouseup = (e) => {
            move.x = e.clientX - start.x
            move.y = e.clientY - start.y
    
            // 记录新坐标,作为下次拖拽的初始位置
            domset.x += move.x
            domset.y += move.y
            domDrag.style.cursor = '' // 恢复光标形状
            domDrag.style.marginLeft = domset.x + 'px'
            domDrag.style.marginTop = domset.y + 'px'
            // 结束拖拽
            start.x = 0
          }
        }
      })
    
    • 重新定位对话框
      默认的 top 是15vh,也就是距离顶部 15% ,所以需要 用 clientHeight * 15 / 100 转换为像素。
      默认的 left 是 50%,所以需要用 clientWidth / 4 转换为像素。

    这样修改有一个小问题,当窗口大小发生改变的时候,左距离不会随之改变。

    • 记录位置坐标和偏移量
      首先要记录对话框的距离,然后要记录拖拽的时候产生的偏移量。
    1. domset 可以记录对话框的初始坐标。
    2. start 可以记录开始拖拽的时候光标的位置。
    3. move 记录拖拽过程中,光标移动的偏移量。
    • 按下鼠标 onmousedown
      按下鼠标,表示开始拖拽,这时候需要我们记录光标的位置。
      另外在测试的时候发现一个小问题,当关闭对话框的时候有一个过渡动画,然后在打开对话框进行拖拽的时候,就飞掉了。

    找了一下原因后发现,在关闭的过渡动画的时候,会把 top 改成 15vh,这样就和我们拖拽后的 top 不一致。

    所以在按下鼠标的时候需要做一个判断。如果恢复了初始状态,那么需要改一下 domset 的 y 坐标。x坐标不用改,因为过渡动画没有改 left 。

    • 移动鼠标 onmousemove
      在移动鼠标的过程中,我们可以得到光标的位置,减去初始光标位置,就是对话框要移动的距离。
      然后我们用对话框的 初始坐标 + 偏移量,就可以得到对话框的新的位置坐标。
      这样就实现了对话框的拖拽。

    • 抬起鼠标 onmouseup
      不能一直拖拽,所以我们需要一个结束动作。
      当抬起鼠标的时候,我们可以认为是结束拖拽了,这时我们要记录对话框的新的位置坐标,
      然后设置 start.x = 0 表示结束拖拽。

    做成插件便于复用

    最后我们把这个拖拽功能做成一个插件,这样可以便于全局注册。

    建立一个js文件

    // dialogDrag.js
    
    const dialogDrag = (app, options) => {
      app.directive('dialogdrag', {
        // 指令的定义
        mounted(el, binding) {
        同上,略...
    }
    
    export default dialogDrag
    

    然后在 main.js 里面挂载这个插件。

    // 拖拽
    import dialogDrag from './control-web/js/dialogDrag.js'
    
    createApp(App).use(dialogDrag) // 对话框的拖拽
    
    

    使用方式

    本来想直接放在 el-dialog 里面,但是却没有效果,所以只好在外面套上一个 div。

    <div v-dialogdrag>
        <el-dialog
          title="收货地址"
          v-model="dialogFormVisible"
          :modal="false"
        >
         略...
        </el-dialog>
      </div>
    

    注意,要加上 v- ,即 v-dialogdrag。

    源码

    https://gitee.com/naturefw/nf-vite2-element
    /src/control-web/js/dialogDrag.js

    https://gitee.com/naturefw/nf-vite2-element/tree/master/src/control-web/js

  • 相关阅读:
    Java操作zip压缩和解压缩文件工具类
    Java操作图片的工具类
    使用Jacob操作Wrod文档的工具类代码
    Java计算文件的SHA码和MD5码
    Java 文件名操作的相关工具类
    Java中windows路径转换成linux路径等工具类
    JDBC的批量批量插入
    显示创建一个表的SQL语句
    MySQL中的保留字
    插入到Mysql数据库中的汉字乱码
  • 原文地址:https://www.cnblogs.com/jyk/p/14807244.html
Copyright © 2020-2023  润新知