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;
