標籤:事業 自訂事件 預設 分享圖片 can val 一個 結束 get
================================慣例碎碎念前言================================
當時首先想到要做長按事件的時候,我想到的是vue內部的自訂指令,畢竟官網裡邊有這麼一句描述:
有的情況下,你仍然需要對普通 DOM 元素進行底層操作,這時候就會用到自訂指令。
但是項目用在app中,因為另一個未知原因的bug,自訂事件躺槍(至今死不瞑目)。長按事件被我改成了在初始化時,就直接綁定到需要他的dom上。
================================正經文================================
綁定的命令寫在mounted鉤子裡,這是因為在created內部我也找不到dom,而在mounted階段,所有的dom結構和資料都被展示到頁面當中,
詳情可以參見“vue生命週期強刷”:https://www.cnblogs.com/padding1015/p/9159381.html
所以,下邊的代碼經過不斷的輪迴,最終是寫在mounted裡邊的
let oDiv = document.getElementById(‘canvas‘);
// 因為長按事件要加在div#canvas上的,如果事件是任何地方的話,就是document
1 oDiv.addEventListener("touchstart", function(e) {}, false);2 3 oDiv.addEventListener("touchmove", function(e) {}, false);4 5 oDiv.addEventListener("touchend", function(e) {}, false);
至於,三個綁定事件的回調裡再寫什麼,就不太關vue的事了(dui,就是這麼草率)。
在公布最終的成型代碼之前,容我先補一波關於事件的”兵”。
主要兵線呢有三條:
中路:dom2級事件處理常式
上路:事件流
下路:觸摸事件
所需事件知識點分布如:
(查看大圖:右鍵新標籤中開啟)掌握了上邊的知識點後,就是在長按功能裡邊的應用了。
別急~
長按功能原理分析一波:
所謂的長按其實就是手指按下去,不移動,超過一定時間才把手指拿開的一個過程(我說的好有道理哈哈哈。然後聽到一片同一個聲音:廢話!!)。
而在這個過程中,正好是觸摸的三個事件。
監聽手指按下去後是否有移動,就該touches上場了,監聽他的clientX,clientY只要沒變就是沒移動。
並且在這個過程中,還會不時地有地方的英雄冒出來幹擾我們補兵。那就是一個手機內建的效果了:
長按時,在移動端觸摸文字,(至少ios裡)會出現選擇文字等幹擾我們的真正功能,用了preventDefault()這個屬性後就沒有了。
紙上談兵是沒用的額,直接上長按功能代碼:
為了不必要的麻煩(其實就是我懶得寫),把多餘的其他代碼刪掉了,所以不要驚訝export default裡邊為什麼只有mounted。
<script> let x = 0, y = 0, z = 0, timer1 = null; export default { mounted(){ let oDiv = document.getElementById(‘canvas‘); oDiv.addEventListener("touchstart", function(e) { if (e.preventDefault) e.preventDefault(); else e.returnValue = false; if (e.touches.length > 1) { return false; } z = 0; timer1 = setTimeout(function() { z = 1; }, 500); x = e.touches[0].clientX; y = e.touches[0].clientY; }, false); oDiv.addEventListener("touchmove", function(e) { if (e.preventDefault) e.preventDefault(); else e.returnValue = false; if (x != e.touches[0].clientX || y != e.touches[0].clientY) { clearTimeout(timer1); return false; } }, false); oDiv.addEventListener("touchend", function(e) { if (e.preventDefault) e.preventDefault(); else e.returnValue = false; if (z != 1) { clearTimeout(timer1); x = 0; y = 0; return false; } else if(z=1){ x = 0; y = 0; z = 0; /* 到這裡已確定觸發了長按事件,接下來執行長按後要做的其他事情 */ } }, false); } }</script>
哎呀,我怎麼可能直接甩了代碼扭頭就走呢!接下來請看~
講解版本的代碼
<script> let x = 0,//用於記錄clientX y = 0,//用於記錄clientX z = 0,//用於判斷,是否是已按住並超過了設定時間。 timer1 = null;//用於定時器 export default { mounted(){ let oDiv = document.getElementById(‘canvas‘); // 因為長按事件要加在div#canvas上的,如果事件是任何地方的話,就是document /* 添加touchstart,手指觸摸事件 */ oDiv.addEventListener("touchstart", function(e) { /* 阻止預設事件,其實這裡ie的相容寫法returnValue沒必要*/ if (e.preventDefault) e.preventDefault(); else e.returnValue = false; /* 移動時觸發touchmove導致多個touches對象,所以可以直接跳出*/ if (e.touches.length > 1) { return false; } /* 這裡有曆史淵源的,是第二次點擊的時候,把z的值還原。*/ z = 0; /* 手指一旦觸控螢幕幕,就開啟一個倒計時定時器timer1 */ timer1 = setTimeout(function() { z = 1; // 如果倒計時結束還沒有清楚定時器的話,就把z賦值為1,這樣,當判斷z=1就說明按住螢幕的時間達到了開發人員設定的長按時間。也就是滿足了長按事件 }, 500); /* 手指一旦觸控螢幕幕,要立即做的第二件事:記錄觸摸時的點的位置,並存在x,y兩個變數裡*/ x = e.touches[0].clientX; y = e.touches[0].clientY; }, false); /* 綁定第二個事件touchmove,手指在螢幕上連續滑動時連續地觸發*/ oDiv.addEventListener("touchmove", function(e) { if (e.preventDefault) e.preventDefault(); else e.returnValue = false; /*判斷,因為每次手指移動會連續觸發touchmove,也就會不停的往event事件對象裡邊添加“跟蹤觸摸屬性touches”*/ //這個屬性是一個數組,每次新添加的都會在最前邊。所以每次擷取數組裡邊的第一個對象對應的clientX和clientY,就是即時的移動點的位置 //找這個點的作用,就是為了監聽使用者,是否按住還移動了。如果移動了,那不能算長按事件(不過這個也看產品需求,如果按住也要觸發長按規定那個邏輯的話,這不要判斷) if (x != e.touches[0].clientX || y != e.touches[0].clientY) { // 具體的判斷方法,還記得touchstar那裡已經記錄了‘起跑點‘了,只要和x,y的值進行比較,與兩個的值有任意一個不等,就是移動了。 // 那麼移動的話,先要清除事先埋伏的定時器timer1.要不然,雖然不是長按事件但是倒計時還在進行中。 clearTimeout(timer1); return false;//除掉‘後患‘後,安心的結束本次使用者的觸摸事件監聽。 } }, false); /* 添加第三個觸摸事件touchend,這個事件的情境就是使用者手指從螢幕拿開時觸發*/ oDiv.addEventListener("touchend", function(e) { if (e.preventDefault) e.preventDefault(); else e.returnValue = false; /* 上文已經介紹,當使用者拿開的時候,只要判斷使用者從點擊到拿開的時間。而記錄時間長度比較麻煩,所以當時用了定時器,設定了一個我們想要的時間,時間到了就改變一個狀態值z,所以這裡我們只要判斷z是否被改變即可*/ if (z != 1) { /* 如果使用者手指頭拿開的時候,z還是0,即不等於1,說明定時器還沒被觸發,也就說明沒有達到長按的時間,那麼不用期待了,同touchmove壽終正寢時要做的一樣,清除定時器即可。*/ clearTimeout(timer1); /* 但是,touchend和touchmove還有一點不一樣的,touchend是end(廢話),touchmove不管move多少次,最終手指總要從螢幕拿開的,這就是他的關鍵點。而拿開就是結束,結束,是整個這次觸摸生命過程的結束。*/ /* 所以,end還要做的事是初始化,是還原。他需要把x,y的值歸為原始。以為了下次再次觸發touchstart時做準備。好感人的故事。*/ x = 0; y = 0; return false;//然後安心的“死去”。 } else if(z=1){ /* 如果,觸發了長按事件,終於觸發了長按事件!*/ /* 他還是不能得意忘形,在大展宏圖之前,還是要做點準備,打掃一下戰場,將各個變數初始化 */ x = 0; y = 0; z = 0; /* 然後,他才能開心的做自己計劃已久的事業:執行長按後要做的其他事情 */ /* 是什麼事情呢? 五點半了,吃飯。*/ } }, false); } }</script>
奧,對了還有個現象,在於vue中的swiper一同食用時,長按住並且滑動會觸發上/下翻頁。如果touchmove裡邊還要有什麼動作的話,加上swiper體驗很不好。
2018-07-07 17:35:31
JS案例 - 基於vue的移動端長按手勢