日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

移動web開發技能之touch事件詳解_IOS

作者:呂小鳴 ? 更新時間: 2022-10-30 編程語言

概述

單擊事件是任何一個前端頁面中最常用的交互行為之一,在傳統的PC端大部分是使用click事件來實現用戶單擊交互的程序邏輯,而在移動Web端新增了touch事件來實現移動端更加敏感和復雜的觸摸交互行為。本章將就移動端touch事件的使用以及它與PC端的click事件的區別進行深入探討。

touch事件

在傳統的PC端,用戶的單擊操作主要是由鼠標的左鍵或者右鍵來產生,它主要是指鼠標的按鈕被按下,并且在很短的時間內(一般小于300ms)又被釋放開,這就被稱為單擊操作(或稱為一次點擊操作)。

而對于移動Web端,同樣也是如此,當手指觸摸到屏幕時開始計算時間,并且在300ms內離開屏幕,這段時間手指不能移動,這就算是移動Web端的單擊事件,手指觸摸就被稱為touch。

touch事件分類

移動Web端的touch觸摸事件主要由屏幕和觸摸點組成,其中屏幕可以是手機、平板或者觸摸板,而觸摸點可以通過手指、胳膊肘或觸摸筆,甚至耳朵、鼻子都行,但一般是通過手指。根據touch觸摸的類型可分為以下4種事件:

  • touchstart:當手指與屏幕接觸時觸發。
  • touchmove:當手指在屏幕上滑動時連續地觸發。
  • touchend:當手指從屏幕上離開時觸發。
  • touchcancel:當touch事件被迫終止,例如電話接入或者彈出信息時會觸發,或者當觸摸點太多,超過了支持的上限(自動取消早先的觸摸點)時觸發,一般不常用。

相比PC端,以上4種事件將用戶的touch行為劃分得更細,并且通過這些細化的事件可以實現移動Web端獨有的用戶交互行為,例如拖動swipe、長按longtap、雙指縮放pinch,等等。

其中的touchstart、touchmove和touchend是最常用的3個事件,其中touchstart最先觸發,touchend結束時觸發,而touchmove是否觸發取決于手指是否在觸摸屏上移動。下面用代碼來感受一下這3種事件的觸發順序,如下

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
  <title>touch事件</title>
  <script type="text/javascript">
    document.addEventListener("touchstart",function () {
        console.log("開始觸摸");
    });
    document.addEventListener("touchmove",function () {
        console.log("移動手指");
    });
    document.addEventListener("touchend",function () {
        console.log("結束觸摸")
    });
  </script>
</head>
<body>
</body>
</html>

在瀏覽器中運行這段代碼,同時注意要啟用Chrome中DevTools工具中的Device Mode功能,并使用鼠標模擬手指在屏幕上觸發觸摸事件,隨后就會在Console控制臺看到打印出對應的日志,從中可以看到一個簡單的觸摸操作是如何完成的。

touch事件對象

對于touch事件,每一次觸發都可以得到一個事件對象,在JavaScript中這個對象叫作TouchEvent,利用TouchEvent可以獲取touch事件觸發時的坐標、元素以及到底有幾個手指觸發等,下面就來了解一下TouchEvent事件對象。

可以在Console控制臺打印出當前觸發touch時的TouchEvent對象,代碼如下所示:

document.addEventListener("touchstart",function (e) {
    console.log(e);
});

打印的內容如圖:

在上面的TouchEvent的屬性中,經常使用的就是touches、targetTouches和changedTouches,它們的含義分別是:

  • touches:當前頁面(屏幕)上所有的觸摸點。
  • targetTouches:當前綁定事件的元素上的觸摸點。
  • changedTouches:當前屏幕上剛剛接觸的手指或者離開的手指的觸摸點。

這3個屬性返回的是TouchList對象,代表的是一個touch的集合數組,也就是說每一次touch觸發,都會兼顧到多指觸摸的場景,下面就分別以單指觸摸的場景和多指觸摸的場景來講解這3個屬性的區別。

首先是單指觸摸的場景,我們來模擬用戶一個手指觸摸,如圖。

外層的線框代表頁面,里面的一個<div>元素綁定了touch事件,1號手指觸摸了該<div>元素,這時touches、targetTouches以及changedTouches里面的觸摸點都是指1號手指這個觸摸點,應該很好理解。 對于多指觸摸的場景,條件是手指觸摸屏幕之后暫不離開,如圖。

外層的線框代表頁面,里面的一個<div>元素綁定了touch事件,首先1號手指第一個觸摸了該<div>元素,然后2號手指第二個也觸摸了該<div>元素,最后3號手指第三個觸摸了div外面的區域,這時touches涵蓋的觸摸點的集合數組包括1號、2號、3號手指,而targetTouches涵蓋的觸摸點的集合數組包括1號和2號手指,而changedTouches涵蓋的觸摸點的集合數組包括2號和3號手指。

當手指都離開屏幕之后,touches和targetTouches中將不會再有值,changedTouches還會有一個值,此值為最后一個離開屏幕的手指的接觸點。這就是touches、targetTouches和changedTouches這3個屬性對于單指觸摸的場景和多指觸摸的場景下的區別,總結如下:

單指觸摸的場景:

  • touches:1號手指
  • targetTouches:1號手指
  • changedTouches:1號手指

多指觸摸的場景:

  • touches:1,2,3號手指
  • targetTouches:1,2號手指
  • changedTouches:2,3號手指

對于單指觸摸的場景來說,它們并無區別,主要區別在于多指觸摸的場景,所以在使用時可以根據具體的程序邏輯來選擇使用合適的屬性。

對于涵蓋觸摸點的集合數組TouchList而言,里面每個元素都是一個touch對象,通過這個對象可以獲取當前觸摸的位置,如圖。

其中,主要用到了offsetX/Y、pageX/Y和clientX/Y這3個屬性,它們的區別和含義分別是:

  • offsetX/Y:觸摸位置相當于事件源元素的位置坐標,以當前<div>元素盒子模型的內容區域的左上角為原點。
  • pageX/Y:觸摸位置相當于整個頁面內容區域的位置坐標,當頁面過長時,包括滾動隱藏的部分內容,以頁面完整內容區域的左上角為原點。
  • clientX/Y:觸摸位置相當于瀏覽器視區(屏幕)區域的位置坐標,以相對于頁面的可見部分內容區域的左上角為原點。

具體的位置和距離可以參考下圖,外層表示頁面的所有內容,中間框表示瀏覽器的視區,其中有一個<div>元素綁定了touch事件,黑點表示觸摸點的位置。

移動web單擊事件

在了解了touch事件之后,我們知道移動Web端的單擊事件完全可以由touchstart、touchmove和touchend來組合實現,移動Web端同時也提供了原生的click事件,它和傳統的PC端的click事件一樣,在用戶完成一次完整的手指單擊屏幕之后觸發。在移動Web端使用click綁定單擊事件,代碼如下:

document.addEventListener("click",function (e) {
    console.log(e);
});

一切看似都很順利,在需要使用單擊時就用click事件,在需要使用touch時(拖動,長按等)就使用touch對應的事件。但是,對于移動Web端而言,處于iOS系統或Android系統時,采用click實現單擊事件卻有著不同的表現。

iOS單擊延遲

這要追溯至2007年初,蘋果公司在發布首款iPhone前遇到了一個問題:當時的網站都是為大屏幕設備所設計的,于是提出了視區(Viewport)的概念,其中一項即是用戶在瀏覽網頁時,可以在頁面的任何地方通過雙擊操作將頁面放大(Double Tap to Zoom)。這個交互功能提升了用戶瀏覽網頁時的體驗,于是Android和iOS的移動端瀏覽器紛紛支持了這個功能,但是對于雙擊這個操作而言,其實是包括了兩次單擊操作,當第一次單擊完成后,系統需要有一段時間來監聽是否有第二次單擊,如果有則表明此次操作是一個雙擊操作,而這段時間間隔大概有300毫秒(ms)。

因此,哪怕是只想要單擊這個事件,也都會經過雙擊放大這個判斷邏輯,導致要等到300毫秒之后才能收到單擊事件程序邏輯的反饋,這就是300毫秒的單擊延遲問題。

對于Android系統的瀏覽器而言,可以通過給視區設置user-scalable=no來禁止用戶進行縮放,隨后就可以正常地使用原生的click事件而沒有延遲;對于iOS系統而言,瀏覽器對user-scalable支持度存在Bug(漏洞),導致了無法通過簡單的設置來達到正常使用原生click事件的目的。代碼如下:

<meta name="viewport" content=" initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />

所以,在iOS移動端,如果想要實現真正的單擊事件而沒有300毫秒延遲問題,就不能采用原生的click事件,可以通過touch(touchstart、touchmove和touchend)事件來模擬一次單擊操作。好在當前業界已有比較流行的方案,例如Zepto.js中的tap事件和FastClick.js庫可用來解決這個問題,在這里主要介紹一下FastClick.js庫。

FastClick.js是FT Labs團隊結合touch事件專門為解決移動端瀏覽器的300毫秒單擊延遲問題所開發的一個輕量級的庫。正常情況下,在移動Web端,當用戶單擊屏幕時,會依次觸發touchstart、touchmove(0 次或多次)、touchend、click(原生)這些事件。touchmove事件只有當手指在屏幕上移動時才會觸發。Touchstart、touchmove或者touchend 事件的任意一個調用event.preventDefault()方法,都會直接阻止原生click事件的觸發。

FastClick的實現原理是在檢測到touchend事件觸發時,把瀏覽器在300毫秒之后原生的click事件阻止掉,然后通過DOM自定義事件立即發出一個模擬的click事件,這樣就消除了300毫秒的延遲,提供了一個快速響應的“單擊”事件。如下代碼演示了FastClick的使用。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
  <title>FastClick.js</title>
  <script type="text/javascript" src="./fastclick.js"></script>
</head>
<body>
  <button id="click">點我</button>
  <script type="text/javascript">
    // 頁面加載完成后,使用FastClick,一般傳遞最外層的body元素即可
    document.addEventListener('DOMContentLoaded', function(){
        FastClick.attach(document.body);// 在實際的項目中,需判斷在iOS移動端才需要此程序邏輯
    }, false);
    document.getElementById("click").addEventListener("click",function(){
        alert("單擊觸發! ");
    },false)
  </script>
</body>
</html>

需要注意的是,在不修改<meta>標簽中的user-scalable屬性的情況下,300毫秒單擊延遲的問題只會出現在iOS系統的瀏覽器中,并且解決方案只需要針對iOS端,上文也提到了這個問題的產生是由于對user-scalable支持度存在Bug,之后蘋果公司也意識到了這個問題的嚴重性,于是在iOS 9.3版本時,提供了一個基于新的內核WKWebView的瀏覽器,并將其應用在Safari瀏覽器上,由此解決了這個問題(存在300毫秒單擊延遲問題的瀏覽器是UIWebView,這個內核已經不再維護了),并且后續使用iOS 9.3版本系統的瀏覽器在訪問頁面時,會默認使用WKWebView瀏覽器。

至此,移動Web端的300毫秒單擊延遲問題得到了徹底的改善。

“單擊穿透”問題

在移動Web端,有一個很常見的應用場景,單擊一個按鈕會出現一個蒙層,此蒙層是全屏遮蓋,并且有最高層級,當單擊蒙層時,蒙層消失。此場景和交互操作看似并沒有什么問題,但是假如頁面中有一個綁定了單擊事件的<div>元素被蒙層遮蓋,而單擊蒙層關閉時的位置剛好和該<div>元素重合,那么蒙層關閉后會同時觸發該<div>元素的單擊事件,對于用戶來說,這個操作并不是要單擊該<div>元素,這就是所謂的“單擊穿透”問題,如圖。

出現“單擊穿透”問題需要有個條件,即蒙層是通過綁定的touch事件來實現隱藏,而其遮蓋的<div>元素綁定的是原生click事件,這樣就形成了touch事件觸發之后,蒙層隱藏了,300毫秒后當前這個觸摸點的click事件又觸發了,就形成“單擊穿透”。

移動Web端的“單擊穿透”問題出現的原因其實和300毫秒單擊延遲問題脫不了關系,但是“單擊穿透”出現的場景比較單一,并且也比較好解決。

解決“單擊穿透”問題可以從問題出現的原因上來著手,主要有以下兩種解決方案:

  • 不要同時混用touch事件和click事件,要么給蒙層和<div>元素同時綁定touch事件,要么同時綁定click事件,在iOS 9.3版本之后,只用click事件即可,此方案體驗最好。
  • 延遲蒙層消失的時間,例如在touch事件觸發后,在350毫秒后再讓蒙層消失,這樣后面的<div>元素就不會觸發click事件了,此方案會導致蒙層消失的響應慢,體驗差,并且有時會觸發兩次消失邏輯,故不推薦使用。

無論是300毫秒單擊延遲問題,還是“單擊穿透”問題,這些都是移動Web端特有的問題,也在一定程度上反映出移動Web端環境的復雜性,需要注意支持度和兼容性問題的地方很多,所以大家在進行移動Web端開發時,要有意識地去關注這些問題。

原文鏈接:https://juejin.cn/post/7138247179470110727

欄目分類
最近更新