最近项目上遇到个需求,即页面上存在一个方框,按下鼠标后开始拖动,松开鼠标后停止。在这里记录一下开发心得
思路
逻辑上不难,无非就是分如下两步
- 触发目标元素的
mousedown
事件时,获取鼠标相对元素的位置x、y
- 在页面的
mousemove
事件中,保证x、y永远不变
问题
思路理清了,主要解决点就是如何获取鼠标相对元素的位置
了。
可以通过事件对象e.pageX/e.pageY
来获取鼠标距离浏览器左/上的距离,然后只要获取拖拽元素距离浏览器左/上的距离,减去就可以得到所需位置了。
但这个距离怎么拿到呢?offsetLeft/offsetRight?
だめだよ、offset是相对于offsetParent
的边界的位置,即最近的拥有定位的父元素或者table,td,th,body元素,而我需要获取相对于页面边界的距离,这显然是无法使用的。
getBoundingClientRect
详细参考,这里只做简单描述。
该方法通过元素调用element.getBoundingClientRect()
,不需要传递参数。返回一个对象,包括了8个描述元素的属性,分别为:
- width:元素宽度
- height:元素高度
- left:元素左边界距离视窗左边界的距离
- top:元素上边界距离视窗上边界的距离
- right:元素右边界距离视窗左边界的距离
- bottom:元素下边界距离视窗上边界的距离
- x和y:官方未给描述,从数值来看和top与left值相等,什么作用我暂且蒙在崛川雷鼓里
既然这样,只需要通过如下方式就可以获取鼠标在元素里的位置
1 2 3 4
| e.pageX - element.getBoundingClientRect().left
e.pageY - element.getBoundingClientRect().top
|
需求实现(vue)
template部分
1 2 3
| <div ref="mainRef" class="main"> <div ref="areaRef" class="area" @mousedown.prevent="areaMouseDown($event)"></div> </div>
|
js部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| export default { data() { return { areaStartMove: false, mainAttr: {}, areaAttr: {} } }, mounted() { document.body.addEventListener('mousemove', this.areaMouseMove) document.body.addEventListener('mouseup', this.areaMouseUp) }, destroyed() { document.body.removeEventListener('mousemove', this.areaMouseMove) document.body.removeEventListener('mouseup', this.areaMouseUp) }, methods: { areaMouseDown(e) { const areaElement = e.currentTarget this.areaStartMove = true this.mainAttr = this.$refs.mainRef.getBoundingClientRect() this.areaAttr = areaElement.getBoundingClientRect() this.areaAttr.clientX = e.pageX - this.areaAttr.left this.areaAttr.clientY = e.pageY - this.areaAttr.top }, areaMouseMove(e) { if (this.areaStartMove) { const areaElement = this.$refs.areaRef const top = e.pageY - this.mainAttr.top - this.areaAttr.clientY const left = e.pageX - this.mainAttr.left - this.areaAttr.clientX const maxTop = this.mainAttr.height - this.areaAttr.height const maxLeft = this.mainAttr.width - this.areaAttr.width if (top <= 0) { areaElement.style.top = '0' } else if (top >= maxTop) { areaElement.style.top = maxTop + 'px' } else { areaElement.style.top = top + 'px' } if (left <= 0) { areaElement.style.left = '0' } else if (left >= maxLeft) { areaElement.style.left = maxLeft + 'px' } else { areaElement.style.left = left + 'px' } } }, areaMouseUp() { if (this.areaStartMove) { this.areaStartMove = false } } } }
|
css部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| .main { position: relative;
.area { position: absolute; width: 100px; height: 40px; top: 0; left: 0; border: 2px dotted #ccc; border-radius: 2px; cursor: move; } }
|
参考
MDN-getBoundingClientRect