手势识别与事件库 Touch.js若干问题及解决方法

来源:互联网 发布:js attr style 编辑:程序博客网 时间:2024/06/05 20:42

Touch.js是移动设备上的手势识别与事件库, 由百度云Clouda团队维护,也是在百度内部广泛使用的开发工具.
Touch.js的代码已托管于github并开源,希望能帮助国内更多的开发者学习和开发出优秀的App产品.
Touch.js手势库专为移动设备设计, 请在Webkit内核浏览器中使用.

极速CDN

<script src="http://code.baidu.com/touch-0.2.14.min.js"></script>

Examples

//swipe example touch.on('.target', 'swipeleft swiperight', function(ev){    console.log("you have done", ev.type);});

向左及向右 滑动更灵活的解决方案

参考牛人博客的代码,据说相当好用。

  1(function(global,doc,factoryFn){  2     //初始化toucher主方法  3     var factory = factoryFn(global,doc);  4       5     //提供window.util.toucher()接口  6     global.util = global.util || {};  7     global.util.toucher = global.util.toucher || factory;  8       9     //提供CommonJS规范的接口 10     global.define && define(function(require,exports,module){ 11         //对外接口 12         return factory; 13     }); 14 })(this,document,function(window,document){ 15     /** 16      * 判断是否拥有某个class 17      */ 18     function hasClass(dom,classSingle){ 19         return dom.className.match(new RegExp('(\\s|^)' + classSingle +'(\\s|$)')); 20     } 21  22     /** 23      * @method 向句柄所在对象增加事件监听 24      * @description 支持链式调用 25      *  26      * @param string 事件名 27      * @param [string] 事件委托至某个class(可选) 28      * @param function 符合条件的事件被触发时需要执行的回调函数  29      *  30      */ 31     function ON(eventStr,a,b){ 32         this._events = this._events || {}; 33         var className,fn; 34         if(typeof(a) == 'string'){ 35             className = a.replace(/^\./,''); 36             fn = b; 37         }else{ 38             className = null; 39             fn = a; 40         } 41         //检测callback是否合法,事件名参数是否存在· 42         if(typeof(fn) == 'function' && eventStr && eventStr.length){ 43             var eventNames = eventStr.split(/\s+/); 44             for(var i=0,total=eventNames.length;i<total;i++){ 45              46                 var eventName = eventNames[i]; 47                 //事件堆无该事件,创建一个事件堆 48                 if(!this._events[eventName]){ 49                     this._events[eventName] = []; 50                 } 51                 this._events[eventName].push({ 52                     'className' : className, 53                     'fn' : fn 54                 }); 55             } 56         } 57  58         //提供链式调用的支持 59         return this; 60     } 61  62     /** 63      * @method 事件触发器 64      * @description 根据事件最原始被触发的target,逐级向上追溯事件绑定 65      *  66      * @param string 事件名 67      * @param object 原生事件对象 68      */ 69     function EMIT(eventName,e){ 70         this._events = this._events || {}; 71         //事件堆无该事件,结束触发 72         if(!this._events[eventName]){ 73             return 74         } 75         //记录尚未被执行掉的事件绑定 76         var rest_events = this._events[eventName]; 77          78         //从事件源:target开始向上冒泡 79         var target = e.target; 80         while (1) { 81             //若没有需要执行的事件,结束冒泡 82             if(rest_events.length ==0){ 83                 return; 84             } 85             //若已经冒泡至顶,检测顶级绑定,结束冒泡 86             if(target == this.dom || !target){ 87                 //遍历剩余所有事件绑定 88                 for(var i=0,total=rest_events.length;i<total;i++){ 89                     var classStr = rest_events[i]['className']; 90                     var callback = rest_events[i]['fn']; 91                     //未指定事件委托,直接执行 92                     if(classStr == null){ 93                         event_callback(eventName,callback,target,e); 94                     } 95                 } 96                 return; 97             } 98              99             //当前需要校验的事件集100             var eventsList = rest_events;101             //置空尚未执行掉的事件集102             rest_events = [];103 104             //遍历事件所有绑定105             for(var i=0,total=eventsList.length;i<total;i++){106                 var classStr = eventsList[i]['className'];107                 var callback = eventsList[i]['fn'];108                 //符合事件委托,执行109                 if(hasClass(target,classStr)){110                     //返回false停止事件冒泡及后续事件,其余继续执行111                     if(event_callback(eventName,callback,target,e) == false){112                         return113                     }114                 }else{115                     //不符合执行条件,压回到尚未执行掉的列表中116                     rest_events.push(eventsList[i]);117                 }118             }119             //向上冒泡120             target = target.parentNode;121         }122     }123     124     /**125      * 执行绑定的回调函数,并创建一个事件对象126      * @param[string]事件名127      * @param[function]被执行掉的函数128      * @param[object]指向的dom129      * @param[object]原生event对象130      */131     function event_callback(name,fn,dom,e){132         var touch = e.touches.length ? e.touches[0] : {};133         134         var newE = {135             'type' : name,136             'target' : e.target,137             'pageX' : touch.clientX || 0,138             'pageY' : touch.clientY || 0139         };140         //为swipe事件增加交互初始位置及移动距离141         if(name == 'swipe' && e.startPosition){142             newE.startX = e.startPosition['pageX'],143             newE.startY = e.startPosition['pageY'],144             newE.moveX = newE.pageX - newE.startX,145             newE.moveY = newE.pageY - newE.startY146         }147         var call_result = fn.call(dom,newE);148         //若绑定方法返回false,阻止浏览器默认事件149         if(call_result == false){150             e.preventDefault();151             e.stopPropagation();152         }153         154         return call_result;155     }156     /**157      * 判断swipe方向158      */159     function swipeDirection(x1, x2, y1, y2) {160         return Math.abs(x1 - x2) >=161             Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down')162     }163 164     /**165      * 监听原生的事件,主动触发模拟事件166      * 167      */168     function eventListener(DOM){169         var this_touch = this;170 171         //轻击开始时间172         var touchStartTime = 0;173         174         //记录上一次点击时间175         var lastTouchTime = 0;176         177         //记录初始轻击的位置178         var x1,y1,x2,y2;179         180         //轻击事件的延时器181         var touchDelay;182         183         //测试长按事件的延时器184         var longTap;185         186         //记录当前事件是否已为等待结束的状态187         var isActive = false;188         //记录有坐标信息的事件189         var eventMark = null;190         //单次用户操作结束191         function actionOver(e){192             isActive = false;193             clearTimeout(longTap);194             clearTimeout(touchDelay);195         }196         197         //触屏开始198         function touchStart(e){199             //缓存事件200             eventMark = e;201         202             x1 = e.touches[0].pageX;203             y1 = e.touches[0].pageY;204             x2 = 0;205             y2 = 0;206             isActive = true;207             touchStartTime = new Date();208             EMIT.call(this_touch,'swipeStart',e);209             //检测是否为长按210             clearTimeout(longTap);211             longTap = setTimeout(function(){212                 actionOver(e);213                 //断定此次事件为长按事件214                 EMIT.call(this_touch,'longTap',e);215             },500);216         }217         //触屏结束218         function touchend(e){219             //touchend中,拿不到坐标位置信息,故使用全局保存下的事件220             EMIT.call(this_touch,'swipeEnd',eventMark);221             if(!isActive){222                 return223             }224             var now = new Date();225             if(now - lastTouchTime > 260){226                 touchDelay = setTimeout(function(){227                     //断定此次事件为轻击事件228                     actionOver();229                     EMIT.call(this_touch,'singleTap',eventMark);230                 },250);231             }else{232                 clearTimeout(touchDelay);233                 actionOver(e);234                 //断定此次事件为连续两次轻击事件235                 EMIT.call(this_touch,'doubleTap',eventMark);236             }237             lastTouchTime = now;238         }239         240         //手指移动241         function touchmove(e){242             //缓存事件243             eventMark = e;244             //在原生事件基础上记录初始位置(为swipe事件增加参数传递)245             e.startPosition = {246                 'pageX' : x1,247                 'pageY' : y1248             };249             //断定此次事件为移动事件250             EMIT.call(this_touch,'swipe',e);251 252             if(!isActive){253                 return254             }255            x2 = e.touches[0].pageX256             y2 = e.touches[0].pageY257             if(Math.abs(x1-x2)>2 || Math.abs(y1-y2)>2){258                 //断定此次事件为移动手势259                 var direction = swipeDirection(x1, x2, y1, y2);260                 EMIT.call(this_touch,'swipe' + direction,e);261             }else{262                 //断定此次事件为轻击事件263                 actionOver(e);264                 EMIT.call(this_touch,'singleTap',e);265             }266             actionOver(e);267         }268 269         /**270          * 对开始手势的监听271          */272         DOM.addEventListener('touchstart',touchStart);273         DOM.addEventListener('MSPointerDown',touchStart);274         DOM.addEventListener('pointerdown',touchStart);275 276         /**277          * 对手势结束的监听(轻击)278          */279         DOM.addEventListener('touchend',touchend);280         DOM.addEventListener('MSPointerUp',touchend);281         DOM.addEventListener('pointerup',touchend);282 283         /**284          * 对移动手势的监听285          */286         DOM.addEventListener('touchmove',touchmove);287         DOM.addEventListener('MSPointerMove',touchmove);288         DOM.addEventListener('pointermove',touchmove);289 290         /**291          * 对移动结束的监听292          */293         DOM.addEventListener('touchcancel',actionOver);294         DOM.addEventListener('MSPointerCancel',actionOver);295         DOM.addEventListener('pointercancel',actionOver);296     }297     298     /**299      * touch类300      * 301      */302     function touch(DOM,param){303         var param = param || {};304 305         this.dom = DOM;306         //监听DOM原生事件307         eventListener.call(this,this.dom);308     }309     //拓展事件绑定方法310     touch.prototype['on'] = ON;311     312     //对外提供接口313     return function (dom){314         return new touch(dom);315     };316 });

解决除谷歌浏览器之外的浏览器兼容性方案

1:jquery mobile里面的touch组件。
1:百度的童鞋们实现的touch.js.网址也贴一下吧: http://touch.code.baidu.com/
3:参考大神的:

(function($) {  var options, Events, Touch;  options = {    x: 20,    y: 20  };  Events = ['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'tap', 'longTap', 'drag'];  Events.forEach(function(eventName) {    $.fn[eventName] = function() {      var touch = new Touch($(this), eventName);      touch.start();      if (arguments[1]) {        options = arguments[1]      }      return this.on(eventName, arguments[0])    }  });  Touch = function() {    var status, ts, tm, te;    this.target = arguments[0];    this.e = arguments[1]  };  Touch.prototype.framework = function(e) {    e.preventDefault();    var events;    if (e.changedTouches) events = e.changedTouches[0];    else events = e.originalEvent.touches[0];    return events  };  Touch.prototype.start = function() {    var self = this;    self.target.on("touchstart",    function(event) {      event.preventDefault();      var temp = self.framework(event);      var d = new Date();      self.ts = {        x: temp.pageX,        y: temp.pageY,        d: d.getTime()      }    });    self.target.on("touchmove",    function(event) {      event.preventDefault();      var temp = self.framework(event);      var d = new Date();      self.tm = {        x: temp.pageX,        y: temp.pageY      };      if (self.e == "drag") {        self.target.trigger(self.e, self.tm);        return      }    });    self.target.on("touchend",    function(event) {      event.preventDefault();      var d = new Date();      if (!self.tm) {        self.tm = self.ts      }      self.te = {        x: self.tm.x - self.ts.x,        y: self.tm.y - self.ts.y,        d: (d - self.ts.d)      };      self.tm = undefined;      self.factory()    })  };  Touch.prototype.factory = function() {    var x = Math.abs(this.te.x);    var y = Math.abs(this.te.y);    var t = this.te.d;    var s = this.status;    if (x < 5 && y < 5) {      if (t < 300) {        s = "tap"      } else {        s = "longTap"      }    } else if (x < options.x && y > options.y) {      if (t < 250) {        if (this.te.y > 0) {          s = "swipeDown"        } else {          s = "swipeUp"        }      } else {        s = "swipe"      }    } else if (y < options.y && x > options.x) {      if (t < 250) {        if (this.te.x > 0) {          s = "swipeLeft"        } else {          s = "swipeRight"        }      } else {        s = "swipe"      }    }    if (s == this.e) {      this.target.trigger(this.e);      return    }  }})(window.jQuery || window.Zepto);
0 0