html5移動端長按事件填坑歷程


背景:

在h5頁面,實現長按彈出上拉菜單,我們知道h5沒有所謂的長按事件,有些UI組件庫封裝了長按事件,比如zepto的longtap

在pc端有鼠標事件(mousedown,mousemove,mouseup),在h5有touch觸摸事件(touchstart,touchmove,touchend)

不能因為需要一個長按事件就引入一個庫,這里我們就基於touch觸摸事件來實現。

 

touch事件群

  • touchstart事件:當手指觸摸屏幕的時候觸發, 一個手指觸摸觸發一次;
  • touchmove事件:當手指在屏幕上滑動的時候觸發,滑動一下可能觸發多次,是一個持續的過程
  • touchend事件: 當手指從屏幕上移開的時候觸發;
  • touchcancel事件:當觸摸點被中斷時觸發。例如在觸摸過程中突然頁面alert()一個提示框

     手指快速點擊一個元素,會經過:touchstart  -> touchend --> click

     手指長按一個元素,會經過:touchstart  -> touchend

     手指滑動,會經過:touchstart  ->  touchmove(n次) -> touchend

     綜上:

     1、click事件發生在touchstart和touchend之后,這也就是我們所說的移動端點擊事件一般有300ms的延遲(可用tap事件替代)

     2、長按的過程是touchstart之后touchend之前,區分長按還是點擊,可以根據時間判斷,比如大於500ms是長按,小於500ms是點擊

     3、如果在點擊屏幕的時候手指滑動的話,是不會觸發click事件的

 由以上結論,我們就可實現長按事件

 主要邏輯:

      1、在touchstart里定義延遲器,500ms后執行長按邏輯

      2、在touchend里取消延遲器,即在touchstart觸發的500ms之內,如果手指從屏幕上移開,則不是長按

      3、在touchmove里取消延遲器,即如果手指在屏幕上滑動,則不是長按

代碼如下:

<div  @touchstart="onTouchStart(d,$event)"
      @touchmove="onTouchMove($event)"
      @touchend="onTouchEnd">按鈕</div>


export default {
   data () {
      toucheX: 0,
      toucheY: 0,
      timeOutEvent: ''
   },
   methods: { 
     // 長按彈出上拉操作面板
    onTouchStart (data, e) {
      this.toucheX = e.targetTouches[0].screenX
      this.toucheY = e.targetTouches[0].screenY
      // 開啟定時器前先清除定時器,防止重復觸發
      this.timeOutEvent && clearTimeout(this.timeOutEvent)
// 顯示上拉面板
this.timeOutEvent = setTimeout(() => {this.showActionSheet = true }, 500) e.preventDefault() // 阻止系統默認事件 }, onTouchMove (e) { const moveX = e.targetTouches[0].screenX const moveY = e.targetTouches[0].screenY // 解決vivo機型,手指沒有move,touchmove事件仍然會調用而導致setTimeout被clear if (this.toucheX !== moveX || this.toucheY !== moveY) { // 手指滑動,清除定時器,中斷長按邏輯 this.timeOutEvent && clearTimeout(this.timeOutEvent) } }, onTouchEnd () {
// 清除定時器,結束長按邏輯
this.timeOutEvent && clearTimeout(this.timeOutEvent)
// 若手指離開屏幕,時間小於我們設置的長按時間,則為點擊事件 }, } }

注意:

1、H5頁面長按會觸發系統的默認事件,ios和安卓表現不同,如下圖。可以使用event.preventDefault()方法阻止后面默認事件的發生

      

在touchstart中preventDefault,會導致click事件不觸發和a鏈接點擊沒反應。可以使用tap代替click,但是a標簽的話就不太方便了

在touchmove中preventDefault,會阻止瀏覽器默認滾動

 

2、在某些手機,比如vivo,沒有move,但是touchmove事件仍然會觸發

在這種情況,可以根據touch的位置判斷手指是否移動,來區分是點擊長按或者是滑動

在touchstart事件中記錄touch時的x,y的坐標,然后在touchmove中,再判斷touch的位置是否和touchstart中的一樣的。

注意,查看TouchEvent最好在谷歌模擬手機瀏覽器中查看,不要在真實手機瀏覽器查看,本人在真實手機瀏覽器console和alert都看不到詳細的信息,疑惑了半天,發現真實手機瀏覽器雖然看不到信息,但是可以直接取值(TouchList是類數組對象)

 

觸屏事件的操作信息都存儲在TouchEvent類型對象中,此對象屬性較多,下面着重介紹下touches、targetTouches與changedTouches

   touches[只讀]:手指觸摸到屏幕上,所有觸摸點的集合;

   targetTouchs[只讀]:手指觸摸到DOM元素(綁定事件的dom節點)上的觸摸點的集合 

   changeTouches[只讀]:表示自上次觸摸事件以來發生改變的(和觸摸事件對應的Touch 對象)

            對於 touchstart 事件, changedTouches是此次事件中新增加的觸點。

            對於 touchmove 事件,changedTouches是和上一次事件相比較,發生了變化的觸點。

      對於touchend事件,changedTouches 是剛觸摸面離開的觸點(最后一次離開屏幕的手指的Touch 對象)

 

      下圖中有兩個div,只對DIV2綁定了touchstart事件

   當手指第一次觸摸到DIV2時,三個對象表示的都是一樣的

   再放下第二根手指和第三根手指同時觸摸DIV1DIV2時,

        此時touches對象表示的是第一根手指、第二根手指、第三根手指的信息

   此時targetTouches對象表示的是第一根手指和第三根手指的信息,因為綁定touch事件的節點為DIV2

   而changedTouches對象表示的是第二根手指和第三根手指的信息,因為第一根手指沒有變化

 綜上:

touchmove時,如果手指從目標元素(綁定事件的dom節點)滑出,targetTouches還會有此觸摸點信息

當一個觸摸點從目標元素離開,它的信息將從 touches、targetTouches里移除,但是changedTouches會保留此觸摸點信息;

當最后一個觸摸點離開,touches、targetTouches變成空值,而changedTouches保留着最后一個離開的觸摸點信息

 

 擴展:

Touch.screenX:觸點相對於屏幕左邊沿的X坐標。只讀屬性。

Touch.screenY:觸點相對於屏幕上邊沿的Y坐標。只讀屬性。

Touch.clientX:觸點相對於可見視區(visual viewport)左邊沿的X坐標。不包括任何滾動偏移。只讀屬性。

Touch.clientY:觸點相對於可見視區(visual viewport)上邊沿的Y坐標。不包括任何滾動偏移。只讀屬性。

Touch.pageX:觸點相對於HTML文檔左邊沿的X坐標。當存在水平滾動的偏移時,這個值包含了水平滾動的偏移。只讀屬性。

Touch.pageY:觸點相對於HTML文檔上邊沿的Y坐標。當存在水平滾動的偏移時,這個值包含了垂直滾動的偏移。只讀屬性。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM