最近在作一個手機版的項目,而作手機網頁,那麼就會考慮到用輕量級庫,用jquery的話,會比較龐大,而咱們就選用 zepto.js 來作開發,但是在開發的時候要用到手勢事件(好比左右滑動,上下滑動),因而就在網上查了一下 zepto.js 的官網,發現有 touch 事件來模擬手勢事件,這個開發就會帶來便利,而不用去寫JS底層代碼;在網上搜羅了一下 zepto.js touch 庫,找到了很少 touch.js 的相關信息,真的感受不多(不知道是否是zepto.js不成熟的表現),終於在網上搜羅到了一個外國人寫的 touch.js 庫,而後就用在了項目上,可是在用到的時候,發現 swipeLeft ,swipeRight 這些事件會有一些 bug 並且不靈活,最後又在網上搜羅了一個國人修改的,可是最後用起來仍是不咋滴(敏感度有點差),最後在羣裏問問大神們,發現有個高手他說他本身寫了一個庫,因而乎我就跟他聊了幾下,而後他告訴他的代碼放在了 github 上,那麼我就上去抄了下來,用在了項目上,頓時感受手勢事件靈活了,不錯,真心不錯,仍是得謝謝那個哥們,好了,說了那麼多就上代碼吧,把這個庫壓縮一下,引用在項目上就可使用 swipeLeft,swipeRight 這些事件了,因爲本人只用了向左向右的滑動事件,就沒有測試向上向下滑動了,若是使用過這些事件的童鞋,也能夠告訴我,讓我也好知道一下 ^_^... 使用方法,請查看這位高手的博客 https://github.com/bh-lay/toucher/blob/master/touch.htmlhtml
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 return 113 } 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]指向的dom 129 * @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 || 0 139 }; 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.startY 146 } 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 return 223 } 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' : y1 248 }; 249 //判定這次事件爲移動事件 250 EMIT.call(this_touch,'swipe',e); 251 252 if(!isActive){ 253 return 254 } 255 x2 = e.touches[0].pageX 256 y2 = e.touches[0].pageY 257 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 });