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

資訊專欄INFORMATION COLUMN

zepto touch 庫源碼分析

lentrue / 1025人閱讀

摘要:源碼分析不愿意下代碼的可以直接點這里地址首先贊一下的代碼注釋,非常全。屬性一個對象,包含了代表所有從上一次觸摸事件到此次事件過程中,狀態(tài)發(fā)生了改變的觸點的對象。

所謂 zepto 的 touch 其實就是指這個文件啦,可以看到區(qū)區(qū) 165 行(包括注釋)就完成了 swipe 和 tap 相關(guān)的事件實現(xiàn)。在正式開始分析源碼之前,我們先說說 touch 相關(guān)的幾個事件,因為無論是 tap 還是 swipe 都是基于他們的。

touch 相關(guān)事件

touchstart 觸摸屏幕的瞬間

touchmove 手指在屏幕上的移動過程一直觸發(fā)

touchend 離開屏幕的瞬間

touchcancel 觸摸取消(取決于瀏覽器實現(xiàn),并不常用)

觸摸屏下事件觸發(fā)順序是

touchstart -> touchmove -> touchend -> click
引入 touch 的背景

click事件在移動端上會有 300ms 的延遲,同時因為需要 長按,雙觸擊 等富交互,所以我們通常都會引入類似 zepto 這樣的庫。zepto 實現(xiàn)了"swipe", "swipeLeft", "swipeRight", "swipeUp", "swipeDown", "doubleTap", "tap", "singleTap", "longTap" 這樣一些功能。

zepto touch 源碼

我們直接看到 touch 源碼的 49 行,從這里開始就是上述事件的實現(xiàn)了。不難想到 MSGesture 是對 mobile ie 的實現(xiàn),本文不做討論。往下面看到 66 行,$(document).on("touchstart MSPointerDown pointerdown") 開始。

//判斷事件類型是否為 touch
if((_isPointerType = isPointerEventType(e, "down")) &&
  !isPrimaryTouch(e)) return
// touches 是觸摸點的數(shù)量
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 事件的 dom 
touch.el = $("tagName" in firstTouch.target ?
  firstTouch.target : firstTouch.target.parentNode)
// 如果 touchTimeout 存在就清理掉
touchTimeout && clearTimeout(touchTimeout)
// 記錄當(dāng)前坐標(biāo)
touch.x1 = firstTouch.pageX
touch.y1 = firstTouch.pageY
// 觸摸時間差小于 250ms 則為 DoubleTap
if (delta > 0 && delta <= 250) touch.isDoubleTap = true
// 記錄執(zhí)行后的時間
touch.last = now
// 留一個長觸摸,如果 touchmove 會把這個清理掉
longTapTimeout = setTimeout(longTap, longTapDelay)  

接下來是 $(document).on("touchmove MSPointerMove pointermove")

//判斷事件類型是否為 move
if((_isPointerType = isPointerEventType(e, "move")) &&
          !isPrimaryTouch(e)) return
firstTouch = _isPointerType ? e : e.touches[0]
// 一旦進入 move 就會清理掉 LongTap
cancelLongTap()
// 當(dāng)前手指坐標(biāo)
touch.x2 = firstTouch.pageX
touch.y2 = firstTouch.pageY
// x 軸和 y 軸的變化量 Math.abs 是取絕對值的意思
deltaX += Math.abs(touch.x1 - touch.x2)
deltaY += Math.abs(touch.y1 - touch.y2)

最后當(dāng)然就是 $(document).on("touchend MSPointerUp pointerup") 了,這個也是整個 touch 最為復(fù)雜的一部分。

if((_isPointerType = isPointerEventType(e, "up")) &&
          !isPrimaryTouch(e)) return
        cancelLongTap()

    // 如果是 swipe,x 軸或者 y 軸移動超過 30px
    if ((touch.x2 && Math.abs(touch.x1 - touch.x2) > 30) ||
        (touch.y2 && Math.abs(touch.y1 - touch.y2) > 30))

      swipeTimeout = setTimeout(function() {
        touch.el.trigger("swipe")
        // swipeDirection 是判斷 swipe 方向的
        touch.el.trigger("swipe" + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2)))
        touch = {}
      }, 0)

    // tap 事件
    else if ("last" in touch)
      if (deltaX < 30 && deltaY < 30) {
         // tapTimeout 是為了 scroll 的時候方便清除
        tapTimeout = setTimeout(function() {
          // 創(chuàng)建 tap 事件,并增加 cancelTouch 方法
          var event = $.Event("tap")
          event.cancelTouch = cancelAll
          touch.el.trigger(event)

          // 觸發(fā) DoubleTap
          if (touch.isDoubleTap) {
            if (touch.el) touch.el.trigger("doubleTap")
            touch = {}
          }

          // singleTap (這個概念是相對于 DoubleTap 的,可以看看我們在最初的那段源碼解析中有這樣一段 if (delta > 0 && delta <= 250) touch.isDoubleTap = true ,所以 250 ms 之后沒有二次觸摸的就算是 singleTap 了 
          else {
            touchTimeout = setTimeout(function(){
              touchTimeout = null
              if (touch.el) touch.el.trigger("singleTap")
              touch = {}
            }, 250)
          }
        }, 0)
      } else {
        touch = {}
      }
      deltaX = deltaY = 0

整個讀下來其實就是對 touchstart, touchmove, touchend 做了一些封裝和判斷,然后通過 zepto 自己的事件體系來注冊和觸發(fā)。

fastclick 對比 zepto

我們在聊到移動端 js 方案的時候很容易聽到這兩者,但我個人認(rèn)為這兩者是無法對比的。原因如下:zepto 是一個移動端的 js 庫,而 fastclick 專注于 click 在移動端的觸發(fā)問題。fastclick 的 github 主頁上有一句話是“Polyfill to remove click delays on browsers with touch UIs”,翻譯過來就是干掉移動端 click 延時的補丁。這個延時就是我們在引入 touch 的背景里提到過。

fastclick 源碼分析

不愿意下代碼的可以直接點這里github地址首先贊一下 fastclick 的代碼注釋,非常全。

fastclick 的使用非常簡單,直接 FastClick.attach(document.body); 一句話搞定。所以源碼分析就從 attach 方法來看吧,824 行

    FastClick.attach = function(layer, options) {
        // 返回 FastClick 實例 layer 是一個 element 通常是 document.body ,options 自然就是配置了
        return new FastClick(layer, options);
    };

接下來回到 23 行看到 FastClick 構(gòu)造函數(shù),

 // 方法綁定,兼容老版本的安卓
function bind(method, context) {
    return function() { return method.apply(context, arguments); };
}

var methods = ["onMouse", "onClick", "onTouchStart", "onTouchMove", "onTouchEnd", "onTouchCancel"];
var context = this;
for (var i = 0, l = methods.length; i < l; i++) {
    context[methods[i]] = bind(context[methods[i]], context);
}
 // 事件處理綁定部分
if (deviceIsAndroid) {
    layer.addEventListener("mouseover", this.onMouse, true);
    layer.addEventListener("mousedown", this.onMouse, true);
    layer.addEventListener("mouseup", this.onMouse, true);
}

layer.addEventListener("click", this.onClick, true);
layer.addEventListener("touchstart", this.onTouchStart, false);
layer.addEventListener("touchmove", this.onTouchMove, false);
layer.addEventListener("touchend", this.onTouchEnd, false);
layer.addEventListener("touchcancel", this.onTouchCancel, false);

 // stopImmediatePropagation 的兼容
 
 if (!Event.prototype.stopImmediatePropagation) {
    layer.removeEventListener = function(type, callback, capture) {
        var rmv = Node.prototype.removeEventListener;
        if (type === "click") {
            rmv.call(layer, type, callback.hijacked || callback, capture);
        } else {
            rmv.call(layer, type, callback, capture);
        }
    };

    layer.addEventListener = function(type, callback, capture) {
        var adv = Node.prototype.addEventListener;
        if (type === "click") {
            adv.call(layer, type, callback.hijacked || (callback.hijacked = function(event) {
                if (!event.propagationStopped) {
                    callback(event);
                }
            }), capture);
        } else {
            adv.call(layer, type, callback, capture);
        }
    };
}

// 如果 layer 有 onclick ,就把 onclick 轉(zhuǎn)換為 addEventListener 的方式
if (typeof layer.onclick === "function") {
    oldOnClick = layer.onclick;
    layer.addEventListener("click", function(event) {
        oldOnClick(event);
    }, false);
    layer.onclick = null;
}

FastClick.prototype.onTouchStart 和 zepto 一樣做了一些參數(shù)的紀(jì)錄,所以我這里就直接跳到 FastClick.prototype.onTouchEnd 看 fastclick 的核心。

FastClick.prototype.onTouchEnd = function(event) {
    var forElement, trackingClickStart, targetTagName, scrollParent, touch, targetElement = this.targetElement;

    if (!this.trackingClick) {
        return true;
    }
    // 防止 double tap 的時間間隔內(nèi) click 觸發(fā)
    if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {
        this.cancelNextClick = true;
        return true;
    }
    // 超出 longtap 的時間
    if ((event.timeStamp - this.trackingClickStart) > this.tapTimeout) {
        return true;
    }

    this.cancelNextClick = false;
    // 紀(jì)錄當(dāng)前時間
    this.lastClickTime = event.timeStamp;

    trackingClickStart = this.trackingClickStart;
    this.trackingClick = false;
    this.trackingClickStart = 0;

    if (deviceIsIOSWithBadTarget) {
        touch = event.changedTouches[0];
        targetElement = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset) || targetElement;
        targetElement.fastClickScrollParent = this.targetElement.fastClickScrollParent;
    }
    // 獲取 targetTagName 上面的一段是 targetTagName 兼容性
    targetTagName = targetElement.tagName.toLowerCase();
    // 解決 label for
    if (targetTagName === "label") {
        forElement = this.findControl(targetElement);
        if (forElement) {
            this.focus(targetElement);
            if (deviceIsAndroid) {
                return false;
            }

            targetElement = forElement;
        }
    } else if (this.needsFocus(targetElement)) {
        if ((event.timeStamp - trackingClickStart) > 100 || (deviceIsIOS && window.top !== window && targetTagName === "input")) {
            this.targetElement = null;
            return false;
        }
        // 解決 input focus 
        this.focus(targetElement);
        // 觸發(fā) sendClick
        this.sendClick(targetElement, event);

        if (!deviceIsIOS || targetTagName !== "select") {
            this.targetElement = null;
            event.preventDefault();
        }

        return false;
    }

    if (deviceIsIOS && !deviceIsIOS4) {
        scrollParent = targetElement.fastClickScrollParent;
        if (scrollParent && scrollParent.fastClickLastScrollTop !== scrollParent.scrollTop) {
            return true;
        }
    }
    // 最后就來觸發(fā) sendClick 了
    if (!this.needsClick(targetElement)) {
        event.preventDefault();
        this.sendClick(targetElement, event);
    }

    return false;
};

看完上面的代碼,我們馬上來解讀 FastClick.prototype.sendClick

FastClick.prototype.sendClick = function(targetElement, event) {
    var clickEvent, touch;
    // 拿觸摸的第一個手指
    touch = event.changedTouches[0];
    // 自定義 clickEvent 事件
    clickEvent = document.createEvent("MouseEvents");
    clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
    clickEvent.forwardedTouchEvent = true;
    // 觸發(fā) clickEvent 事件
    targetElement.dispatchEvent(clickEvent);
};

到此 fastclick 主要的東西我們就看得差不多了,代碼當(dāng)中不難看到 fastclick 的兼容性做的很好。它的主要目的是解決 click 在觸摸屏下的使用,引入之后再初始化一次就好了,很適合復(fù)用代碼的情景。

擴展講一下 touchEvent

本文中 zepto 和 fastclick 都有用到 touchEvent,但是 zepto 當(dāng)中用的是 e.touches 而 fastclick 卻用的是 e.targetTouches。這兩者的差異我們來一點一點地扒。

TouchEvent 是一類描述手指在觸摸平面(觸摸屏、觸摸板等)的狀態(tài)變化的事件。這類事件用于描述一個或多個觸點,使開發(fā)者可以檢測觸點的移動,觸點的增加和減少,等等。

屬性:

TouchEvent.changedTouches 一個 TouchList 對象,包含了代表所有從上一次觸摸事件到此次事件過程中,狀態(tài)發(fā)生了改變的觸點的 Touch 對象。

TouchEvent.targetTouches 一個 TouchList 對象,是包含了如下觸點的 Touch 對象:觸摸起始于當(dāng)前事件的目標(biāo) element 上,并且仍然沒有離開觸摸平面的觸點.

TouchEvent.touches 一個 TouchList 對象,包含了所有當(dāng)前接觸觸摸平面的觸點的 Touch 對象,無論它們的起始于哪個 element 上,也無論它們狀態(tài)是否發(fā)生了變化。

TouchEvent.type 此次觸摸事件的類型,可能值為 touchstart, touchmove, touchend 等等

TouchEvent.target 觸摸事件的目標(biāo) element,這個目標(biāo)元素對應(yīng) TouchEvent.changedTouches 中的觸點的起始元素。

TouchEvent.altKey, TouchEvent.ctrlKey, TouchEvent.metaKey, TouchEvent.shiftKey 觸摸事件觸發(fā)時,鍵盤對應(yīng)的鍵(例如 alt )是否被按下。

TouchList 與 Touch

TouchList 就是一系列的 Touch,通過 TouchList.length 可以知道當(dāng)前有幾個觸點,TouchList[0] 或者 TouchList.item(0) 用來訪問第一個觸點。

屬性

Touch.identifier:touch 的唯一標(biāo)志,整個 touch 過程中(也就是 end 之前)不會改變

Touch.screenXTouch.screenY:坐標(biāo)原點為屏幕左上角

Touch.clientXTouch.clientY:坐標(biāo)原點在當(dāng)前可視區(qū)域左上角,這兩個值不包含滾動偏移

Touch.pageXTouch.pageY:坐標(biāo)原點在HTML文檔左上角,這兩個值包含了水平滾動的偏移

Touch.radiusXTouch.radiusY:觸摸平面的最小橢圓的水平軸(X軸)半徑和垂直軸(Y軸)半徑

Touch.rotationAngle:觸摸平面的最小橢圓與水平軸順時針夾角

Touch.force:壓力值 0.0-1.0

Touch.target:Touch相關(guān)事件觸發(fā)時的 element 不會隨 move 變化。如果 move 當(dāng)中該元素被刪掉,這個 target 依然會不變,但不會冒泡。最佳實踐是將觸摸事件的監(jiān)聽器綁定到這個元素本身, 防止元素被移除后, 無法再從它的上一級元素上偵測到從該元素冒泡的事件。

希望本文能解答一些大家在移動端開發(fā)當(dāng)中的一些問題,本文行文匆忙如有不正確的地方希望能回復(fù)告知。

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

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

相關(guān)文章

  • Zepto源碼Touch模塊

    摘要:在觸發(fā)事件前,先將保存定時器的變量釋放,如果對象中存在,則觸發(fā)事件,保存的是最后觸摸的時間。如果有觸發(fā)的定時器,清除定時器即可阻止事件的觸發(fā)。其實就是清除所有相關(guān)的定時器,最后將對象設(shè)置為。進入時,立刻清除定時器的執(zhí)行。 大家都知道,因為歷史原因,移動端上的點擊事件會有 300ms 左右的延遲,Zepto 的 touch 模塊解決的就是移動端點擊延遲的問題,同時也提供了滑動的 swip...

    Prasanta 評論0 收藏0
  • Zepto源碼之Gesture模塊

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

    coolpail 評論0 收藏0
  • FastClick 源碼解讀

    摘要:所有瀏覽器瀏覽器不支持安卓中中有屬性安卓中中有屬性有屬性的有屬性的所以在不需要的瀏覽器會直接掉,不會執(zhí)行下面的所有代碼。見源碼行,可以看出在響應(yīng)無操作后,則觸發(fā)。 其實一直就想花些時間讀一讀那些優(yōu)秀的開源庫,今天終于下了決定打算死磕下自己,2016年每個月讀2-3個優(yōu)秀的開源庫,把源碼精彩的地方和自己心得分享給大家。 目錄 (一)背景(二)源碼解析(三)Zepto 點擊穿透與 Fast...

    Chaz 評論0 收藏0
  • 如何實現(xiàn)swipe、tap、longTap等自定義事件

    摘要:分別存儲事件的定時器。事件定時器延時時間存儲事件對象滑動方向判斷我們根據(jù)下圖以及對應(yīng)的代碼來理解滑動的時候方向是如何判定的。取消長按,以及取消所有事件取消長按取消所有事件方式都是類似,先調(diào)用取消定時器,然后釋放對應(yīng)的變量,等候垃圾回收。 前言 移動端原生支持touchstart、touchmove、touchend等事件,但是在平常業(yè)務(wù)中我們經(jīng)常需要使用swipe、tap、double...

    羅志環(huán) 評論0 收藏0

發(fā)表評論

0條評論

lentrue

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<