摘要:一直好奇是如何工作,于是花了幾天空余的時(shí)間一步步調(diào)試代碼,學(xué)習(xí)。一般會(huì)在時(shí)暫停游戲存檔等操作。這樣即使不監(jiān)聽(tīng)事件也能實(shí)現(xiàn)點(diǎn)擊的偵聽(tīng)。這種情況下一定會(huì)觸發(fā)事件。
Patience and perseverance will get paid.
這段時(shí)間開(kāi)始實(shí)習(xí)了,在公司做hybrid,專職寫(xiě)js,學(xué)習(xí)到了不少東西。一直好奇fastclick是如何工作,于是花了幾天空余的時(shí)間一步步調(diào)試代碼,學(xué)習(xí)fastclick。這篇文章可以結(jié)合者代碼看,希望可以給予需要學(xué)習(xí)fastclick的人一點(diǎn)思路。
有錯(cuò)誤的地方希望指正,thk~
主流程FastClick.attach()
FastClick(layer)
初始化化變量
this.trackingClick = false; //追蹤一個(gè)click this.trackingClickStart = 0; //追蹤時(shí)間 this.targetElement = null; // 目標(biāo)元素 this.touchStartX = 0;// X坐標(biāo) this.touchStartY = 0;// y坐標(biāo) this.lastTouchIndentifier = 0; this.touchBoundary = 10;//邊界條件(是否是一個(gè)點(diǎn)擊) this.layer = layer;//layer可以是document.body/document.documentElement
安卓設(shè)備綁定鼠標(biāo)事件(在捕獲階段,為的是第一時(shí)間處理到事件)
layer.addEventListener("mouseover",bind(this.onMouse,this),true); layer.addEventListener("mousedown",bind(this.onMouse,this),true); layer.addEventListener("mouseup",bind(this.onMouse,this),true);
綁定touch和click事件(判定是否是click行為,取消之前的click),
//最先捕獲到 layer.addEventListener("click", bind(this.onClick, this), true); //冒泡階段捕獲 layer.addEventListener("touchstart", bind(this.onTouchStart, this), false); layer.addEventListener("touchmove", bind(this.onTouchMove, this), false); layer.addEventListener("touchend", bind(this.onTouchEnd, this), false); layer.addEventListener("touchcancel", bind(this.onTouchCancel, this), false);
判斷是否存在stopImmediatePropagation,如果不存在則進(jìn)行hack,在onMouse中會(huì)利用stopImmediatePropagation來(lái)阻止其他點(diǎn)擊事件的回調(diào)函數(shù)的執(zhí)行,避免ghost click的現(xiàn)象
onMouse中,防止點(diǎn)透等詭異現(xiàn)象的代碼
if (!this.needsClick(this.targetElement) || this.cancelNextClick) { // Prevent any user-added listeners declared on FastClick element from being fired. if (event.stopImmediatePropagation) { event.stopImmediatePropagation(); } else { // Part of the hack for browsers that don"t support Event#stopImmediatePropagation (e.g. Android 2) event.propagationStopped = true; } // Cancel the event event.stopPropagation(); event.preventDefault(); return false; }
判斷是否通過(guò)onclick綁定了回調(diào)函數(shù),如果有讀取出來(lái),使用addEventListener,綁定事件處理函數(shù)
if (typeof layer.onclick === "function") { // Android browser on at least 3.2 requires a new reference to the function in layer.onclick // - the old one won"t work if passed to addEventListener directly. oldOnClick = layer.onclick; layer.addEventListener("click", function (event) { oldOnClick(event); }, false); layer.onclick = null; }觸發(fā)click流程 onTouchStart()
當(dāng)一些更高級(jí)別的事件發(fā)生的時(shí)候(如電話接入或者彈出信息)會(huì)取消當(dāng)前的touch操作,即觸發(fā)ontouchcancel。一般會(huì)在ontouchcancel時(shí)暫停游戲、存檔等操作。因此在調(diào)試的時(shí)候才會(huì)在touchStart之后,就觸發(fā)了touchCancel
直接斷點(diǎn)touchend,在控制臺(tái)打印,可以看到touchstart也是觸發(fā)的了、
判斷是不是單點(diǎn)觸發(fā)
if (event.targetTouches.length > 1) { return true; }
獲取目標(biāo)對(duì)象和touch事件對(duì)象
targetElement = this.getTargetElementFromEventTarget(event.target); touch = event.targetTouches[0];
根據(jù)touch事件對(duì)象,設(shè)置一些初始屬性
this.trackingClick = true; //標(biāo)識(shí)跟蹤該次點(diǎn)擊 this.trackingClickStart = event.timeStamp;//點(diǎn)擊開(kāi)始的時(shí)間 this.targetElement = targetElement;//目標(biāo)元素 this.touchStartX = touch.pageX; //x坐標(biāo) this.touchStartY = touch.pageY; //y坐標(biāo)onTouchMove()
如果剛觸發(fā)完touchstart事件馬上就觸發(fā)touchend,說(shuō)明手指只是輕輕點(diǎn)了一下屏幕,也就是所謂的點(diǎn)擊操作。這樣即使不監(jiān)聽(tīng)click事件也能實(shí)現(xiàn)點(diǎn)擊的偵聽(tīng)。不過(guò)這里有一個(gè)實(shí)際的情況,很多山寨的Android設(shè)備屏幕很不靈敏,需要使勁按下才能有所感知。這種情況下一定會(huì)觸發(fā)touchmove事件。所以針對(duì)Android設(shè)備的點(diǎn)擊操作可以適當(dāng)放寬,比如touchstart和touchend之間可以允許有少量幾個(gè)touchmove,并且touchmove的距離不能超過(guò)多少個(gè)像素等等
因此也是需要監(jiān)聽(tīng)onTouchMove,并且加入判斷
// If the touch has moved, cancel the click tracking if ( this.targetElement !== this.getTargetElementFromEventTarget(event.target) || this.touchHasMoved(event) ) { this.trackingClick = false; this.targetElement = null; }onTcouhEnd()
在touchend的時(shí)候,執(zhí)行this.onTouchEnd(上個(gè)流程綁定了)
判斷是否在追蹤該click,在this.onTouchMove的時(shí)候,如果移動(dòng)的距離大于邊界,則將this.trackingClick=false,在touchend就不用再判斷是否為一個(gè)click的行為
if(!this.trackingClick){ return true; }
獲取目標(biāo)元素標(biāo)簽,需要根據(jù)標(biāo)簽名來(lái)做一些判斷
targetTagName = targetElement.tagName.toLowerCase();
如果是label,進(jìn)行bug修復(fù)
執(zhí)行this.needsFocus,針對(duì)表單元素的focus和click事件的處理
先f(wàn)ocus表單
在觸發(fā)點(diǎn)擊事件
針對(duì)IOS,滾動(dòng)層bug修復(fù)
判斷元素是否需要原生的click,實(shí)際上就是有些行為還是要瀏覽器來(lái)執(zhí)行默認(rèn)的行為
表單元素disabled,點(diǎn)擊不了
type=file的控件
video
label
如果不需要,則發(fā)送一個(gè)click事件
event.preventDefault(); this.sendClick(targetElement, event);sendClick()流程
在一些安卓設(shè)備上,必須讓一個(gè)元素blured,才能使創(chuàng)建的clickEvent生效
if (document.activeElement && document.activeElement !== targetElement) { document.activeElement.blur(); }
創(chuàng)建clickEvent,使用touch事件對(duì)象的屬性來(lái)進(jìn)行初始化
clickEvent = document.createEvent("MouseEvents"); clickEvent.initMouseEvent( this.determineEventType(targetElement), //bug修復(fù)針對(duì)select true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
創(chuàng)建完成之后,賦予對(duì)象一個(gè)額外的屬性,在onClick中可以使用,然后觸發(fā)點(diǎn)擊事件,此時(shí)通過(guò)addEventListner綁定的click事件就會(huì)觸發(fā)
clickEvent.forwardedTouchEvent = true; targetElement.dispatchEvent(clickEvent);onClick()
addEventListener添加會(huì)按照添加順序執(zhí)行
onClick作為第一個(gè)注冊(cè)監(jiān)聽(tīng)的,因此,是第一個(gè)執(zhí)行的click事件的回調(diào)函數(shù)
特殊情況處理,一般不會(huì)執(zhí)行
/* It"s possible for another FastClick-like library delivered with third- party code to fire a click event before FastClick does (issue #44). In that case, set the click-tracking flag back to false and return early. This will cause onTouchEnd to return early. */ if (this.trackingClick) { this.targetElement = null; this.trackingClick = false; return true; }
特殊情況處理
if (event.target.type === "submit" && event.detail === 0) { return true; }
執(zhí)行onMouse,
//創(chuàng)建時(shí),附帶的一個(gè)屬性 if (event.forwardedTouchEvent) { return true; }
最后返回為真
return permitted; //true
注意:在這里的return的true或false并不會(huì)影響綁定的其他回調(diào)函數(shù)的執(zhí)行
總結(jié)完整的看完代碼,深深感覺(jué)到移動(dòng)端的坑非常的多,很有怪異的現(xiàn)象因?yàn)闆](méi)有遇到過(guò)暫時(shí)理解不了,希望之后可以繼續(xù)研究,把代碼完全讀懂。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/79839.html
摘要:前言最近公司新開(kāi)了一條業(yè)務(wù)線,有幸和大佬們一起從頭開(kāi)始構(gòu)建一套適合新業(yè)務(wù)的框架。俗話說(shuō)得好呀,適合自己的才是最好的 前言 最近公司新開(kāi)了一條業(yè)務(wù)線,有幸和大佬們一起從頭開(kāi)始構(gòu)建一套適合新業(yè)務(wù)的框架。俗話說(shuō)得好呀,適合自己的才是最好的
摘要:前言最近公司新開(kāi)了一條業(yè)務(wù)線,有幸和大佬們一起從頭開(kāi)始構(gòu)建一套適合新業(yè)務(wù)的框架。俗話說(shuō)得好呀,適合自己的才是最好的前言 (Foreword) 最近公司新開(kāi)了一條業(yè)務(wù)線,有幸和大佬們一起從頭開(kāi)始構(gòu)建一套適合新業(yè)務(wù)的框架。俗話說(shuō)得好呀,適合自己的才是最好的
閱讀 1406·2021-11-08 13:14
閱讀 761·2021-09-23 11:31
閱讀 1051·2021-07-29 13:48
閱讀 2789·2019-08-29 12:29
閱讀 3384·2019-08-29 11:24
閱讀 1910·2019-08-26 12:02
閱讀 3703·2019-08-26 10:34
閱讀 3447·2019-08-23 17:07