前言
这里我根据我实现的一个拖拽组件,简单把主要实现原理和注意点说明一下,总有人会有需求😄
原理
- 监听拖拽元素的 mousedown 事件
onMouseDown(event) {
// 监听事件
// 为了避免不必要的事件监听,这里在 mousedown 事件中才去监听其他事件;
// 这里直接用 document 监听 mousemove 和 mouseup 是因为拖拽过程中我会设置拖拽
// 元素 css 属性 pointer-events: none;这样元素就监听不到事件了,这样做是为了能
// 更自由的拖拽,和找到我拖拽的目标位置存在什么元素方便处理业务
document.addEventListener('mousemove', this.onMoving)
document.addEventListener('mouseup', this.onMouseUp)
// 更新状态
this.dragging = true // 记录当前是否在拖拽中
this.style = null // 用于后续调整拖拽元素位置
this.prevX = event.clientX // 记录开始位置
this.prevY = event.clientY // 记录开始位置
// 存之前 body 的 cursor 属性,因为拖拽过程中为了生动,我会重新设置 css 属性 cursor: grab; 和
// cursor: grabbing; 所以这里记录一下,之后方便还原
this.prevCursor = document.body.style.cursor
// 存之前 body 的 user-select 属性样式,这个点很重要,下面会重新设置 body 的 user-select 属性
// 为 none 这样的话用户就不能在网页上选中区域了,如果不设置这个,你在拖拽过程中,滑动过的元素如果有
// 文本内容就会被选中,然后拖拽就会被中断,你可以试试注释下面的配置看效果就知道了
this.prevUserSelect = document.body.style.userSelect
document.body.style.cursor = 'grabbing'
document.body.style.userSelect = 'none'
}
- mousemove 事件中我们做了什么,下面定义了两个方法,find 和 onMoving,代码中会告诉你他们可以用来干嘛
onMoving(event) {
if (!this.dragging) return
// 更新位置,这里用最新的x、y位置减去之前记录的x、y位置,就是偏移量,也就是拖拽偏移位置
const translate3d = `
translate(${event.clientX - this.prevX}px, ${event.clientY - this.prevY}px)
`
this.style = { transform: translate3d }
// 寻找当前我所拖拽到的目标元素,因为之前拖拽元素设置了样式 pointer-events: none;
// 所以这里就可以很方便的找到我当前拖拽到的目标位置相关的信息,比如我可以用来做一个拖拽
// 元素和目标元素交换位置的需求,这样这个处理就很有用
this.find(event)
},
find(event) {
this.$emit('find', event)
}
- 最后 mouseup 事件中处理一下结尾逻辑就好
onMouseUp(event) {
// 销毁事件,记得结束拖拽后销毁事件
document.removeEventListener('mousemove', this.onMoving)
document.removeEventListener('mouseup', this.onMouseUp)
// 重置状态
this.dragging = false
this.style = { transition: 'all .2s ease' } // 配置一个过渡样式,让恢复拖拽时效果生动
// 还原一下之前的 body 样式
document.body.style.cursor = this.prevCursor
document.body.style.userSelect = this.prevUserSelect
}