likes
comments
collection
share

在Vue2中,如何实现浮窗移动功能如何通过组件监听的形式,实现浮窗拖拽式移动呢?下面,跟随我的脚步,一步一步了解功能细节

作者站长头像
站长
· 阅读数 57

原功能基于 el-dialog 实现,因而代码中参数名称会与该组件有所关联

效果展示

在Vue2中,如何实现浮窗移动功能如何通过组件监听的形式,实现浮窗拖拽式移动呢?下面,跟随我的脚步,一步一步了解功能细节

相关DOM

了解所需DOM

首先,需要的明确的是,过程中涉及对哪几个DOM元素的操作?

  • 为使得浮窗移动,需要动态调整其样式。因而,浮窗自身 便是操作元素之一

  • 而我们还需要某个区域,用于触发功能。这个区域往往是浮窗的头部;因而,浮窗头部 也是操作元素之一

  • 触发了功能,下一步便是执行移动操作。我们需要在视口相同范围内监听鼠标的位置。浮窗自身尺寸远小于视口,不满足需求。因而,我们只能采取其 外层遮罩 作为操作元素

综上,涉及的DOM元素有3个,分别是 浮窗自身浮窗头部外层遮罩

获取DOM

同一document中,可能同时存在多个浮窗;直接使用 querySelector 获取DOM,易导致查询错误

因而,我们需要借助 Vue refs 缩小查询范围

外层遮罩 通过 $refs 获取,其余两个DOM元素,在其范围内查询

// 外层遮罩(切记在对应标签上设置ref)
this.dialogWrapperDom = this.$refs.dialog.$el
// 浮窗
this.dialogDom = this.dialogWrapperDom.querySelector('.el-dialog')
// 触发区域
this.dragBodyDom = this.dialogDom.querySelector('.el-dialog__header')

相关监听事件

鼠标「按下」

// 将函数定义到 DOM对象中,便于后期移除
this.dragBodyDom.dragMouseDownEvent = (e) => {}

// 设置鼠标按下操作
this.dragBodyDom.addEventListener(
  'mousedown',
  this.dragBodyDom.dragMouseDownEvent
)

鼠标「移动」

// 移动函数(单独定义,便于移除)
const move = (e) => {}

// 鼠标按下后,添加 鼠标移动 监听事件
this.dialogWrapperDom.addEventListener('mousemove', move)

鼠标「弹起」

// 鼠标抬起后,移除 鼠标移动 监听事件
this.dialogWrapperDom.addEventListener(
  'mouseup',
  () => {
    // 移除 鼠标移动 事件
    this.dialogWrapperDom.removeEventListener('mousemove', move)
  },
  // 执行一次后随即移除。避免多次开启移动后,累加 mouseup 事件
  { once: true }
)

浮窗位置计算逻辑

设置浮窗的位置时,我们需要明确其 左侧、顶部 距离视口边缘的距离(即 lefttop

借助 MDN官方文档 ,了解到 mouse相关事件 回调函数的 event 均包含两个参数 pageXpageY,二者分别是鼠标指针相对于整个文档的 X轴、Y轴 坐标。

mousedownmousemove 两个阶段中,我们均能获取到 pageXpageY。前者是一个 瞬时值,后者则是 动态值

计算「鼠标指针」与「浮窗边框」距离

在Vue2中,如何实现浮窗移动功能如何通过组件监听的形式,实现浮窗拖拽式移动呢?下面,跟随我的脚步,一步一步了解功能细节

mousedown 阶段,通过差值运算,便能得到 鼠标指针浮窗左侧、顶部 的距离

const { offsetLeft, offsetTop } = this.dialogDom

// 计算鼠标距离盒子左边框、上边框的距离
const x = e.pageX - offsetLeft
const y = e.pageY - offsetTop

计算浮窗 left、top

在Vue2中,如何实现浮窗移动功能如何通过组件监听的形式,实现浮窗拖拽式移动呢?下面,跟随我的脚步,一步一步了解功能细节

mousedown 阶段,我们得到了 鼠标指针浮窗左侧、顶部 的距离

而在 moudemove 阶段,我们可以实时获取到 pageXpageY

通过 e.pageX - xe.pageY - y 计算,便能得到 lefttop

完整代码

下方 setDragListener 方法,需在 浮窗显示 时调用。为避免获取DOM元素失败,还需使用 $nextTick 包裹

避免DOM元素紊乱,未在 mounted 生命周期中定义,而选择在 setDragListener 方法内部定义

// 拖拽监听设置
setDragListener() {
  // 获取DOM对象
  this.dialogWrapperDom = this.$refs.dialog.$el
  this.dialogDom = this.dialogWrapperDom.querySelector('.el-dialog')
  this.dragBodyDom = this.dialogDom.querySelector('.el-dialog__header')

  // 将函数定义到 DOM对象中,便于后期移除
  this.dragBodyDom.dragMouseDownEvent = (e) => {
    const { offsetLeft, offsetTop } = this.dialogDom

    // 计算鼠标距离盒子左边框、上边框的距离
    const x = e.pageX - offsetLeft
    const y = e.pageY - offsetTop

    // 移动函数(单独定义,便于移除)
    const move = (e) => {
      this.dialogDom.style.left = `${e.pageX - x}px`
      this.dialogDom.style.top = `${e.pageY - y}px`
    }

    // 鼠标按下后,添加 鼠标移动 监听事件
    this.dialogWrapperDom.addEventListener('mousemove', move)

    // 鼠标抬起后,移除 鼠标移动 监听事件
    this.dialogWrapperDom.addEventListener(
      'mouseup',
      () => {
        this.dialogWrapperDom.removeEventListener('mousemove', move)
      },
      // 执行一次后随即移除,避免累加 mouseup事件
      { once: true }
    )
  }

  // 设置鼠标按下操作
  this.dragBodyDom.addEventListener(
    'mousedown',
    this.dragBodyDom.dragMouseDownEvent
  )
},

浮窗关闭后,执行下方代码,移除监听,使浮窗返回原始位置

// 清除鼠标按下监听事件
this.dragBodyDom.removeEventListener(
  'mousedown',
  this.dragBodyDom.dragMouseDownEvent
)

// 清除拖拽时的位置信息
this.dialogDom.removeAttribute('style')

备注

浮窗基础样式

// 浮窗自身
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);

// 触发区域
cursor: move;
user-select: none;

现存问题

el-dialog 组件使用了上述方法,且配置了 destroy-on-close,会造成关闭时,闪回原始位置

在Vue2中,如何实现浮窗移动功能如何通过组件监听的形式,实现浮窗拖拽式移动呢?下面,跟随我的脚步,一步一步了解功能细节

destroy-on-close 会在关闭时,重置页面结构,进而导致原绑定在DOM元素上的 lefttop 属性丢失

为避免该问题,建议无特殊需求下,避免使用该属性

转载自:https://juejin.cn/post/7396934542958837771
评论
请登录