vue2:拖拽容器设计一

319 阅读2分钟

首先:使用方式设计

1. 简单使用(MyComponent组件全区域可拖拽)

<DragWrapper>
  <MyComponent />   
</DragWrapper>

2. 在MyComponent组件外部提供可拖拽区域

<DragWrapper>
  <template #trigger>
    <div>拖拽区域</div>  
  </template>
  <MyComponent />  
</DragWrapper>

3. MyComponent内容提供了用来触发拖拽的区域

<DragWrapper contain-trigger>
  <MyComponent />
</DragWrapper>
  1. 需要设置DragWrapper组件的contain-trigger属性。

  2. MyComponent组件内部的trigger元素需要设置属性drag-trigger

    // MyComponent组件
    <div class="my-component">
      <div drag-trigger>可拖拽区域</div>
      <div>组件其它内容</div>
    </div>
    

实现分析

怎样实现拖拽?

  1. 拖拽效果,其实就是元素位置跟随按下的鼠标移动的过程。
  2. 使用transform: translate(x, y)来实现跟随鼠标位置变化的效果。
  3. 当鼠标松开时,使用绝对定位来修改元素的位置,复位translate。

事件的绑定与解绑

  1. trigger元素上绑定mousedown/touchstart事件。
  2. 触发mousedown/touchstart事件时,为document绑定mousemove/touchmove事件和mouseup/touchend事件。
  3. mouseup/touchend事件触发后,解绑mousemove/touchmove事件和mouseup/touchend事件。

其它一些注意点和问题

1. 页面上有iframe

如果页面上有iframe,鼠标移动到iframe上时,documentmousemove/mouseup等事件不会被监听到。

原因

鼠标移动到iframe上时,只会触发iframe内部的鼠标事件,不会传递给外部的document。事件不是从外部的document开始捕获的,而是从iframe内的document开始捕获,所以外部的鼠标事件监听无法被触发。

解决方案

当开始拖拽时(触发mousedown事件),为页面上的所有iframe设置css属性pointer-events: none禁止事件穿透。拖拽完成后再移除。

2. requestAnimationFrame()使用注意点

必须同时对mousemovemouseup事件的回调使用raf。仅对mousemove事件设置raf,有时会出现一些问题,mousemove事件的一些回调在mouseup事件的之后调用。

更多功能支持

1. 可以锁定在一个轴向上移动。

通过props: lockAxis

<DragWrapper lockAxis="x">
  <MyComponent />   
</DragWrapper>

2. 可指定拖拽的区域(父级容器)

<div class="container">
  <DragWrapper restricted-area>
    <MyComponent />
  </DragWrapper>
</div>

通过restricted-area属性表示被限制拖拽区域,默认限制的活动区域是父级元素。

也可以通过在祖先元素上添加属性drag-restricted-area