【此处为铺垫内容,可跳过】 随着 Web 前端的不断发展,各类各样的前端规范和新知识、新技术层出不穷,极大地拓展了开发者的操做空间,也大大地提高了用户体验。而随着移动端的不断发展,在移动端,人机交互方式发生了很大转变,新的人机交互方式对提供给用户的 “动做” 有了更高要求,例如拖拽功能,就是在移动端会常常接触到的功能。此文就详细地讲解一下,一个简单的拖拽功能的实现(拖拽元素可改变,浏览器窗口边界检测)。html
为了让各位对此文内容有更深入的掌握,须要掌握以下知识。前端
一、clientX,clientY 属性浏览器
clientX( clientY ) 事件属性返回当事件被触发时鼠标指针相对于浏览器当前可视窗口的水平(垂直)坐标。
注意:不包括工具栏和滚动条
二、offsetTop,offsetLeft 属性函数
offsetTop ( offsetLeft ),指的是子元素距离其父元素的上边框(左边框)的偏移量,在不一样的浏览器中其值不一样,且与父元素的 position 属性( position: static; 除外)有关。在不一样浏览以及不一样 position 下的具体值,读者能够自行查阅相关资料,因其内容较多,就不展开论述了。
三、clientWidth,clientHeight属性工具
Element.clientWidth( Element.clientHeight )属性表示元素的内部宽度,以像素计。该属性包括内边距,但不包括垂直滚动条(若是有)、边框和外边距。该属性值会被四舍五入为一个整数。若是你须要一个小数值,可以使用 element.getBoundingClientRect() 。
四、setCapture,releaseCapture 方法this
MDN 对 SetCapture() 函数的说明为:“该函数在属于当前线程的指定窗口里设置鼠标捕获。一旦窗口捕获了 鼠标,全部鼠标输入都针对该窗口,不管光标是否在窗口的边界内。同一时刻只能有一个窗口捕获鼠标。若是鼠标光标在另外一个线程建立的窗口上,只有当鼠标键按下时系统才将鼠标输入指向指定的窗口。”线程
通俗来说,举个栗子:一只羊被一根有弹性的绳子( SetCapture )拴在木桩,羊能够在绳子能够延展的范围内 随意活动,但永远没法摆脱绳子的束缚。除非有其余因素致使绳子断了(使用了 ReleaseCapture 或点击了其余窗口)。指针
ReleaseCapture() 用来释放鼠标捕获,当再也不须要继续得到鼠标消息就要应该调用 ReleaseCapture() 释放掉,不然别的线程想捕获鼠标事件就会失败。注意:SetCapture() 和 ReleaseCapture() 必须成对出现。code
若是想对元素进行拖拽,那么必须使用三个事件,而且这三个事件的使用顺序不能打乱。htm
一、onmousedown:鼠标按下事件 二、onmousemove:鼠标移动事件 三、onmouseup:鼠标抬起事件
拖拽的基本原理就是根据鼠标的移动来移动被拖拽的元素。鼠标的移动也就是 x、y 坐标的变化;元素的移动就是元素 position 属性的 top 和 left 值的改变。固然,并非任什么时候候移动鼠标都要形成元素的移动,而应该判断鼠标左键的状态是否为按下状态,是不是在可拖拽的元素上按下的。具体过程以下:
拖拽状态 = false 鼠标在元素上按下以后 { 拖拽状态 = true 设置鼠标捕获 记录下鼠标的 x,y 坐标 记录下元素的 x,y 坐标 } 鼠标在元素上移动时 { 若拖拽状态为 false 就什么也不作 若是拖拽状态是 true,那么 元素的 y 坐标 = 如今鼠标 y - 原来鼠标 y + 原来元素 y 元素的 x 坐标 = 如今鼠标 x - 原来鼠标 x + 原来元素 x } 鼠标抬起时 { 拖拽状态 = false }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>原生JS实现元素拖拽</title> <style> *{ margin: 0; height: 0; } #dragDiv{ top: 0; left: 0; width: 100px; height: 100px; cursor: move; /*鼠标呈拖拽状*/ position: absolute; /*设置绝对定位,脱离文档流,便于拖拽时计算坐标*/ background-color: red; } </style> </head> <body> <div id="dragDiv"></div> <script> window.onload = function(){ var oDiv = document.getElementById("dragDiv"); // 获取到要拖拽的元素 drag(oDiv); // 调用本身封装的拖拽函数 function drag(obj){ obj.onmousedown = function(e){ var e = e || window.event; // 兼容 IE // 鼠标点击物体那一刻相对于物体左侧边框的距离=点击时的位置相对于浏览器 // 最左边的距离-物体左边框相对于浏览器最左边的距离,纵向同理 var divX = e.clientX - this.offsetLeft; var divY = e.clientY - this.offsetTop; if(obj.setCapture){ obj.setCapture(); // 修复低版本 IE bug } document.onmousemove = function(e){ var e = e || window.event; var disX = e.clientX - divX; var disY = e.clientY - divY; // 控制拖拽物体的范围只能在浏览器视窗内,不容许出现滚动条或拖出可视区域 if ( disX < 0 ) { disX = 0; } else if ( disX > document.documentElement.clientWidth - obj.offsetWidth ) { disX = document.documentElement.clientWidth - obj.offsetWidth; } if ( disY < 0 ) { disY = 0; } else if ( disY > document.documentElement.clientHeight - obj.offsetHeight ) { disY = document.documentElement.clientHeight - obj.offsetHeight; } // 移动时从新获得物体的距离,解决拖动时出现晃动现象 obj.style.top = disY + "px"; obj.style.left = disX + "px"; document.onmouseup = function(){ // 鼠标抬起时再也不移动 // 预防鼠标弹起来后还会循环(即预防鼠标放上去的时候还会移动) document.onmousedown = document.onmousemove = null; if( obj.releaseCapture ){ obj.releaseCapture(); // 修复低版本 IE bug } } } } } } </script> </body> </html>
注意事项
一、onmousedown 事件中的操做对象为拖拽元素,而 onmousemove 和 onmouseup 事件中的操做对象为 document,这是由于,点击某物体时,用须要拖拽的对象便可,onmousemove 和 onmouseup 是全局区域,也就是整个文档通用,应该使用 document 对象而不是被拖拽的对象(不然,采用拖拽对象时物体只能往右方或下方移动)二、之因此使用 setCapture() 和 releaseCapture(),其目的是为了修复低版本 IE 的 bug。在低版本 IE 下,当咱们在要拖动的元素上,按下鼠标按钮拖动时,当拖动过快,或者是超出浏览器的文档窗口时,拖动对象身上的 onmousedown 事件就会失效。在 Chrome 咱们能够为 doucment 绑定 onmouseout 事件来判断是否发生这样的状况,可是 IE 下却行不通,因此最好的解决办法就时为要拖动的元素对象锁定鼠标事件,在拖动后再解除事件锁定。在本例中,这两个方法用于 onmousedown 和 onmouseup 中。
三、另外,在 Firefox 中有类似的功能,它们分别是:
- captureEvents ( Event.eventType )
- releaseEvents ( Event.eventType )
虽然元素的拖拽算是一个比较基础的知识点,但在实现的过程当中,有许多细节须要注意,例如计算坐标的时候,对那几个属性的了解程度,再例如,事件的触发顺序,还有,IE 中的事件获取, setCapture() 和 releaseCapture() 等。
虽然 H5 直接提供了拖拽 API,但为了兼容性,小伙伴们仍是须要用 js 去处理的。上例中,虽然对拖拽作了必定的兼容性处理和封装,拖拽对象能够是 div,图片,文字等,但总的来讲,是一个比较基础的实现,但有了这个原型,小伙伴们能够根据本身的需求,再加以封装和拓展,例如限定拖拽方向、范围、速度等等。有任何疑问或建议,能够在评论区留言哦,转载请注明出处。