vue对el-dialog封装增加拖动功能

2,063 阅读1分钟

背景介绍

项目当中弹框的地方比较多,应用户反馈,弹框的填写可能需要参考前面的页面,所以需要将弹框变为可拖动,尽量能看后面的内容。

比如在表格录入新增行的时候要参考下前面行的数据:

image.png

怎么做

计划写一个minxin的文件,混入到elementui的dialog中,在他的钩子函数中对dialog添加拖动事件,从而实现。

先上整体代码

编写minxin文件

export default ElementUI => {
  ElementUI.Dialog.mixins[ElementUI.Dialog.mixins.length] = {
    mounted() {
      let el = this.$el; // dialog的dom
      const dialogHeaderEl = el.querySelector(".el-dialog__header"); // dialog的头部,用来拖动
      const dragDom = el.querySelector(".el-dialog"); // 用来获取位置

      // 先增加一点拖动样式
      dialogHeaderEl.style.cssText += ";cursor:move;";

      // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
      const sty = (function () {
        if (window.document.currentStyle) {
          return (dom, attr) => dom.currentStyle[attr];
        } else {
          return (dom, attr) => getComputedStyle(dom, false)[attr];
        }
      })();

      // 添加拖动事件
      dialogHeaderEl.onmousedown = e => {
        // 注:需要对offsetTop,clientWidth,scrollTop等位置相关属性有了解
        // 鼠标按下,计算当前元素距离可视区的距离
        const disX = e.clientX - dialogHeaderEl.offsetLeft; // 相对dialog左上角的横向位置
        const disY = e.clientY - dialogHeaderEl.offsetTop; // 相对dialog左上角的纵向位置
        const screenWidth = document.body.clientWidth; // 可视区域宽度
        const screenHeight = document.documentElement.clientHeight; // 可视区域高度(应为body高度,可某些环境下无法获取)
        const dragDomWidth = dragDom.offsetWidth; // 对话框宽度
        const dragDomheight = dragDom.offsetHeight; // 对话框高度
        const minDragDomLeft = dragDom.offsetLeft; // 最小的left拖动距离 保证dialog全部能看见
        const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth - 30; // 最大的left拖动距离 多减30避免横向滚动条

        const minDragDomTop = dragDom.offsetTop; // 最小的top拖动距离 保证dialog全部能看见
        const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight; // 最大的top拖动距离

        // 如果弹窗高度太高,禁止拖动
        if (dragDomheight > document.documentElement.clientHeight) {
          dialogHeaderEl.style.cursor = "default";
          return;
        }
        // 获取dom的原有属性,获取到的值带px 正则匹配替换
        let styL = sty(dragDom, "left");
        let styT = sty(dragDom, "top");

        styL = +styL.replace(/\px/g, "");
        styT = +styT.replace(/\px/g, "");


        document.onmousemove = function (e) {

          // 通过事件委托,计算移动的距离 disx和disy是相对dialog左上角的距离
          let left = e.clientX - disX;
          let top = e.clientY - disY;

          // 计算移动位置 边界处理 保证dialog一直在可视区域
          if (-left > minDragDomLeft) {
            left = -minDragDomLeft;
          } else if (left > maxDragDomLeft) {
            left = maxDragDomLeft;
          }
          if (-top > minDragDomTop) {
            top = -minDragDomTop;
          } else if (top > maxDragDomTop) {
            top = maxDragDomTop;
          }

          // 移动当前元素
          dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`;
        };

        // 最后移除事件
        document.onmouseup = function (e) {
          document.onmousemove = null;
          document.onmouseup = null;
        };
        return false;
      };
    },
    beforeDestroy() {
      // 销毁组件移除事件
      this.$el.querySelector(".el-dialog__header").onmousedown = null;
    }
  };
};

在main.js中引入文件并使用

import ElementUI from "element-ui";
import dragDialog from "./mixins/dragDialog";
import "element-ui/lib/theme-chalk/index.css";

dragDialog(ElementUI); // 全局弹窗可以拖动

Vue.use(ElementUI);

主要位置说明

1.怎么直接在elementUI中混入
export default ElementUI => {
  // 关键点
  ElementUI.Dialog.mixins[ElementUI.Dialog.mixins.length] = {
      // 这里写内容
  }
2.添加拖动事件要有边界处理
要保证dialog永远在可视区域内部,具体为下面这段代码。
注:需要对offsetTop,clientTop,scrollTopd等有了解
// 计算移动位置 边界处理 保证dialog一直在可视区域
  if (-left > minDragDomLeft) {
    left = -minDragDomLeft;
  } else if (left > maxDragDomLeft) {
    left = maxDragDomLeft;
  }
  if (-top > minDragDomTop) {
    top = -minDragDomTop;
  } else if (top > maxDragDomTop) {
    top = maxDragDomTop;
  }
3.最后要移除事件
4.要看后面的内容,对应dialog的灰色蒙层就要覆盖
.v-modal {
  background: transparent !important;
}

最终效果

dialog可拖动且一直在可视范围内

image.png