我按思考顺序写一下,想看最终效果的直接拉动到最后~
一开始我先写出了这个代码~
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> body{ user-select: none; } .a, .b, .c { position: absolute; width: 100px; height: 100px; } .a { background: red; } .b { background: blue; } .c { background: green; } </style></head><body> <div style="left:0;top:0" class="draggable a"></div> <div style="left:0;top:0" class="draggable b"></div> <div style="left:0;top:0" class="draggable c"></div> <script> var maxIndex = 2; window.addEventListener('mousedown', e => { if (e.target.matches('.draggable')) { var movingEl = e.target movingEl.style.zIndex = maxIndex++ var lastX = e.pageX var lastY = e.pageY window.addEventListener('mousemove', function move(e) { if (e.buttons == 0) { window.removeEventListener('mousemove', move) } var diffX = e.pageX - lastX var diffY = e.pageY - lastY lastX = e.pageX lastY = e.pageY movingEl.style.left = parseInt(movingEl.style.left) + diffX + 'px' movingEl.style.top = parseInt(movingEl.style.top) + diffY + 'px' }) } }) </script></body></html>可以看到,初步效果是实现了的,我这里使用了事件代理,这样的话在浏览器里直接添加元素,也能够进行拖拽,而且也减少了DOM绑定事件。
CSS方面,user-select:none这个属性是防止鼠标变成 🚫(禁止) 这个图标,从而导致无法选中。
JS方面,要在mousemove事件下注意保存上次事件发生时的鼠标坐标,即为lastX与lastY。用e.buttons来判断是否中止拖拽,贴一段MDN的代码:
MouseEvent.buttons指示事件触发时哪些鼠标按键被按下。每一个按键都用一个给定的数(见下文)表示。如果同时多个按键被按下,buttons 的值为各键对应值做与计算(+)后的值。例如,如果右键(2)和滚轮键(4)被同时按下,buttons 的值为 2 + 4 = 6。
0: 没有按键或者是没有初始化1: 鼠标左键2: 鼠标右键4: 鼠标滚轮或者是中键8: 第四按键 (通常是“浏览器后退”按键)16: 第五按键 (通常是“浏览器前进”)
但是,这样子写看起来是实现了,但是还是有BUG的,比如:
竟然可以拖动到窗口外面,这被测试发现不就是BUG嘛!!!赶紧修改一下JS
var maxIndex = 2; window.addEventListener('mousedown', e => { if (e.target.matches('.draggable')) { var movingEl = e.target movingEl.style.zIndex = maxIndex++ var lastX = e.pageX var lastY = e.pageY window.addEventListener('mousemove', function move(e) { if (e.buttons == 0) { window.removeEventListener('mousemove', move) } var diffX = e.pageX - lastX var diffY = e.pageY - lastY lastX = e.pageX lastY = e.pageY var left = parseInt(movingEl.style.left) + diffX var top = parseInt(movingEl.style.top) + diffY if (left < 0) { left = 0 }else if (left > innerWidth - 100) { left = innerWidth - 100 } if (top < 0) { top= 0 }else if (top > innerHeight - 100) { top = innerHeight - 100 } movingEl.style.left = left + 'px' movingEl.style.top = top + 'px' }) } })这样虽然拖不到视窗外面了,但是又发现了新BUG。。。
虽然元素移不出视口了,但是,第一种BUG,当鼠标在视口外并且处于按下状态,向视口内移动时,还没等到鼠标移动进视口,里面的元素倒是先动起来了。第二种BUG,鼠标在视口外松开,移动到视口内时,元素会突然跳变。
第一种情况,鼠标移动到视口外时,left本来也是同步增加的,但是因为我们设置了if语句让元素跑不出视口。而鼠标在视口外向视口内移动,元素也同步往相同方向移动,其实是正确的。所以我们的目的是让鼠标移到窗口内的元素时再进行移动。
第二种情况,由于在视口外松开鼠标,mousemove并不会触发,但是这里的程序会记录最后一次mousemove时的lastX和lastY,当鼠标从视口外再次移动到视口内时,mousemove事件最后一次在视口内触发(之后就被解绑),e.pageX和e.pageY会最后一次读取鼠标所在的位置,与之前在视口外记录的lastX和lastY相减得到负数,因此元素的left和top值就会变小,也就一下子发生了跳变。
因此,针对第一种情况,我们改变一下策略,记录鼠标的初始位置和元素的初始位置。元素移动的距离就是 :
元素的初始位置+鼠标相对鼠标本身初始位置的距离
第二种情况,我们之前都是在mousemove事件里触发解绑事件,而在视口外松开鼠标,mousemove又不会触发,造成解绑不及时,所以我们再绑定一个mouseup事件用来解绑。
代码修改如下:
var maxIndex = 2; window.addEventListener('mousedown', e => { if (e.target.matches('.draggable')) { var movingEl = e.target movingEl.style.zIndex = maxIndex++ var mouseInitX = e.pageX var mouseInitY = e.pageY var elInitX = parseInt(movingEl.style.left) var elInitY = parseInt(movingEl.style.top) var move window.addEventListener('mousemove', move = function (e) { if (e.buttons == 0) { window.removeEventListener('mousemove', move) } var diffX = e.pageX - mouseInitX var diffY = e.pageY - mouseInitY var left = elInitX + diffX var top = elInitY + diffY if (left < 0) { left = 0 }else if (left > innerWidth - 100) { left = innerWidth - 100 } if (top < 0) { top= 0 }else if (top > innerHeight - 100) { top = innerHeight - 100 } movingEl.style.left = left + 'px' movingEl.style.top = top + 'px' }) window.addEventListener('mouseup', e => { if (e.buttons == 0) { window.removeEventListener('mousemove', move) } }) } })这样就好了~~
---------------分割线---------------
我们还可以修改一下if判断做一个有趣的东西~
if (left < 100) { left = 0 }else if (left > innerWidth - 100) { left = innerWidth - 100}if (top < 100) { top= 0 }else if (top > innerHeight - 100) { top = innerHeight - 100}相当于一个磁铁吸附的感觉,相信这个功能大家肯定遇到过吧~