自定义指令-拖拽元素不影响下层元素的正常使用

新建dialogDrag.js文件

/*
* v-dialogDrag="{
        DragButton: '.window-header', 拖拽的按钮,或者拖拽鼠标的元素,move
        DragVindow: '.window', 拖拽的主窗口
        DragContainer: '.move-con', 拖拽元素的移动区域,默认为body
        custom: true 自定义,true的话默认移动区域(DragContainer)内居中显示
      }"
*/
// dialogDrag.js
 export default (app) => {
  app.directive('dialogDrag', (el, binding) => {
      // 没有规定默认移动窗口的时候,默认使用body作为窗口
    let bodyEl = document.body
    if (binding.value.DragContainer) {
      bodyEl = el.closest(binding.value.DragContainer) || document.body
    }
    // 移动区域可视宽度
    const screenWidth = bodyEl.clientWidth
    // 移动区域可视高度
    const screenHeight = bodyEl.clientHeight
    // 移动区域距离可视区域左侧的距离
    const containerLeft = bodyEl.getBoundingClientRect().left
    // 移动区域距离可视区域顶部的距离
    const containerTop = bodyEl.getBoundingClientRect().top

    // 拖拽窗口 DragVindow
    let DragVindow=el.querySelector(binding.value.DragVindow)
    // 以下两个判断是为了找到元素,也可以不需要
    if (!DragVindow) {
      DragVindow = el.closest(binding.value.DragVindow)
    }
    if(!DragVindow){
      DragVindow = document.querySelector(binding.value.DragVindow)
    }
    const dragDomWidth = DragVindow.offsetWidth // 对话框宽度
    const dragDomheight = DragVindow.offsetHeight // 对x话框高度

    // 拖拽按钮
    let DragButton=el.querySelector(binding.value.DragButton)
    if (!DragButton) {
      DragButton = el.closest(binding.value.DragButton)
    }
    if (!DragButton) {
      DragButton = document.querySelector(binding.value.DragButton)
    }
    DragButton.style.cssText += ';cursor:move;'

    // 如果是自定义组件 设置窗口默认居中
    if (binding.value.custom) {
      const [left, top] = [
        (screenWidth - dragDomWidth) / 2 + containerLeft,
        (screenHeight - dragDomheight) / 2 + containerTop
      ]
      DragVindow.style.cssText += `;left:${left}px;top:${top}px;`
    }

    // 获取元素的left或者top属性
    const sty = (function () {
      if (window.document.currentStyle) {
        return (dom, attr) => dom.currentStyle[attr]
      } else {
        return (dom, attr) => getComputedStyle(dom, false)[attr]
      }
    })()

    // 按下鼠标处理事件
    DragButton.onmousedown = (e) => {
      // 鼠标按下,计算当前元素距离可视区的距离 也就是鼠标的按下时候的起点位置
      const disX = e.pageX
      const disY = e.pageY

      // 元素left的最大值、最小值 最小值等于移动区域距离页面的距离 最大值等于移动区域的宽度减去移动元素的宽度加上移动区域距离页面的距离
      const minDragDomLeft = containerLeft
      const maxDragDomLeft = screenWidth - dragDomWidth + containerLeft

      const minDragDomTop = containerTop
      const maxDragDomTop = screenHeight - dragDomheight + containerTop

      // 记录移动前的初始left 只尝试了px,未尝试%
      let styL = sty(DragVindow, 'left')
      let styT = sty(DragVindow, 'top')
      if (styL.includes('%')) {
        styL = +bodyEl.clientWidth * (+styL.replace(/%/g, '') / 100)
        styT = +bodyEl.clientHeight * (+styT.replace(/%/g, '') / 100)
      } else {
        styL = +styL.replace(/px/g, '')
        styT = +styT.replace(/px/g, '')
      }
      document.onmousemove = (e) => {
        // 通过事件委托,计算鼠标移动的时候,移动元素的left值
        // e.pageX - disX未鼠标移动的距离  鼠标移动的距离加上初始left位置为移动后的left距离
        let left = e.pageX - disX + styL
        let top = e.pageY - disY + styT

        // 边界处理 left必须位于最小值和最大值之内
        if (left < minDragDomLeft) {
          left = minDragDomLeft
        } else if (left > maxDragDomLeft) {
          left = maxDragDomLeft
        }

        if (top < minDragDomTop) {
          top = minDragDomTop
        } else if (top > maxDragDomTop) {
          top = maxDragDomTop
        }
        // 设置当前元素
        DragVindow.style.cssText += `;left:${left}px;top:${top}px;`
      }
      document.onmouseup = () => {
        document.onmousemove = null
        document.onmouseup = null
      }
    }
  })
}

在main.js中引用

// main.js
import { createApp } from 'vue'
import dialogDrag from  '@/directive/dialogDrag.js'
import App from './App.vue'
const app = createApp(App)
app.use(dialogDrag)

组件中使用

<template>
  <div class="ss move-con">
    <div @click="showComponentModal = true">打开拖拽组件</div>
    <!-- first是一个可滚动的容器 -->
    <div class="first">
      <div class="second"></div>
    </div>

    <div
      class="custom"
      v-if="showComponentModal"
      v-dialogDrag="{
        DragButton: '.window-header',
        DragVindow: '.window',
        DragContainer: '.move-con',
        custom: true
      }"
    >
      <div class="window">
        <div class="window-header">
          按住拖拽 标题
          <div class="close" @click.stop="showComponentModal = false">关闭</div>
        </div>
        <div class="window-body">窗口默认</div>
      </div>
    </div>
  </div>
</template>
<script setup>
import { ref, onMounted } from 'vue'

const showComponentModal = ref(false)

onMounted(() => {})
</script>

<style lang="less" scoped>
.first {
  overflow: auto;
  width: 100%;
  height: 300px;
  background: #fff;
}
.ss {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
.second {
  width: 2000px;
  height: 200px;
  background: #eee;
}

.window {
  width: 400px;
  height: 300px;
  border: 1px solid #cccccc;
  /* 此处采用的是固定定位,和原作者有差别 */
  position: fixed;
  z-index: 99999;
  background: #e1eafc;
  .window-header {
    position: relative;
    height: 50px;
    line-height: 50px;
    text-align: center;
    background: #beceeb;
    user-select: none;
    .close {
      position: absolute;
      right: 20px;
      top: 10px;
      cursor: pointer;
    }
  }
}
</style>

在move-con区域内进行拖拽

dragDialog_1

在body区域内进行拖拽

dragDialog_2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值