vue 实现多个可拖拽的element-ui Dialog

直接上代码

//css
.el-dialog__wrapper {
    overflow: hidden!important;
    pointer-events: none;
}
.sc-common-dialog.el-dialog {
    margin: 0!important; 
    pointer-events: auto;
    top:80px ;
    left: calc(50% - 490px);
    height: 625px;
    box-shadow: 0px 4px 6px 0px rgba(0, 0, 0, 0.15);
}
.sc-common-dialog .el-dialog__header{
    padding:0 20px;
    color: #FFFFFF;
    height: 40px;
    line-height: 40px;
    background: linear-gradient(
84deg
, #5c65de 0%, #50a6fb 100%);
}

dialogDrag指令:

Vue.directive('dialogDrag', {
    bind(el, binding, vnode, oldVnode) {

        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) => {
            //点击的dialog层级设置最高
            const wrappers = document.getElementsByClassName('el-dialog__wrapper');
            const zIndexArray = Array.prototype.map.call(wrappers, item => item.style.zIndex)
            if (el.style.zIndex !== Math.max(...zIndexArray)) {
                el.style.zIndex = Math.max(...zIndexArray) + 1
            }
            // 鼠标按下,计算当前元素距离可视区的距离
            const disX = e.clientX - dialogHeaderEl.offsetLeft

            const disY = e.clientY - dialogHeaderEl.offsetTop

            // 获取到的值带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) {
                // 通过事件委托,计算移动的距离
                const l = e.clientX - disX
                const t = e.clientY - disY
                // 边界处理
                let left = l + styL;
                let top = t + styT;
                const styW = +sty.width.replace(/\px/g, '');
                const styH = +sty.height.replace(/\px/g, '');
                if (left < 0) {
                    left = 0
                } else if (styW > document.body.clientWidth) {
                    left = 0;
                } else if (left + styW > document.body.clientWidth) {
                    left = document.body.clientWidth - styW;
                }
                if (top < 0) {
                    top = 0
                } else if (styH > document.body.clientHeight) {
                    top = 0
                } else if (top + styH > document.body.clientHeight) {
                    top = document.body.clientHeight - styH;
                }
                // 移动当前元素:移动距离+原来的定位
                dragDom.style.left = `${left}px`
                dragDom.style.top = `${top}px`
            }

            document.onmouseup = function (e) {
                document.onmousemove = null
                document.onmouseup = null
            }
        }
    }
})
//dialog使用
    
        
    
实现难点
1. 使用pointer-events属性和z-index 属性解决层级覆盖问题

pointer-events:指定在什么情况下某个特定的图形元素可以成为鼠标事件的 target。对于浏览器来说,只有autonone两个值可用,其它的几个是针对SVG的(本身这个属性就来自于SVG技术)。

auto:与pointer-events属性未指定时的表现效果相同,鼠标不会穿透当前层。对于SVG内容,该值与visiblePainted效果相同。
none:元素永远不会成为鼠标事件的target。但是,当其后代元素的pointer-events属性指定其他值时,鼠标事件可以指向后代元素,在这种情况下,鼠标事件将在捕获或冒泡阶段触发父元素的事件侦听器。
也就是说,父元素设置为none,子元素设置为auto,是可以穿透当前层的。那就试试, el-dialog__wrapper 设置pointer-events:none,el-dialog设置pointer-events:auto,果然可以穿透了,可以获取鼠标事件。

2. 计算移动的距离及边界问题

需要计算2次距离,一次计算当前元素距离可视区的距离,一次计算移动的距离,最后移动元素。
鼠标位置及元素宽度问题详见js中的鼠标位置及元素宽度位置总结

你可能感兴趣的:(vue 实现多个可拖拽的element-ui Dialog)