ModalDragMixins.js 4.49 KB
import {getRefPromise} from '@/utils/util'

/** JModal 的拖拽混入 */
export default {
  data() {
    return {
      // 拖动配置
      dragSettings: {
        // 上次拖动top记录
        top: null,
        // 上次拖动left记录
        left: null,
        wrapEl: null,
        dragEl: null,
        headerEl: null,
      },
    }
  },
  watch: {
    visible() {
      if (!this.visible || !this.draggable) {
        return
      }
      this.handleDrag()
    },
    draggable() {
      if (!this.visible || !this.draggable) {
        return
      }
      this.handleDrag()
    },
  },
  methods: {
    async handleDrag() {
      let modalRef = await getRefPromise(this, 'modal')
      const dragWraps = modalRef.$el.querySelectorAll('.ant-modal-wrap')
      let wrapEl = dragWraps[0]
      if (!wrapEl) return
      this.dragSettings.wrapEl = wrapEl
      this.dragSettings.dragEl = wrapEl.querySelector('.ant-modal')
      this.dragSettings.headerEl = wrapEl.querySelector('.ant-modal-header')
      const display = getStyle(wrapEl, 'display')
      const draggable = wrapEl.getAttribute('data-drag')
      if (display !== 'none') {
        // 拖拽位置
        if (draggable === null || this.destroyOnClose) {
          this.enableDrag(wrapEl)
        }
      }
    },
    /** 启用拖拽 */
    enableDrag() {
      let {wrapEl, dragEl, headerEl} = this.dragSettings
      if (!wrapEl) return
      wrapEl.setAttribute('data-drag', this.draggable)
      if (!headerEl || !dragEl || !this.draggable) return

      // 还原上一次移动的位置
      this.resetModalPosition()

      headerEl.style.cursor = 'move'
      headerEl.onmousedown = (e) => {
        if (!e) return
        // 鼠标按下,计算当前元素距离可视区的距离
        const disX = e.clientX
        const disY = e.clientY
        const screenWidth = document.body.clientWidth // body当前宽度
        const screenHeight = document.documentElement.clientHeight // 可见区域高度(应为body高度,可某些环境下无法获取)

        const dragElWidth = dragEl.offsetWidth // 对话框宽度
        const dragElHeight = dragEl.offsetHeight // 对话框高度

        const minDragElLeft = dragEl.offsetLeft

        const maxDragElLeft = screenWidth - dragEl.offsetLeft - dragElWidth
        const minDragElTop = dragEl.offsetTop
        const maxDragElTop = screenHeight - dragEl.offsetTop - dragElHeight
        // 获取到的值带px 正则匹配替换
        const domLeft = getStyle(dragEl, 'left')
        const domTop = getStyle(dragEl, 'top')
        let styL = +domLeft
        let styT = +domTop

        // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
        if (domLeft.includes('%')) {
          styL = +document.body.clientWidth * (+domLeft.replace(/%/g, '') / 100)
          styT = +document.body.clientHeight * (+domTop.replace(/%/g, '') / 100)
        } else {
          styL = +domLeft.replace(/px/g, '')
          styT = +domTop.replace(/px/g, '')
        }

        document.onmousemove = (e) => {
          // 全屏时不触发移动方法
          if (this.innerFullscreen) {
            return
          }
          // 通过事件委托,计算移动的距离
          let left = e.clientX - disX
          let top = e.clientY - disY

          // 边界处理
          if (-left > minDragElLeft) {
            left = -minDragElLeft
          } else if (left > maxDragElLeft) {
            left = maxDragElLeft
          }

          if (-top > minDragElTop) {
            top = -minDragElTop
          } else if (top > maxDragElTop) {
            top = maxDragElTop
          }

          this.setModalPosition(top + styT, left + styL)
        }

        document.onmouseup = () => {
          document.onmousemove = null
          document.onmouseup = null
        }
      }
    },

    /**
     * 移动弹窗位置
     * @param top 顶部位置
     * @param left 左侧位置
     * @param remember 是否记录位置,默认 true
     */
    setModalPosition(top, left, remember = true) {
      // 记录移动位置
      if (remember) {
        this.dragSettings.top = top
        this.dragSettings.left = left
      }
      // 移动当前元素
      this.dragSettings.dragEl.style.cssText += `;left:${left}px;top:${top}px;`
    },
    /**
     * 将弹窗移动到上次记录的位置
     */
    resetModalPosition() {
      this.setModalPosition(this.dragSettings.top, this.dragSettings.left, false)
    },

  },
}

function getStyle(dom, attr) {
  return getComputedStyle(dom)[attr]
}