vue实现打开多个可拖动弹窗

4/28/2023 VueJavaScript

实现可拖动这一功能的话网上教程很多,而我需要在此基础上实现点击页面打开多个可拖动弹出框。

首先以下执行命令,安装vuedraggable,通过vuedraggable 实现拖动过渡动画比较好。

npm install vuedraggable --save
新建dialogDrag.js文件,文件内容如下:

import Vue from 'vue'
Vue.directive('dialogDrag', {
  bind(el) {
    const dialogHeaderEl = el.querySelector('.el-dialog__header')
    const dragDom = el.querySelector('.el-dialog')
    dialogHeaderEl.style.cursor = 'move'
    // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
    const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null)
 
    dialogHeaderEl.onmousedown = (e) => {
      // 鼠标按下,计算当前元素距离可视区的距离
      const disX = e.clientX - dialogHeaderEl.offsetLeft
      const disY = e.clientY - dialogHeaderEl.offsetTop
      const screenWidth = document.body.clientWidth // body当前宽度
      const screenHeight = document.documentElement.clientHeight // 可见区域高度(应为body高度,可某些环境下无法获取)
      const dragDomWidth = dragDom.offsetWidth // 对话框宽度
      const dragDomheight = dragDom.offsetHeight // 对话框高度
      const minDragDomLeft = dragDom.offsetLeft
      const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth
      const minDragDomTop = dragDom.offsetTop
      const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight
 
      // 获取到的值带px 正则匹配替换
      let styL, styT
 
      // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
      if (sty.left.includes('%')) {
        styL = +document.body.clientWidth * (+sty.left.replace(/%/g, '') / 100)
        styT = +document.body.clientHeight * (+sty.top.replace(/%/g, '') / 100)
      } else {
        styL = +sty.left.replace(/\px/g, '')
        styT = +sty.top.replace(/\px/g, '')
      }
 
      document.onmousemove = function(e) {
        // 获取body的页面可视宽高
        // var clientHeight = document.documentElement.clientHeight || document.body.clientHeight
        // var clientWidth = document.documentElement.clientWidth || document.body.clientWidth
 
        // 通过事件委托,计算移动的距离
        var l = e.clientX - disX
        var t = e.clientY - disY
 
        // 边界处理
        if (-l > minDragDomLeft) {
          l = -minDragDomLeft
        } else if (l > maxDragDomLeft) {
          l = maxDragDomLeft
        }
        if (-t > minDragDomTop) {
          t = -minDragDomTop
        } else if (t > maxDragDomTop) {
          t = maxDragDomTop
        }
        // 移动当前元素
        dragDom.style.left = `${l + styL}px`
        dragDom.style.top = `${t + styT}px`
 
        // 将此时的位置传出去
        // binding.value({x:e.pageX,y:e.pageY})
      }
 
      document.onmouseup = function() {
        document.onmousemove = null
        document.onmouseup = null
      }
    }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

在页面中通过v-dialogDrag命令绑定到组件上,实现拖动效果。

<el-dialog 
    v-dialogDrag //绑定拖动效果
    :title="title"
    :lock-scroll="false"
    :close-on-press-escape="false"
    :visible.sync="dialogVisible"
    width="30%"
    :append-to-body="true" // 解决弹窗关不掉或者在弹窗框上层多出一个遮罩层的问题
    destroy-on-close>
     <el-image :src="imageUrl"></el-image>
</el-dialog>
1
2
3
4
5
6
7
8
9
10
11

此时已实现弹窗框的可拖动效果。
但还未达到我的需求,接下来需要实现打开弹出框可以继续操作页面。弹出框默认是有遮罩层的,需要关闭遮罩层,同时关闭通过点击 modal 关闭 Dialog。

<el-dialog 
    v-dialogDrag //绑定拖动效果
    :title="title"
    :lock-scroll="false"
    :close-on-press-escape="false"
    :visible.sync="dialogVisible"
    width="30%"
    :append-to-body="true" // 解决弹窗关不掉或者在弹窗框上层多出一个遮罩层的问题
    :close-on-click-modal="false" //关闭通过点击 modal 关闭 Dialog
    :modal="false" // 关闭遮罩层
    destroy-on-close>
     <el-image :src="imageUrl"></el-image>
</el-dialog>
1
2
3
4
5
6
7
8
9
10
11
12
13

接下来实现打开弹窗后可继续点击页面。
加上下边css样式即可。

.el-dialog__wrapper {
    pointer-events: none;
}
 
.el-dialog {
    pointer-events:auto;
}
1
2
3
4
5
6
7

注意:css样式一定要写对位置,否则会不生效,因为.el-dialog样式未生效,于是把.el-dialog样式写到了dialogDrag.js文化中。
最终版代码如下:

import Vue from 'vue'
 
// v-dialogDrag: 弹窗拖拽
Vue.directive('dialogDrag', {
  bind(el) {
    const dialogHeaderEl = el.querySelector('.el-dialog__header')
    const dragDom = el.querySelector('.el-dialog')
    dialogHeaderEl.style.cursor = 'move'
    // 重点!!!!解决打开多个弹窗后,弹窗失去拖动功能
    // .el-dialog样式
    dragDom.style.pointerEvents = 'auto';
    // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
    const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null)
    dialogHeaderEl.onmousedown = (e) => {
      
      // 鼠标按下,计算当前元素距离可视区的距离
      const disX = e.clientX - dialogHeaderEl.offsetLeft
      const disY = e.clientY - dialogHeaderEl.offsetTop
      const screenWidth = document.body.clientWidth // body当前宽度
      const screenHeight = document.documentElement.clientHeight // 可见区域高度(应为body高度,可某些环境下无法获取)
      const dragDomWidth = dragDom.offsetWidth // 对话框宽度
      const dragDomheight = dragDom.offsetHeight // 对话框高度
      const minDragDomLeft = dragDom.offsetLeft
      const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth
      const minDragDomTop = dragDom.offsetTop
      const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight
 
      // 获取到的值带px 正则匹配替换
      let styL, styT
 
      // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
      if (sty.left.includes('%')) {
        styL = +document.body.clientWidth * (+sty.left.replace(/%/g, '') / 100)
        styT = +document.body.clientHeight * (+sty.top.replace(/%/g, '') / 100)
      } else {
        styL = +sty.left.replace(/\px/g, '')
        styT = +sty.top.replace(/\px/g, '')
      }
 
      document.onmousemove = function(e) {
        // 获取body的页面可视宽高
        // var clientHeight = document.documentElement.clientHeight || document.body.clientHeight
        // var clientWidth = document.documentElement.clientWidth || document.body.clientWidth
 
        // 通过事件委托,计算移动的距离
        var l = e.clientX - disX
        var t = e.clientY - disY
 
        // 边界处理
        if (-l > minDragDomLeft) {
          l = -minDragDomLeft
        } else if (l > maxDragDomLeft) {
          l = maxDragDomLeft
        }
        if (-t > minDragDomTop) {
          t = -minDragDomTop
        } else if (t > maxDragDomTop) {
          t = maxDragDomTop
        }
        // 移动当前元素
        dragDom.style.left = `${l + styL}px`
        dragDom.style.top = `${t + styT}px`
 
        // 将此时的位置传出去
        // binding.value({x:e.pageX,y:e.pageY})
      }
 
      document.onmouseup = function() {
        document.onmousemove = null
        document.onmouseup = null
      }
    }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<template>
      <div class="dd_class_div">
            <el-dialog 
            v-dialogDrag //绑定拖动效果
            :title="title"
            :lock-scroll="false"
            :close-on-press-escape="false"
            :visible.sync="dialogVisible"
            width="30%"
            :append-to-body="true" // 解决弹窗关不掉或者在弹窗框上层多出一个遮罩层的问题
            :close-on-click-modal="false" //关闭通过点击 modal 关闭 Dialog
            :modal="false" // 关闭遮罩层
            destroy-on-close>
                <el-image :src="imageUrl"></el-image>
            </el-dialog>
      </div>
</template>
 
<script>
 export default {
  name: 'draggableDialog',
  data() {
      return {
            dialogVisible:false,
            imageUrl:'',
            title:''
      }
  },
  methods: {
        dialogClose(){
            this.dialogVisible = false;
        }
  }
 }
</script>
 
<style scoped>
.el-dialog__wrapper{
      pointer-events: none;
}
 
.dd_class_div {
      text-align: center;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
Last Updated: 12/28/2023, 5:03:22 PM