首先:使用方式设计
1. 简单使用(MyComponent组件全区域可拖拽)
<DragWrapper>
<MyComponent />
</DragWrapper>
2. 在MyComponent组件外部提供可拖拽区域
<DragWrapper>
<template #trigger>
<div>拖拽区域</div>
</template>
<MyComponent />
</DragWrapper>
3. MyComponent内容提供了用来触发拖拽的区域
<DragWrapper contain-trigger>
<MyComponent />
</DragWrapper>
-
需要设置
DragWrapper组件的contain-trigger属性。 -
MyComponent组件内部的trigger元素需要设置属性drag-trigger。// MyComponent组件 <div class="my-component"> <div drag-trigger>可拖拽区域</div> <div>组件其它内容</div> </div>
实现分析
怎样实现拖拽?
- 拖拽效果,其实就是元素位置跟随按下的鼠标移动的过程。
- 使用
transform: translate(x, y)来实现跟随鼠标位置变化的效果。 - 当鼠标松开时,使用绝对定位来修改元素的位置,复位translate。
事件的绑定与解绑
- 在
trigger元素上绑定mousedown/touchstart事件。 - 触发
mousedown/touchstart事件时,为document绑定mousemove/touchmove事件和mouseup/touchend事件。 mouseup/touchend事件触发后,解绑mousemove/touchmove事件和mouseup/touchend事件。
其它一些注意点和问题
1. 页面上有iframe
如果页面上有iframe,鼠标移动到iframe上时,document的mousemove/mouseup等事件不会被监听到。
原因
鼠标移动到iframe上时,只会触发iframe内部的鼠标事件,不会传递给外部的document。事件不是从外部的document开始捕获的,而是从iframe内的document开始捕获,所以外部的鼠标事件监听无法被触发。
解决方案
当开始拖拽时(触发mousedown事件),为页面上的所有iframe设置css属性pointer-events: none禁止事件穿透。拖拽完成后再移除。
2. requestAnimationFrame()使用注意点
必须同时对mousemove和mouseup事件的回调使用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。