Vue3+TS+Quasar框架实现弹框拖拽

通过自定义指令来实现弹框拖拽

ps : 全局指令会更方便哦,局部指令可以用来调试,调试好提取放在全局即可;

一、局部指令(页面组件里使用)

<script>
	const defineDir = {
  dialogDrag:{
    beforeMount(el,binding){
      if (!binding.value) return
      const dialogHeaderEl = el.querySelector('.dialog_header')
      const dragDom = el.querySelector('.dialog_content')
      dialogHeaderEl.style = 'cursor:move;';
      const sty = (function () {
        if ((document.body).currentStyle) {
          // 在ie下兼容写法
          return (dom, attr) => dom.currentStyle[attr]
        }
        return (dom, attr) => getComputedStyle(dom, null)[attr]
      })()

      dialogHeaderEl.onmousedown = (e) => {
        if(e.target.nodeName=='INPUT' || e.target.nodeName=='TEXTAREA'){
          return
        }
        // 鼠标按下,计算当前元素距离可视区的距离
        const disX = e.clientX - dialogHeaderEl.offsetLeft
        const disY = e.clientY - dialogHeaderEl.offsetTop
        const screenWidth = document.body.clientWidth // body当前宽度
        const screenHeight = document.documentElement.clientHeight // 可见区域高度(应为body高度,可某些环境下无法获取)

        const dragDomWidth = dragDom.offsetWidth // 对话框宽度
        const dragDomheight = dragDom.offsetHeight // 对话框高度

        const minDragDomLeft = dragDom.offsetLeft
        const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth

        const minDragDomTop = dragDom.offsetTop
        const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight

        // 获取到的值带px 正则匹配替换
        let styL = sty(dragDom, 'left')
        // 为兼容ie
        if (styL === 'auto') styL = '0px'
        let styT = sty(dragDom, 'top')

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

        document.onmousemove = function (e) {
          // 通过事件委托,计算移动的距离
          console.log(e)
          let left = e.clientX - disX
          let top = e.clientY - disY
          // 边界处理
          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
      }
    }
  }
}
export default {
  directives:defineDir, //局部 需要在这里注册哦
  components:{dictionaryData},
  name: "SysDictType",
  setup() {
  }
</script>

二、全局自定义指令

新建directives.ts文件

// directives.ts
const drag = {
  beforeMount(el: any, binding: any) {
    // 自定义属性,判断是否可拖拽
    if (!binding.value) return
    const dialogHeaderEl = el.querySelector('.dialog_header')
    const dragDom = el.querySelector('.dialog_content')
    dialogHeaderEl.style.cssText += ';cursor:move;'
    const sty = (function () {
      if ((document.body as any).currentStyle) {
        // 在ie下兼容写法
        return (dom: any, attr: any) => dom.currentStyle[attr]
      }
      return (dom: any, attr: any) => getComputedStyle(dom, null)[attr]
    })()
    dialogHeaderEl.onmousedown = (e: any) => {
      //过滤掉 input textarea
      if(e.target.nodeName=='INPUT' || e.target.nodeName=='TEXTAREA'){
        return
      }
      // 鼠标按下,计算当前元素距离可视区的距离
      const disX = e.clientX - dialogHeaderEl.offsetLeft
      const disY = e.clientY - dialogHeaderEl.offsetTop
      const screenWidth = document.body.clientWidth // body当前宽度
      const screenHeight = document.documentElement.clientHeight // 可见区域高度(应为body高度,可某些环境下无法获取)

      const dragDomWidth = dragDom.offsetWidth // 对话框宽度
      const dragDomheight = dragDom.offsetHeight // 对话框高度

      const minDragDomLeft = dragDom.offsetLeft
      const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth

      const minDragDomTop = dragDom.offsetTop
      const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight

      // 获取到的值带px 正则匹配替换
      let styL = sty(dragDom, 'left')
      // 为兼容ie
      if (styL === 'auto') styL = '0px'
      let styT = sty(dragDom, 'top')
      // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
      if (styL.includes('%')) {
        styL = +document.body.clientWidth * (+styL.replace(/%/g, '') / 100)
        styT = +document.body.clientHeight * (+styT.replace(/%/g, '') / 100)
      } else {
        styL = +styL.replace(/px/g, '')
        styT = +styT.replace(/px/g, '')
      }
      document.onmousemove = function (e) {
        console.log(e)
        // 通过事件委托,计算移动的距离
        let left = e.clientX - disX
        let top = e.clientY - disY
        // 边界处理
        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: any) {
        document.onmousemove = null
        document.onmouseup = null
      }
      return false
    }
  }
}
export default drag;

main.ts文件引入

//我这边框架是需要这么引入哈,具体main.ts里 你们根据自己框架里引入全局即可
import { boot } from 'quasar/wrappers'
import Directive from 'src/boot/directives'
export default boot(({ app }) => {
  app.directive('dialogDrag',Directive)
})

vue页面代码

 <q-dialog v-model="open">
 	// 指令 v-dialogDrag="true"
    <q-card  v-dialogDrag="true" class="box_body" >
        <div class="dialog_content" >
          //弹框头部
          <q-toolbar  class="dialog_header">
            <q-toolbar-title class="text-body1"> {{ title }}</q-toolbar-title>
            <q-btn flat round dense icon="close" v-close-popup />
          </q-toolbar>
          <div class="q-px-md " >
           //内容区域
          </div>
        </div>
      </q-card>
</q-dialog>
<style>
	//样式可自调整
	.box_body{
	  z-index: 9;
	  display: flex;
	  align-items: center;
	  justify-content: center;
	}
	.dialog_content{
	  width: fit-content;
	  height: fit-content;
	  width: 600px;
	  pointer-events: all;
	  overflow: auto;
	  background: white;
	  position: fixed;
	}
</style>

你可能感兴趣的:(javascript,前端,vue.js)