先上菜,看這個模塊的最後一段代碼,一看就明白。android
['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'tap', 'singleTap', 'longTap' ].forEach(function(eventName) { $.fn[eventName] = function(callback) { return this.on(eventName, callback) } })
tap
—元素tap的時候觸發。singleTap
and doubleTap
— 這一對事件能夠用來檢測元素上的單擊和雙擊。(若是你不須要檢測單擊、雙擊,使用 tap
代替)。longTap
— 當一個元素被按住超過750ms觸發。swipe
, swipeLeft
, swipeRight
, swipeUp
, swipeDown
— 當元素被劃過期觸發。(可選擇給定的方向)已經廣爲人知了,由於 click 事件有 200~300 ms 的延遲,爲了更快的響應,最好用 Zepto 提供的 tap 事件,但是zepto的tap會點透,我想說它的設計必然會點透。git
看看zepto中tap的實現,其中有個setTimeout(function(){},0)。若是咱們引入了fastclick,其實元素的click會比tap要先觸發,緣由嗎就是那個setTimeout,singleTap延遲250毫秒,這個就更慢了。fastclick寫得更好,稍後再來分學習學習,而後分析分析。github
tapTimeout = setTimeout(function() { // trigger universal 'tap' with the option to cancelTouch() // (cancelTouch cancels processing of single vs double taps for faster 'tap' response) var event = $.Event('tap') //能夠cancelAll event.cancelTouch = cancelAll //觸發tap touch.el.trigger(event) // trigger double tap immediately // 以前touchstart 裏面有判斷和上次點擊時間的時間差 if (touch.isDoubleTap) { if (touch.el) touch.el.trigger('doubleTap') touch = {} } // trigger single tap after 250ms of inactivity else { //延遲250毫秒 touchTimeout = setTimeout(function() { touchTimeout = null if (touch.el) touch.el.trigger('singleTap') touch = {} }, 250) } }, 0)
android 4.4 swipe 事件會有問題,須要再修復,不過修復也很差使,慎重!! 因此通常仍是本身實現,不用zepto的。web
關於點透,zepto把事件綁在document上,裏面的tap沒有組織冒泡,上機元素必然會觸發相關事件,因此點透真是預料之中。那麼解決也就簡單,阻止冒泡就是了,具體怎麼作,兩種方式吧!!修改源碼加上阻止冒泡,或者給裏面元素綁定事件,手動阻止冒泡。學習
沒啥可說的直接上代碼。this
; (function($) { //保存滑動過程當中的一些數據 var touch = {}, //記錄各類timeout touchTimeout, tapTimeout, swipeTimeout, longTapTimeout, //默認超過750毫秒就觸發longTap longTapDelay = 750, gesture //判斷滑動的方向 上下左右。首先判斷x和y哪一個滑動距離大,而後在根據x肯定是左右,根據一肯定上下 function swipeDirection(x1, x2, y1, y2) { return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down') } function longTap() { //置爲null longTapTimeout = null if (touch.last) { //觸發touch.el的longTap touch.el.trigger('longTap') //重置touch對象 touch = {} } } //取消longTapTimeout定時器, function cancelLongTap() { if (longTapTimeout) clearTimeout(longTapTimeout) longTapTimeout = null } //所有取消 重置 function cancelAll() { if (touchTimeout) clearTimeout(touchTimeout) if (tapTimeout) clearTimeout(tapTimeout) if (swipeTimeout) clearTimeout(swipeTimeout) if (longTapTimeout) clearTimeout(longTapTimeout) touchTimeout = tapTimeout = swipeTimeout = longTapTimeout = null touch = {} } /* * 是否爲移動webkit內核的事件 */ function isPrimaryTouch(event) { return (event.pointerType == 'touch' || event.pointerType == event.MSPOINTER_TYPE_TOUCH) && event.isPrimary } /* * IE 10 和11的事件 */ function isPointerEventType(e, type) { return (e.type == 'pointer' + type || e.type.toLowerCase() == 'mspointer' + type) } //兩次$(document) 難道開始就不能 var $d=$(document) 嗎!! $(document).ready(function() { var now, delta, deltaX = 0, deltaY = 0, firstTouch, _isPointerType if ('MSGesture' in window) { gesture = new MSGesture() gesture.target = document.body } $(document) .bind('MSGestureEnd', function(e) { var swipeDirectionFromVelocity = e.velocityX > 1 ? 'Right' : e.velocityX < -1 ? 'Left' : e.velocityY > 1 ? 'Down' : e.velocityY < -1 ? 'Up' : null; if (swipeDirectionFromVelocity) { //觸發元素相關事件 MSGestureEnd,不清楚是神馬東西,估計就是一些奇葩特有的,IE!!!! touch.el.trigger('swipe') touch.el.trigger('swipe' + swipeDirectionFromVelocity) } }) /** * MSPointerDown pointerdown IE10 11 * touchstart 移動Webkit */ .on('touchstart MSPointerDown pointerdown', function(e) { //踢出奇葩的,不是標準webkit或者IE10 11 直接返回 if ((_isPointerType = isPointerEventType(e, 'down')) && !isPrimaryTouch(e)) return //我只知道正常狀況都是 e.touches[0] firstTouch = _isPointerType ? e : e.touches[0] //一個手指在操做 if (e.touches && e.touches.length === 1 && touch.x2) { //重置x2 和y2 // Clear out touch movement data if we have it sticking around // This can occur if touchcancel doesn't fire due to preventDefault, etc. touch.x2 = undefined touch.y2 = undefined } //初始觸發時間 now = Date.now() //當前的now減去上次觸發時間,取得差值 //若是上次時間沒有就是此次的時間, 那麼delta就是0 delta = now - (touch.last || now) //取得touch el touch.el = $('tagName' in firstTouch.target ? firstTouch.target : firstTouch.target.parentNode) //touchTimeout存在即清除 清除 延遲250毫秒執行的singleTap touchTimeout && clearTimeout(touchTimeout) //取到開始的 pageX 、pageY 賦值給x一、y1 touch.x1 = firstTouch.pageX touch.y1 = firstTouch.pageY //上次touchstart 和此次touchstart時間相隔在250毫秒之內,就是isDoubleTap if (delta > 0 && delta <= 250) touch.isDoubleTap = true //賦值touch.last,下次觸發減去這個時間,來計算是否觸發doubleTap touch.last = now //設置longtap事件 longTapTimeout = setTimeout(longTap, longTapDelay) // adds the current touch contact for IE gesture recognition //這個表示不懂,咱們從不兼容IE if (gesture && _isPointerType) gesture.addPointer(e.pointerId); }) .on('touchmove MSPointerMove pointermove', function(e) { //touchstart、touchmove、touchend 都用到這個判斷,拷貝三次,我以爲改簡單封裝下,雖然代碼很少,可是也有必要。^_^ if ((_isPointerType = isPointerEventType(e, 'move')) && !isPrimaryTouch(e)) return firstTouch = _isPointerType ? e : e.touches[0] //一旦發生touchmove 就取消cancelLongTap。 我以爲應該仍是有個居來判斷吧!!!!好比不超過5像素也沒問題 cancelLongTap() //取得此時的pageX、pageY touch.x2 = firstTouch.pageX touch.y2 = firstTouch.pageY //計算出和touchstart的差值 deltaX += Math.abs(touch.x1 - touch.x2) deltaY += Math.abs(touch.y1 - touch.y2) /** * 修復 android 4.4 swipe 事件 * https://github.com/madrobby/zepto/issues/315#issuecomment-8386027 */ //當頁面有滾動條的時候,這個修復就啃爹了。沒有滾動條卻是能夠隨便修復。 /*if ((touch.x2 && Math.abs(touch.x1 - touch.x2) > 10) && (touch.y2 && Math.abs(touch.y1 - touch.y2) < 10)) e.preventDefault()*/ /*if (touch.x2 && Math.abs(touch.x1 - touch.x2) > 10) e.preventDefault()*/ }) .on('touchend MSPointerUp pointerup', function(e) { if ((_isPointerType = isPointerEventType(e, 'up')) && !isPrimaryTouch(e)) return //這個就不說了, cancelLongTap() // swipe 默認超過就觸發swipe相關事件 if ((touch.x2 && Math.abs(touch.x1 - touch.x2) > 30) || (touch.y2 && Math.abs(touch.y1 - touch.y2) > 30)) swipeTimeout = setTimeout(function() { //觸發swpie 和'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown' touch.el.trigger('swipe') touch.el.trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2))) //重置touch touch = {} }, 0) // normal tap else if ('last' in touch) // don't fire tap when delta position changed by more than 30 pixels, // for instance when moving to a point and back to origin // 滑動距離小於30像素 if (deltaX < 30 && deltaY < 30) { // delay by one tick so we can cancel the 'tap' event if 'scroll' fires // ('tap' fires before 'scroll') // 爲何要搞個setTimeout呢,由於發生滾動的時候要取消這些事件的執行,直接執行了就取消不了了 tapTimeout = setTimeout(function() { // trigger universal 'tap' with the option to cancelTouch() // (cancelTouch cancels processing of single vs double taps for faster 'tap' response) var event = $.Event('tap') //能夠cancelAll event.cancelTouch = cancelAll //觸發tap touch.el.trigger(event) // trigger double tap immediately // 以前touchstart 裏面有判斷和上次點擊時間的時間差 if (touch.isDoubleTap) { if (touch.el) touch.el.trigger('doubleTap') touch = {} } // trigger single tap after 250ms of inactivity else { //延遲250毫秒 touchTimeout = setTimeout(function() { touchTimeout = null if (touch.el) touch.el.trigger('singleTap') touch = {} }, 250) } }, 0) } else { touch = {} } //重置這兩個數據 deltaX = deltaY = 0 }) // when the browser window loses focus, // for example when a modal dialog is shown, // cancel all ongoing events .on('touchcancel MSPointerCancel pointercancel', cancelAll) // scrolling the window indicates intention of the user // to scroll, not tap or swipe, so cancel all ongoing events $(window).on('scroll', cancelAll) }) ; ['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'tap', 'singleTap', 'longTap' ].forEach(function(eventName) { $.fn[eventName] = function(callback) { return this.on(eventName, callback) } }) })(Zepto)