前面几篇文章,我跟大家分享了JavaScript的一些基础知识,这篇文章,将会进入第一个实战环节:利用前面几章的所涉及到的知识,封装一个拖拽对象。为了能够帮助大家了解更多的方式与进行对比,我会使用三种不同的方式来实现拖拽。
•不封装对象直接实现;
•利用原生JavaScript封装拖拽对象;
•通过扩展jQuery来实现拖拽对象。
本文的例子会放置于codepen.io[1]中,供大家在阅读时直接查看。如果对于codepen不了解的同学,可以花点时间稍微了解一下。
拖拽的实现过程会涉及到非常多的实用小知识,因此为了巩固我自己的知识积累,也为了大家能够学到更多的知识,我会尽量详细的将一些细节分享出来,相信大家认真阅读之后,一定能学到一些东西。
1、如何让一个DOM元素动起来
我们常常会通过修改元素的top,left,translate
来其的位置发生改变。在下面的例子中,每点击一次按钮,对应的元素就会移动5px。大家可点击查看。
点击查看一个让元素动起来的小例子[2]
由于修改一个元素top/left值会引起页面重绘,而translate不会,因此从性能优化上来判断,我们会优先使用translate属性。
2、如何获取当前浏览器支持的transform兼容写法
transform是css3的属性,当我们使用它时就不得不面对兼容性的问题。不同版本浏览器的兼容写法大致有如下几种:
['transform', 'webkitTransform', 'MozTransform', 'msTransform', 'OTransform']
因此我们需要判断当前浏览器环境支持的transform属性是哪一种,方法如下:
// 获取当前浏览器支持的transform兼容写法 function getTransform() { var transform = '', divStyle = document.createElement('div').style, // 可能涉及到的几种兼容性写法,通过循环找出浏览器识别的那一个 transformArr = ['transform', 'webkitTransform', 'MozTransform', 'msTransform', 'OTransform'], i = 0, len = transformArr.length; for (; i < len; i++) { if (transformArr[i] in divStyle) { // 找到之后立即返回,结束函数 return transform = transformArr[i]; } } // 如果没有找到,就直接返回空字符串 return transform; }
该方法用于获取浏览器支持的transform属性。如果返回的为空字符串,则表示当前浏览器并不支持transform,这个时候我们就需要使用left,top值来改变元素的位置。如果支持,就改变transform的值。
3、 如何获取元素的初始位置
我们首先需要获取到目标元素的初始位置,因此这里需要一个专门用来获取元素样式的功能函数。
但是获取元素样式在IE浏览器与其他浏览器有一些不同,我们需要一个兼容性的写法。
function getStyle(elem, property) { // ie通过currentStyle来获取元素的样式,其他浏览器通过getComputedStyle来获取 return document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(elem, false)[property] : elem.currentStyle[property]; }
有了这个方法之后,就可以开始动手写获取目标元素初始位置的方法了。
function getTargetPos(elem) { var pos = { x: 0, y: 0 }; var transform = getTransform(); if (transform) { var transformValue = getStyle(elem, transform); if (transformValue == 'none') { elem.style[transform] = 'translate(0, 0)'; return pos; } else { var temp = transformValue.match(/-?\d+/g); return pos = { x: parseInt(temp[4].trim()), y: parseInt(temp[5].trim()) } } } else { if (getStyle(elem, 'position') == 'static') { elem.style.position = 'relative'; return pos; } else { var x = parseInt(getStyle(elem, 'left') ? getStyle(elem, 'left') : 0); var y = parseInt(getStyle(elem, 'top') ? getStyle(elem, 'top') : 0); return pos = { x: x, y: y } } } }
在拖拽过程中,我们需要不停的设置目标元素的新位置,这样它才会移动起来,因此我们需要一个设置目标元素位置的方法。
// pos = { x: 200, y: 100 } function setTargetPos(elem, pos) { var transform = getTransform(); if (transform) { elem.style[transform] = 'translate(' + pos.x + 'px, ' + pos.y + 'px)'; } else { elem.style.left = pos.x + 'px'; elem.style.top = pos.y + 'px'; } return elem; }
4、我们需要用到哪些事件?
在pc上的浏览器中,结合mousedown、mousemove、mouseup
这三个事件可以帮助我们实现拖拽。
•mousedown
鼠标按下时触发•mousemove
鼠标按下后拖动时触发•mouseup
鼠标松开时触发
而在移动端,分别与之对应的则是
touchstart、touchmove、touchend
。
当我们将元素绑定这些事件时,有一个事件对象将会作为参数传递给回调函数,通过事件对象,我们可以获取到当前鼠标的精确位置,鼠标位置信息是实现拖拽的关键。
事件对象十分重要,其中包含了非常多的有用的信息,这里我就不扩展了,大家可以在函数中将事件对象打印出来查看其中的具体属性,这个方法对于记不清事件对象重要属性的童鞋非常有用。
5、拖拽的原理
当事件触发时,我们可以通过事件对象获取到鼠标的精切位置。这是实现拖拽的关键。当鼠标按下(mousedown触发)时,我们需要记住鼠标的初始位置与目标元素的初始位置,我们的目标就是实现当鼠标移动时,目标元素也跟着移动,根据常理我们可以得出如下关系:
移动后的鼠标位置 - 鼠标初始位置 = 移动后的目标元素位置 - 目标元素的初始位置
如果鼠标位置的差值我们用dis来表示,那么目标元素的位置就等于:
移动后目标元素的位置 = dis + 目标元素的初始位置
通过事件对象,我们可以精确的知道鼠标的当前位置,因此当鼠标拖动(mousemove)时,我们可以不停的计算出鼠标移动的差值,以此来求出目标元素的当前位置。这个过程,就实现了拖拽。
而在鼠标松开(mouseup)结束拖拽时,我们需要处理一些收尾工作。详情见代码。
6、 我又来推荐思维导图辅助写代码了
常常有新人朋友跑来问我,如果逻辑思维能力不强,能不能写代码做前端。我的答案是:能。因为借助思维导图,可以很轻松的弥补逻辑的短板。而且比在自己头脑中脑补逻辑更加清晰明了,不易出错。
上面第六点我介绍了原理,因此如何做就显得不是那么难了,而具体的步骤,则在下面的思维导图中明确给出,我们只需要按照这个步骤来写代码即可,试试看,一定很轻松。