html5移動端新增了touchstart,touchmove,touchend事件,利用這3個事件,判斷手指的點擊和划動軌跡,我們可以封裝各種手勢的識別功能,
這3個事件和pc端的mousedown,mousemove,mouveup非常類似,不同的是touch事件可以有多個點擊的點,而鼠標每次只有一個點,我們即然是做組件封裝,就要考慮在pc上調試的情況,否則用手機調試非常不方便,通過對mouse事件的處理,可以一套代碼同時兼容pc端和移動端。
下面來逐步封裝一個滑動手勢(swipe)的組件
1.判斷是否觸摸屏
我們使用能力檢測,檢測是否支持touchstart事件,就可以知道是否是觸摸屏,因為觸摸事件可以通過document.ontouchstart=function(){} 這樣的方式定義,用in操作符判斷即可,對於win8,觸屏能力會在navigator對象中生成一個msPointerEnabled屬性。
if ('ontouchstart' in window || 'ontouchstart' in document) { //iOS & android supportsTouch = true; } else if(window.navigator.msPointerEnabled) { //Win8 supportsTouch = true; }
2.同時兼容鼠標和觸摸屏的事件綁定
我們根據上一步的判斷,如果支持toucestart就綁定對應的touchstart,touchmove,touchend事件,如果不支持,則綁定對應的3個鼠標事件
if(isSupportTouch()){ el.addEventListener('touchstart',touchStart); el.addEventListener('touchend',touchEnd); el.addEventListener('touchmove',touchMove); }else{ el.addEventListener('mousedown',touchStart); el.addEventListener('mouseup',touchEnd); el.addEventListener('mousemove',touchMove); }
3.獲取點擊的點位置信息(兼容鼠標和觸摸屏)
從事件參數中可以得到位置信息,如果是鼠標,則通過e.pageX,e.pageY獲取點擊位置相對於頁面根節點的坐標,如果是觸摸屏,則e.touches對象是一個點擊點位置的數組,包含多個手指的點擊位置,我們暫時只處理一只手指的情況,所以取e.touches[0].pageX,e.touches[0].pageY.
function touchStart(e){ var t=e.touches?e.touches[0]:e; startPoint={x:t.pageX,y:t.pageY}; }
4.判斷手指滑動方向
在toucemove事件中判斷手指划動,toucemove事件會連續觸發,為了過濾掉划動距離太短的無效滑動,我們可以判斷pageX和pageY和上一次位置的偏移量超過兩個像素才認為是有效事件,然后再判斷滑動方向,當前點擊位置的(x,y)坐標,減去上一個位置的(x,y)坐標,如果x軸的差值大,就認為是左右滑,如果是y軸的差值大就認為是上下滑,再進一步判斷差值 為正數則是左或上,差值為負數則為右或下。代碼如下:
function getSwipeDirection(x1, x2, y1, y2) { return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'left' : 'right') : (y1 - y2 >0 ? 'up' : 'down') }
5.jquery插件封裝
為了更方便使用,可以封裝成jquery插件,我們常說的jquery對象其實是指繼隨自jquery原型的對象,jquery的原型是指$.fn,只要擴展$.fn即可,
如$.fn.methodName=function(){//code}
或用$.fn.extend({
methodName:funciton(){//code}
})
完整代碼如下:
function TouchEvent(){ var self=this,element=$(this); var el=element[0],isTouching,isSwipe,startTime,startPoint,currentPoint; if(arguments.length>1){ var eventType=arguments[0]; } var callback=arguments[arguments.length-1]; function doAction(type,args){ args.type=type; if(eventType){ if(eventType==type){ callback.call(self,args); } }else{ callback.call(self,args); } } function getSwipeDirection(x1, x2, y1, y2) { return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'left' : 'right') : (y1 - y2 >0 ? 'up' : 'down') } function isSupportTouch(){ var supportsTouch = false; if ('ontouchstart' in window || 'ontouchstart' in document) { //iOS & android supportsTouch = true; } else if(window.navigator.msPointerEnabled) { //Win8 supportsTouch = true; } return supportsTouch; } function touchStart(e){ isTouching=true; startTime=new Date(); var t=e.touches?e.touches[0]:e; startPoint={x:t.pageX,y:t.pageY}; } function touchMove(e){ if(isTouching){ var t=e.touches?e.touches[0]:e; var p={x:t.pageX,y:t.pageY}; currentPoint=p; var x1=startPoint.x,x2=currentPoint.x,y1=startPoint.y,y2=currentPoint.y; if(Math.abs(x1-x2)>2 || Math.abs(y1-y2)>2){ isSwipe=true; var direction=getSwipeDirection(x1,x2,y1,y2); //console.log(direction); e.direction=direction; doAction("swipe",e); } } } function touchEnd(e){ isTouching=false; if(!isSwipe){ e["long"]=new Date()-startTime>1000; doAction("tap",e); //console.log("tap"); }else{ var x1=startPoint.x,x2=currentPoint.x,y1=startPoint.y,y2=currentPoint.y; var direction=getSwipeDirection(x1,x2,y1,y2); console.log(direction) doAction("swipeEnd",{direction:direction}); } isSwipe=false; } if(isSupportTouch()){ el.addEventListener('touchstart',touchStart); el.addEventListener('touchend',touchEnd); el.addEventListener('touchmove',touchMove); //el.addEventListener('touchcancel',actionFinsh); }else{ el.addEventListener('mousedown',touchStart); el.addEventListener('mouseup',touchEnd); el.addEventListener('mousemove',touchMove); } } $.fn.touchEvent = TouchEvent;