直接上代码
- //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 火狐谷歌 Windows.getComputedStyle(dom 元素, null);
- const sty = dragDom.currentStyle || Windows.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 中的鼠标位置及元素宽度位置总结
来源: http://www.jianshu.com/p/ec24b4a432a1