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使用
<el-dialog
:custom-class="sc-common-dialog"
v-dialogDrag
:visible="visible"
:modal="false"
:width="30%"
:top="'0'"
:before-close="closeDialog"
:destroy-on-close="true"
>
<slot></slot>
</el-dialog>
实现难点
1. 使用pointer-events
属性和z-index
属性解决层级覆盖问题
pointer-events
:指定在什么情况下某个特定的图形元素可以成为鼠标事件的 target
。对于浏览器来说,只有auto
和none
两个值可用,其它的几个是针对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中的鼠标位置及元素宽度位置总结
发表评论 (审核通过后显示评论):