觸控板觸控式螢幕禁止手指縮放,這麼處理才行

jimojianghu發表於2021-11-11

禁止縮放

有天,辛苦做了個複雜操作功能的頁面,上線後有使用者反饋:很多功能使用不了。驚了,以為是哪裡出了bug。
立馬聯絡使用者,才發現原來是使用者使用膝上型電腦,沒有禁用觸控板,然後不小心碰到導致整個介面都放大,很多功能超出介面不見了。
然而那能怎麼辦,使用者第一,自然得快速解決這個問題,而且是觸控板和觸控式螢幕都得解決。

在以前,如果要禁止移動端裝置的觸控式螢幕上,手指縮小放大的功能,都會想到使用viewport 來處理。如下:

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

然而,目前的瀏覽器已經不再支援 viewport 禁止縮放的定義。現在使用這段程式碼,已經徹底無效,要想解決問題,得想新的方案。

話不多說,下面先給出具體的解決辦法。

觸控板

要禁止觸控板的縮放行為,需要處理滑鼠滾輪事件:wheel
因為筆記本觸控板雙指滑動,將響應該事件,如果對該事件阻止預設行為,就能解決縮放問題了。
注意
passive: false 必須使用,下面會介紹。
此外,對 document 處理了滾輪事件後,因為該事件會傳播冒泡,所以需要注意頁面上其他的子元素——如果有滾動條之類的,需要在子元素上阻止冒泡,不然後滾動會失效。

// 觸控板禁止手指縮放
document.addEventListener('wheel', function(event) {
    event.preventDefault()
}, { passive: false })

// 阻止子元素相同事件冒泡
// document.getElementById('app').addEventListener('wheel', function(event) {
//   event.stopPropagation()
// })

觸控式螢幕

要禁止觸控式螢幕的手指縮放,可以使用如下的 CSSJS 兩種方法。

css 處理

只要在頁面上使用css樣式 touch-action: none,就能禁止web在手機或平板上的縮放了。

<html style="touch-action: none;">

注意:
使用 touch-action: none 作用於html元素上,可以禁止頁面縮放,因為該樣式屬性是非繼承屬性,不會影響頁面子元素的手勢操作。
如果使用 * {touch-action: none;} 全域性作用,則會影響子元素的各種手勢操作。

js 處理

使用js程式碼來禁止觸控式螢幕的手指縮放,和處理觸控板類似的,這裡主要是監聽幾個手勢事件,touchstart, touchend, touchmove等。
注意:需要注意的事項也會觸控板處理時一樣,加上 { passive: false },並且對子元素進行阻止冒泡的操作。

document.addEventListener('touchstart', function(event) {
    event.preventDefault()
}, { passive: false })

// 阻止子元素相同事件冒泡
// document.getElementById('app').addEventListener('touchstart', function(event) {
//   event.stopPropagation()
// })

擴充套件解讀

touch-action

非繼承屬性,預設auto
用於設定觸控式螢幕使用者如何操縱元素的區域,允許你在觸控時控制滾動操作。
例如,瀏覽器內建的縮放功能。
這樣做的好處還有,它可以允許你自己實現這些手勢。
現代瀏覽器都支援該屬性,但部分屬性值只有chrome支援。

touch-action: auto | none | [ [ pan-x | pan-left | pan-right ] || [ pan-y | pan-up | pan-down ] || pinch-zoom ] | manipulation;

touch-action 屬性值

  • auto
    當觸控事件發生在元素上時,由瀏覽器來決定進行哪些操作,比如對viewport進行平滑、縮放等。
  • none
    當觸控事件發生在元素上時,不進行任何操作。
  • pan-x
    啟用單指水平平移手勢。可以與 pan-y 、pan-up、pan-down 和/或 pinch-zoom 組合使用。
  • pan-y
    啟用單指垂直平移手勢。可以與 pan-x 、pan-left 、pan-right 和/或 pinch-zoom 組合使用。
  • manipulation
    這是pan-x pan-y pinch-zoom的別名。
    瀏覽器只允許進行滾動和持續縮放操作,任何其它被auto值支援的行為不被支援。
    啟用平移和縮小縮放手勢,但禁用其他非標準手勢,例如雙擊以進行縮放。
    禁用雙擊可縮放功能可減少瀏覽器在使用者點選螢幕時延遲生成點選事件的需要。
    觸控動作也經常用於完全解決由支援雙擊縮放手勢引起的點選事件的延遲。
  • pinch-zoom
    啟用多手指平移和縮放頁面。
    這可以與任何平移值組合。
  • pan-left, pan-right,pan-up,pan-down
    啟用以指定方向滾動開始的單指手勢。 一旦滾動開始,方向可能仍然相反。
    多個方向可以組合。
    請注意,滾動向上(pan-up)意味著使用者正在將其手指向下拖動到螢幕表面上,同樣 pan-left 表示使用者將其手指向右拖動。
    這些值的相容性較差,Chrome支援,IE\Firefox\Safari不支援。

wheel 與 mousewheel

mousewheel 並不是標準特性,ie、chrome等瀏覽器支援,但 firefox 不支援。
firefox 支援的是自定義的 DOMMouseScroll 事件。

wheel 才是標準特性,現代瀏覽器基本都支援,建議使用 wheel 替代。

筆記本觸控板雙指滑動,將響應滾輪事件,因此可以監聽該事件,禁止觸控板手指縮放介面。
但需要注意滾輪預設事件,如會導致子元素滾動失效,這時候就要在子元素上阻止相同事件冒泡。

passive

passive 是作為屬性值,來自於事件監聽函式 addEventListener 的可選屬性 options
addEventListener 的第三個屬性還可以取布林值,指示是否能冒泡。
需要注意的是:對於第三個引數,IE只支援布林值,不支援屬性物件 options;而部分屬性值,也有瀏覽器不支援。

target.addEventListener(type, listener, options);

options引數

可選引數可用的屬性如下:

  • capture: Boolean,表示 listener 會在該型別的事件捕獲階段傳播到該 EventTarget 時觸發。
  • once: Boolean,表示 listener 在新增之後最多隻呼叫一次。如果是 true, listener 會在其被呼叫之後自動移除。
  • signal:AbortSignal,該 AbortSignal 的 abort() 方法被呼叫時,監聽器會被移除。Safari不支援。
  • mozSystemGroup: 只能在 XBL 或者是 Firefox' chrome 使用,這是個 Boolean,表示 listener 被新增到 system group。
  • passive: Boolean,設定為true時,表示 listener 永遠不會呼叫 preventDefault()。

passived 實際上就是告訴瀏覽器,某個事件監聽是否會使用 preventDefault 來阻止預設行為,便於瀏覽器優化效能。特別是瀏覽器優化頁面滾動時的效能,可以讓頁面滾動更順滑。
在Chrome中,wheel / touch 等事件中的 passive 會預設設定為true,但Safari不支援。
新增 passive 引數為true後,touchmove 事件不會阻塞頁面的滾動(同樣適用於滑鼠的滾輪事件)。

如果 passive 設定為true,而 listener 仍然呼叫了 preventDefault,則瀏覽器客戶端將會忽略它,並丟擲一個控制檯警告:

Unable to preventDefault inside passive event listener invocation。

可能遇到的報錯

Unable to preventDefault inside passive event listener due to target being treated as passive.

由於目標被視為被動,因此無法在被動事件偵聽器中阻止預設行為。
可能的發生情景:移動端使用touch事件後,垂直平移時的報錯。

方法一:使用 touch-action 樣式來禁止垂直平移的預設行為

touch-action: pan-y;

方法二:監聽 touch事件 中,明確設定 passive 為 false,宣告不是被動的。

document.addEventListener('touchmove', function (event) {
  event.preventDefault();
}, { passive: false });

相關文章