Vue 实现可拖拽弹窗

一、实现原理

1、获取鼠标在div中的位置
2、设置 div 的 left 和 top 使其跟随鼠标位置移动,达到拖拽的效果

二、实现步骤

1、UI

2、组件定义props

visible:控制弹窗显示,false 不显示,true 显示
title:弹窗标题,默认 提示
confirmtext:确认按钮文案,默认 确定
canceltext: 取消按钮文案,默认 取消
showCancelButton:是否显示取消按钮,false 否,true 是,默认 true

3、组件事件回调 $emit
methods: {
    cancel: function () {
      // .sync 实现弹窗显示 or 隐藏
      this.$emit("update:visible", false)
      this.$emit("cancel")
    },
    confirm: function () {
      this.$emit("confirm")
    },
  },
4、组件使用到的相关属性
  • event.clientX / event.clientY:鼠标触发时指针相对于浏览器可视区域的水平 / 垂直坐标
  • vnode.offsetLeft / vnode.offsetTop属性:当前元素距离某个定位父辈元素左边 / 顶部的距离
  • vnode.offsetWidth / vnode.offsetHeight:当前元素的宽 / 高度,包括边框和填充
  • window.innerHeight / window.innerHeightWidth:获取当前页面可视区的宽高(包括滚动条)
5、组件自定义指令

https://cn.vuejs.org/v2/guide/custom-directive.html

6、组件使用到的相关事件
  • onmousedown 鼠标按下事件
  • onmousemove 鼠标移动事件
  • onmouseup 鼠标松开事件
el.onmousedown = function(e){
    console.log('鼠标已按下:', e)
}
el.onmousemove = function(e){
    console.log('鼠标移动中:', e)
}
el.onmouseup = function(e){
    console.log('鼠标已松开:', e)
}
// 注:el 表示当前触发的元素
7、实现思路
  • 给弹窗绑定onmousedown事件,获取鼠标在弹窗中按下的位置(以弹窗左上角为原点)
el.onmousedown = ((event) => {
  let mouseX = event.clientX - vnode.offsetLeft
  let mouseY = event.clientY - vnode.offsetTop
})
  • document绑定onmousemove事件,获取当前的鼠标位置,当前鼠标位置 - 鼠标在弹窗中的相对位置,通过style设置弹窗的当前位置
document.onmousemove = ((event) => {
  let left, top
  // 获取新的鼠标位置(event.clientX, event.clientY)
  // 弹窗应该在的位置(left, top)
  // (mouseX, mouseY) 鼠标按下时的坐标
  left = event.clientX - mouseX
  top = event.clientY - mouseY
  // 赋值移动
  vnode.style.left = left + 'px'
  vnode.style.top = top + 'px'
})
  • 鼠标松开解绑document的鼠标事件onmousedown,onmousemove
document.onmouseup = (() => {
  document.onmousemove = document.onmouseup = null
})
  • 控制弹窗只能在浏览器可视区内被拖拽,需要设置水平和垂直方向的最大最小移动位置,在“赋值移动”前添加下面代码
// 获取弹窗在页面中距X轴的最小、最大 位置
let minX = -vnode.offsetWidth / 2 + 100
let maxX = window.innerWidth + vnode.offsetWidth / 2 - 100
if (left <= minX) {
  left = minX
} else if (left >= maxX) {
  left = maxX
}
// 获取弹窗在页面中距Y轴的最小、最大 位置
let minY = vnode.offsetHeight / 2
let maxY = window.innerHeight + vnode.offsetHeight / 2 - 100
if (top <= minY) {
  top = minY
} else if (top >= maxY) {
  top = maxY
}
  • 浏览器可视区大小变化时重置弹窗的位置到初始化状态
window.onresize = (() => {
  vnode.style.left = "50%"
  vnode.style.top = "50%"
})
  • 控制鼠标按下弹窗指定区域才能拖拽弹窗,在 onmousedown 事件中添加下面代码
// my_dialog_title 指定区域对应元素的类名
if (event.target.className !== "my_dialog_title") {
  return
}
8、完整拖拽指令

  directives: {
    drag: {
      inserted: function (el, binding, vnode) {
        vnode = vnode.elm
        el.onmousedown = ((event) => {
          if (event.target.className !== "my_dialog_title") {
            return
          }
          // (clientX, clientY)点击位置距离当前可视区域的坐标(x,y)
          // offsetLeft, offsetTop 距离上层或父级的左边距和上边距

          // 获取鼠标在弹窗中的位置
          let mouseX = event.clientX - vnode.offsetLeft
          let mouseY = event.clientY - vnode.offsetTop

          // 绑定移动和停止函数
          document.onmousemove = ((event) => {
            let left, top

            // 获取新的鼠标位置(event.clientX, event.clientY)
            // 弹窗应该在的位置(left, top)
            left = event.clientX - mouseX
            top = event.clientY - mouseY

            // offsetWidth、offsetHeight 当前元素的宽度
            // innerWidth、innerHeight 浏览器可视区的宽度和高度

            // 获取弹窗在页面中距X轴的最小、最大 位置
            let minX = -vnode.offsetWidth / 2 + 100
            let maxX = window.innerWidth + vnode.offsetWidth / 2 - 100
            if (left <= minX) {
              left = minX
            } else if (left >= maxX) {
              left = maxX
            }

            // 获取弹窗在页面中距Y轴的最小、最大 位置
            let minY = vnode.offsetHeight / 2
            let maxY =window.innerHeight + vnode.offsetHeight / 2 - 100
            if (top <= minY) {
              top = minY
            } else if (top >= maxY) {
              top = maxY
            }
            // 赋值移动
            vnode.style.left = left + 'px'
            vnode.style.top = top + 'px'
          })
          document.onmouseup = (() => {
            document.onmousemove = document.onmouseup = null
          })
        })
        window.onresize = (() => {
          vnode.style.left = "50%"
          vnode.style.top = "50%"
        })
      }
    }
  }
9、引用

使用 import 引入
eg: import myDialog from '@/components/myDialog'




10、CSS

你可能感兴趣的:(Vue 实现可拖拽弹窗)