成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

讀Zepto源碼之Touch模塊

Prasanta / 2961人閱讀

摘要:在觸發(fā)事件前,先將保存定時(shí)器的變量釋放,如果對(duì)象中存在,則觸發(fā)事件,保存的是最后觸摸的時(shí)間。如果有觸發(fā)的定時(shí)器,清除定時(shí)器即可阻止事件的觸發(fā)。其實(shí)就是清除所有相關(guān)的定時(shí)器,最后將對(duì)象設(shè)置為。進(jìn)入時(shí),立刻清除定時(shí)器的執(zhí)行。

大家都知道,因?yàn)闅v史原因,移動(dòng)端上的點(diǎn)擊事件會(huì)有 300ms 左右的延遲,Zeptotouch 模塊解決的就是移動(dòng)端點(diǎn)擊延遲的問(wèn)題,同時(shí)也提供了滑動(dòng)的 swipe 事件。

讀 Zepto 源碼系列文章已經(jīng)放到了github上,歡迎star: reading-zepto

源碼版本

本文閱讀的源碼為 zepto1.2.0

GitBook

《reading-zepto》

實(shí)現(xiàn)的事件
;["swipe", "swipeLeft", "swipeRight", "swipeUp", "swipeDown",
  "doubleTap", "tap", "singleTap", "longTap"].forEach(function(eventName){
  $.fn[eventName] = function(callback){ return this.on(eventName, callback) }
})

從上面的代碼中可以看到,Zepto 實(shí)現(xiàn)了以下的事件:

swipe: 滑動(dòng)事件

swipeLeft: 向左滑動(dòng)事件

swipeRight: 向右滑動(dòng)事件

swipeUp: 向上滑動(dòng)事件

swipeDown: 向下滑動(dòng)事件

doubleTap: 屏幕雙擊事件

tap: 屏幕點(diǎn)擊事件,比 click 事件響應(yīng)更快

singleTap: 屏幕單擊事件

longTap: 長(zhǎng)按事件

并且為每個(gè)事件都注冊(cè)了快捷方法。

內(nèi)部方法 swipeDirection
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")
}

返回的是滑動(dòng)的方法。

x1x軸 起點(diǎn)坐標(biāo), x2x軸 終點(diǎn)坐標(biāo), y1y軸 起點(diǎn)坐標(biāo), y2y軸 終點(diǎn)坐標(biāo)。

這里有多組三元表達(dá)式,首先對(duì)比的是 x軸y軸 上的滑動(dòng)距離,如果 x軸 的滑動(dòng)距離比 y軸 大,則為左右滑動(dòng),否則為上下滑動(dòng)。

x軸 上,如果起點(diǎn)位置比終點(diǎn)位置大,則為向左滑動(dòng),返回 Left ,否則為向右滑動(dòng),返回 Right

y軸 上,如果起點(diǎn)位置比終點(diǎn)位置大,則為向上滑動(dòng),返回 Up ,否則為向下滑動(dòng),返回 Down 。

longTap
var touch = {},
    touchTimeout, tapTimeout, swipeTimeout, longTapTimeout,
    longTapDelay = 750,
    gesture
function longTap() {
  longTapTimeout = null
  if (touch.last) {
    touch.el.trigger("longTap")
    touch = {}
  }
}

觸發(fā)長(zhǎng)按事件。

touch 對(duì)象保存的是觸摸過(guò)程中的信息。

在觸發(fā) longTap 事件前,先將保存定時(shí)器的變量 longTapTimeout 釋放,如果 touch 對(duì)象中存在 last ,則觸發(fā) longTap 事件, last 保存的是最后觸摸的時(shí)間。最后將 touch 重置為空對(duì)象,以便下一次使用。

cancelLongTap
function cancelLongTap() {
  if (longTapTimeout) clearTimeout(longTapTimeout)
  longTapTimeout = null
}

撤銷(xiāo) longTap 事件的觸發(fā)。

如果有觸發(fā) longTap 的定時(shí)器,清除定時(shí)器即可阻止 longTap 事件的觸發(fā)。

最后同樣需要將 longTapTimeout 變量置為 null ,等待垃圾回收。

cancelAll
function cancelAll() {
  if (touchTimeout) clearTimeout(touchTimeout)
  if (tapTimeout) clearTimeout(tapTimeout)
  if (swipeTimeout) clearTimeout(swipeTimeout)
  if (longTapTimeout) clearTimeout(longTapTimeout)
  touchTimeout = tapTimeout = swipeTimeout = longTapTimeout = null
  touch = {}
}

清除所有事件的執(zhí)行。

其實(shí)就是清除所有相關(guān)的定時(shí)器,最后將 touch 對(duì)象設(shè)置為 null 。

isPrimaryTouch
function isPrimaryTouch(event){
  return (event.pointerType == "touch" ||
          event.pointerType == event.MSPOINTER_TYPE_TOUCH)
  && event.isPrimary
}

是否為主觸點(diǎn)。

當(dāng) pointerTypetouch 并且 isPrimarytrue 時(shí),才為主觸點(diǎn)。 pointerType 可為 touchpenmouse ,這里只處理手指觸摸的情況。

isPointerEventType
function isPointerEventType(e, type){
  return (e.type == "pointer"+type ||
          e.type.toLowerCase() == "mspointer"+type)
}

觸發(fā)的是否為 pointerEvent 。

在低版本的移動(dòng)端 IE 瀏覽器中,只實(shí)現(xiàn)了 PointerEvent ,并沒(méi)有實(shí)現(xiàn) TouchEvent ,所以需要這個(gè)來(lái)判斷。

事件觸發(fā) 整體分析
$(document).ready(function(){
    var now, delta, deltaX = 0, deltaY = 0, firstTouch, _isPointerType

    $(document)
      .bind("MSGestureEnd", function(e){
        ...
      })
      .on("touchstart MSPointerDown pointerdown", function(e){
        ...
      })
      .on("touchmove MSPointerMove pointermove", function(e){
        ...
      })
      .on("touchend MSPointerUp pointerup", function(e){
        ...
      })
      
      .on("touchcancel MSPointerCancel pointercancel", cancelAll)

    $(window).on("scroll", cancelAll)

先來(lái)說(shuō)明幾個(gè)變量,now 用來(lái)保存當(dāng)前時(shí)間, delta 用來(lái)保存兩次觸摸之間的時(shí)間差, deltaX 用來(lái)保存 x軸 上的位移, deltaY 來(lái)用保存 y軸 上的位移, firstTouch 保存初始觸摸點(diǎn)的信息, _isPointerType 保存是否為 pointerEvent 的判斷結(jié)果。

從上面可以看到, Zepto 所觸發(fā)的事件,是從 touch 、 pointer 或者 IE 的 guesture 事件中,根據(jù)不同情況計(jì)算出來(lái)的。這些事件都綁定在 document 上。

IE Gesture 事件的處理

IE 的手勢(shì)使用,需要經(jīng)歷三步:

創(chuàng)建手勢(shì)對(duì)象

指定目標(biāo)元素

指定手勢(shì)識(shí)別時(shí)需要處理的指針

if ("MSGesture" in window) {
  gesture = new MSGesture()
  gesture.target = document.body
}

這段代碼包含了前兩步。

on("touchstart MSPointerDown pointerdown", function(e){
  ...
  if (gesture && _isPointerType) gesture.addPointer(e.pointerId)
}

這段是第三步,用 addPointer 的方法,指定需要處理的指針。

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) {
    touch.el.trigger("swipe")
    touch.el.trigger("swipe"+ swipeDirectionFromVelocity)
  }
})

接下來(lái)就是分析手勢(shì)了,Gesture 里只處理 swipe 事件。

velocityXvelocityY 分別為 x軸y軸 上的速率。這里以 1-1 為臨界點(diǎn),判斷 swipe 的方向。

如果 swipe 的方向存在,則觸發(fā) swipe 事件,同時(shí)也觸發(fā)帶方向的 swipe 事件。

start
on("touchstart MSPointerDown pointerdown", function(e){
  if((_isPointerType = isPointerEventType(e, "down")) &&
     !isPrimaryTouch(e)) return
  firstTouch = _isPointerType ? e : e.touches[0]
  if (e.touches && e.touches.length === 1 && touch.x2) {
    touch.x2 = undefined
    touch.y2 = undefined
  }
  now = Date.now()
  delta = now - (touch.last || now)
  touch.el = $("tagName" in firstTouch.target ?
               firstTouch.target : firstTouch.target.parentNode)
  touchTimeout && clearTimeout(touchTimeout)
  touch.x1 = firstTouch.pageX
  touch.y1 = firstTouch.pageY
  if (delta > 0 && delta <= 250) touch.isDoubleTap = true
  touch.last = now
  longTapTimeout = setTimeout(longTap, longTapDelay)
  if (gesture && _isPointerType) gesture.addPointer(e.pointerId)
})
過(guò)濾掉非觸屏事件
if((_isPointerType = isPointerEventType(e, "down")) &&
   !isPrimaryTouch(e)) return
firstTouch = _isPointerType ? e : e.touches[0]

這里還將 isPointerEventType 的判斷結(jié)果保存到了 _isPointerType 中,用來(lái)判斷是否為 PointerEvent

這里的判斷其實(shí)就是只處理 PointerEventTouchEvent ,并且 TouchEventisPrimary 必須為 true 。

因?yàn)?TouchEvent 支持多點(diǎn)觸碰,這里只取觸碰的第一點(diǎn)存入 firstTouch 變量。

重置終點(diǎn)坐標(biāo)
if (e.touches && e.touches.length === 1 && touch.x2) {
  touch.x2 = undefined
  touch.y2 = undefined
}

如果還需要記錄,終點(diǎn)坐標(biāo)是需要更新的。

正常情況下,touch 對(duì)象會(huì)在 touchEnd 或者 cancel 的時(shí)候清空,但是如果用戶自己調(diào)用了 preventDefault 等,就可能會(huì)出現(xiàn)沒(méi)有清空的情況。

這里有一點(diǎn)不太明白,為什么只會(huì)在 touches 單點(diǎn)操作的時(shí)候才清空呢?多個(gè)觸碰點(diǎn)的時(shí)候不需要清空嗎?

記錄觸碰點(diǎn)的信息
now = Date.now()
delta = now - (touch.last || now)
touch.el = $("tagName" in firstTouch.target ?
             firstTouch.target : firstTouch.target.parentNode)
touchTimeout && clearTimeout(touchTimeout)
touch.x1 = firstTouch.pageX
touch.y1 = firstTouch.pageY

now 用來(lái)保存當(dāng)前時(shí)間。

delta 用來(lái)保存兩次點(diǎn)擊時(shí)的時(shí)間間隔,用來(lái)處理雙擊事件。

touch.el 用來(lái)保存目標(biāo)元素,這里有個(gè)判斷,如果 target 不是標(biāo)簽節(jié)點(diǎn)時(shí),取父節(jié)點(diǎn)作為目標(biāo)元素。這會(huì)在點(diǎn)擊偽類(lèi)元素時(shí)出現(xiàn)。

如果 touchTimeout 存在,則清除定時(shí)器,避免重復(fù)觸發(fā)。

touch.x1touch.y1 分別保存 x軸 坐標(biāo)和 y軸 坐標(biāo)。

雙擊事件
if (delta > 0 && delta <= 250) touch.isDoubleTap = true

可以很清楚地看到, Zepto 將兩次點(diǎn)擊的時(shí)間間隔小于 250ms 時(shí),作為 doubleTap 事件處理,將 isDoubleTap 設(shè)置為 true

長(zhǎng)按事件
touch.last = now
longTapTimeout = setTimeout(longTap, longTapDelay)

touch.last 設(shè)置為當(dāng)前時(shí)間。這樣就可以記錄兩次點(diǎn)擊時(shí)的時(shí)間差了。

同時(shí)開(kāi)始長(zhǎng)按事件定時(shí)器,從上面的代碼可以看到,長(zhǎng)按事件會(huì)在 750ms 后觸發(fā)。

move
on("touchmove MSPointerMove pointermove", function(e){
  if((_isPointerType = isPointerEventType(e, "move")) &&
     !isPrimaryTouch(e)) return
  firstTouch = _isPointerType ? e : e.touches[0]
  cancelLongTap()
  touch.x2 = firstTouch.pageX
  touch.y2 = firstTouch.pageY

  deltaX += Math.abs(touch.x1 - touch.x2)
  deltaY += Math.abs(touch.y1 - touch.y2)
})

move 事件處理了兩件事,一是記錄終點(diǎn)坐標(biāo),一是計(jì)算起點(diǎn)到終點(diǎn)之間的位移。

要注意這里還調(diào)用了 cancelLongTap 清除了長(zhǎng)按定時(shí)器,避免長(zhǎng)按事件的觸發(fā)。因?yàn)橛幸苿?dòng),肯定就不是長(zhǎng)按了。

end
on("touchend MSPointerUp pointerup", function(e){
  if((_isPointerType = isPointerEventType(e, "up")) &&
     !isPrimaryTouch(e)) return
  cancelLongTap()

  if ((touch.x2 && Math.abs(touch.x1 - touch.x2) > 30) ||
      (touch.y2 && Math.abs(touch.y1 - touch.y2) > 30))

    swipeTimeout = setTimeout(function() {
      if (touch.el){
        touch.el.trigger("swipe")
        touch.el.trigger("swipe" + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2)))
      }
      touch = {}
    }, 0)

  else if ("last" in touch)
  
    if (deltaX < 30 && deltaY < 30) {
    
      tapTimeout = setTimeout(function() {
        
        var event = $.Event("tap")
        event.cancelTouch = cancelAll
        
        if (touch.el) touch.el.trigger(event)

        if (touch.isDoubleTap) {
          if (touch.el) touch.el.trigger("doubleTap")
          touch = {}
        }

        else {
          touchTimeout = setTimeout(function(){
            touchTimeout = null
            if (touch.el) touch.el.trigger("singleTap")
            touch = {}
          }, 250)
        }
      }, 0)
    } else {
      touch = {}
    }
  deltaX = deltaY = 0

})
swipe
cancelLongTap()
if ((touch.x2 && Math.abs(touch.x1 - touch.x2) > 30) ||
    (touch.y2 && Math.abs(touch.y1 - touch.y2) > 30))

  swipeTimeout = setTimeout(function() {
    if (touch.el){
      touch.el.trigger("swipe")
      touch.el.trigger("swipe" + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2)))
    }
    touch = {}
  }, 0)

進(jìn)入 end 時(shí),立刻清除 longTap 定時(shí)器的執(zhí)行。

可以看到,起點(diǎn)和終點(diǎn)的距離超過(guò) 30 時(shí),會(huì)被判定為 swipe 滑動(dòng)事件。

在觸發(fā)完 swipe 事件后,立即觸發(fā)對(duì)應(yīng)方向上的 swipe 事件。

注意,swipe 事件并不是在 end 系列事件觸發(fā)時(shí)立即觸發(fā)的,而是設(shè)置了一個(gè) 0ms 的定時(shí)器,讓事件異步觸發(fā),這個(gè)有什么用呢?后面會(huì)講到。

tap
else if ("last" in touch)
  
  if (deltaX < 30 && deltaY < 30) {

    tapTimeout = setTimeout(function() {

      var event = $.Event("tap")
      event.cancelTouch = cancelAll

      if (touch.el) touch.el.trigger(event)

    }, 0)
  } else {
    touch = {}
  }
deltaX = deltaY = 0

終于看到重點(diǎn)了,首先判斷 last 是否存在,從 start 中可以看到,如果觸發(fā)了 start , last 肯定是存在的,但是如果觸發(fā)了長(zhǎng)按事件,touch 對(duì)象會(huì)被清空,這時(shí)不會(huì)再觸發(fā) tap 事件。

如果不是 swipe 事件,也不存在 last ,則只將 touch 清空,不觸發(fā)任何事件。

在最后會(huì)將 deltaXdeltaY 重置為 0 。

觸發(fā) tap 事件時(shí),會(huì)在 event 中加了 cancelTouch 方法,外界可以通過(guò)這個(gè)方法取消所有事件的執(zhí)行。

這里同樣用了 setTimeout 異步觸發(fā)事件。

doubleTap
if (touch.isDoubleTap) {
  if (touch.el) touch.el.trigger("doubleTap")
  touch = {}
}

這個(gè) isDoubleTapstart 時(shí)確定的,上面已經(jīng)分析過(guò)了,在 end 的時(shí)候觸發(fā) doubleTap 事件。

因此,可以知道,在觸發(fā) doubleTap 事件之前會(huì)觸發(fā)兩次 tap 事件。

singleTap
touchTimeout = setTimeout(function(){
  touchTimeout = null
  if (touch.el) touch.el.trigger("singleTap")
  touch = {}
}, 250)

如果不是 doubleTap ,會(huì)在 tap 事件觸發(fā)的 250ms 后,觸發(fā) singleTap 事件。

cancel
.on("touchcancel MSPointerCancel pointercancel", cancelAll)

在接受到 cancel 事件時(shí),調(diào)用 cancelAll 方法,取消所有事件的觸發(fā)。

scroll
$(window).on("scroll", cancelAll)

從前面的分析可以看到,所有的事件觸發(fā)都是異步的。

因?yàn)樵?scroll 的時(shí)候,肯定是只想響應(yīng)滾動(dòng)的事件,異步觸發(fā)是為了在 scroll 的過(guò)程中和外界調(diào)用 cancelTouch 方法時(shí), 可以將事件取消。

系列文章

讀Zepto源碼之代碼結(jié)構(gòu)

讀Zepto源碼之內(nèi)部方法

讀Zepto源碼之工具函數(shù)

讀Zepto源碼之神奇的$

讀Zepto源碼之集合操作

讀Zepto源碼之集合元素查找

讀Zepto源碼之操作DOM

讀Zepto源碼之樣式操作

讀Zepto源碼之屬性操作

讀Zepto源碼之Event模塊

讀Zepto源碼之IE模塊

讀Zepto源碼之Callbacks模塊

讀Zepto源碼之Deferred模塊

讀Zepto源碼之Ajax模塊

讀Zepto源碼之Assets模塊

讀Zepto源碼之Selector模塊

參考

zepto touch 庫(kù)源碼分析

PointerEvent

Pointer events

TouchEvent

Touch

GestureEvent

MSGestureEvent

一步一步DIY zepto庫(kù),研究zepto源碼8--touch模塊

zepto源碼學(xué)習(xí)-06 touch

zepto源碼之touch.js

addPointer method.aspx)

License

署名-非商業(yè)性使用-禁止演繹 4.0 國(guó)際 (CC BY-NC-ND 4.0)

最后,所有文章都會(huì)同步發(fā)送到微信公眾號(hào)上,歡迎關(guān)注,歡迎提意見(jiàn):

作者:對(duì)角另一面

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/91962.html

相關(guān)文章

  • Zepto源碼Gesture模塊

    摘要:模塊基于上的事件的封裝,利用屬性,封裝出系列事件。這個(gè)判斷需要引入設(shè)備偵測(cè)模塊。然后是監(jiān)測(cè)事件,根據(jù)這三個(gè)事件,可以組合出和事件。其中變量對(duì)象和模塊中的對(duì)象的作用差不多,可以先看看讀源碼之模塊對(duì)模塊的分析。 Gesture 模塊基于 IOS 上的 Gesture 事件的封裝,利用 scale 屬性,封裝出 pinch 系列事件。 讀 Zepto 源碼系列文章已經(jīng)放到了github上,歡...

    coolpail 評(píng)論0 收藏0
  • Zepto源碼Stack模塊

    摘要:讀源碼系列文章已經(jīng)放到了上,歡迎源碼版本本文閱讀的源碼為改寫(xiě)原有的方法模塊改寫(xiě)了以上這些方法,這些方法在調(diào)用的時(shí)候,會(huì)為返回的結(jié)果添加的屬性,用來(lái)保存原來(lái)的集合。方法的分析可以看讀源碼之模塊。 Stack 模塊為 Zepto 添加了 addSelf 和 end 方法。 讀 Zepto 源碼系列文章已經(jīng)放到了github上,歡迎star: reading-zepto 源碼版本 本文閱讀的...

    crossea 評(píng)論0 收藏0
  • Zepto源碼Form模塊

    摘要:模塊處理的是表單提交。表單提交包含兩部分,一部分是格式化表單數(shù)據(jù),另一部分是觸發(fā)事件,提交表單。最終返回的結(jié)果是一個(gè)數(shù)組,每個(gè)數(shù)組項(xiàng)為包含和屬性的對(duì)象。否則手動(dòng)綁定事件,如果沒(méi)有阻止瀏覽器的默認(rèn)事件,則在第一個(gè)表單上觸發(fā),提交表單。 Form 模塊處理的是表單提交。表單提交包含兩部分,一部分是格式化表單數(shù)據(jù),另一部分是觸發(fā) submit 事件,提交表單。 讀 Zepto 源碼系列文章已...

    陳江龍 評(píng)論0 收藏0
  • Zepto源碼fx_methods模塊

    摘要:所以模塊依賴(lài)于模塊,在引入前必須引入模塊。原有的方法分析見(jiàn)讀源碼之樣式操作方法首先調(diào)用原有的方法,將元素顯示出來(lái),這是實(shí)現(xiàn)動(dòng)畫(huà)的基本條件。如果沒(méi)有傳遞,或者為值,則表示不需要?jiǎng)赢?huà),調(diào)用原有的方法即可。 fx 模塊提供了 animate 動(dòng)畫(huà)方法,fx_methods 利用 animate 方法,提供一些常用的動(dòng)畫(huà)方法。所以 fx_methods 模塊依賴(lài)于 fx 模塊,在引入 fx_m...

    junbaor 評(píng)論0 收藏0
  • Zepto源碼IOS3模塊

    摘要:用法與參數(shù)要理解這段代碼,先來(lái)看一下的用法和參數(shù)用法參數(shù)回調(diào)函數(shù),有如下參數(shù)上一個(gè)回調(diào)函數(shù)返回的值或者是初始值當(dāng)前值當(dāng)前值在數(shù)組中的索引調(diào)用的數(shù)組初始值,如果沒(méi)有提供,則為數(shù)組的第一項(xiàng)。接下來(lái),檢測(cè)回調(diào)函數(shù)是否為,如果不是,拋出類(lèi)型錯(cuò)誤。 IOS3 模塊是針對(duì) IOS 的兼容模塊,實(shí)現(xiàn)了兩個(gè)常用方法的兼容,這兩個(gè)方法分別是 trim 和 reduce 。 讀 Zepto 源碼系列文章...

    lavnFan 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<