觸控和滑鼠

首度再次攜手合作

引言

近三年來,電腦運算體驗是以鍵盤、滑鼠或觸控板做為主要的使用者輸入裝置。不過在過去十年間,智慧型手機和平板電腦新的互動模式:觸控模式。隨著 Windows 8 電腦問世,隨著配備觸控功能的 Windows 8 機器問世,如今除了以觸控式輔助 Chromebook Pixel 問世外,觸控功能也逐漸成為使用者預期的電腦體驗之一。最大的挑戰之一就是要打造同時適用於觸控式裝置和滑鼠裝置,以及使用者需要同時使用這兩種輸入法的裝置!

本文將說明瀏覽器內建的觸控功能、如何將這個新介面機制整合至現有應用程式,以及如何透過滑鼠輸入。

網路平台觸控狀態

iPhone 是第一個在網路瀏覽器中內建專屬觸控 API 的平台。多家瀏覽器供應商也建立了類似的 API 介面,以便與 iOS 實作項目相容,現在請參閱「觸控事件第 1 版規格」。支援觸控事件的方式適用於電腦版 Chrome 和 Firefox,以及 iOS 和 Chrome 上的 Safari、Android 的 Android 瀏覽器,以及其他行動瀏覽器 (例如 Blackberry 瀏覽器)。

我的同事 Boris Smus 針對「觸控事件」編寫了一篇實用的 HTML5Rocks 教學課程,如果還沒看過 Touch 事件,這仍然是很好的方法。事實上,如果您從未使用觸控事件,請先閱讀這篇文章,再繼續操作。繼續,我會等。

全部完成?現在,您已經對觸控事件有基本的基準,但是觸控事件的寫入互動方式,可能和滑鼠 (以及滑鼠模擬觸控板和軌跡球) 事件稍有不同。雖然觸控介面通常會嘗試模擬滑鼠,但這類模擬並不完美或完整,您必須分別支援這兩種互動樣式,而且可能需要獨立支援各個介面。

最重要的是:使用者可能會觸碰及滑鼠

許多開發人員建立的網站可靜態偵測環境是否支援觸控事件,並假設它們只需要支援觸控 (而非滑鼠) 事件。這現在是錯誤假設,只是因為存在觸控事件,並不代表使用者主要使用該觸控輸入裝置。Chromebook Pixel 和部分 Windows 8 筆記型電腦等裝置現已支援「滑鼠」和「觸控」輸入法,未來還會支援更多裝置。在這些裝置上,使用者通常會同時使用滑鼠和觸控螢幕與應用程式互動,因此「支援觸控功能」與「不需要滑鼠支援」並不相同。您不能將問題想成是「我必須撰寫兩種不同的互動樣式,然後切換」,就必須思考兩種互動如何相輔相成。我經常使用 Chromebook Pixel 的觸控板,但也能在相同的應用程式或頁面上,以最自然的方式做任何事情。另一方面,有些觸控螢幕筆電的使用者很少使用觸控螢幕,因此使用觸控輸入時,不應停用或阻礙滑鼠控制。

遺憾的是,很難判斷使用者的瀏覽器環境是否支援觸控輸入。在理想情況下,電腦上的瀏覽器一律會指出支援觸控事件,以便隨時附加觸控螢幕 (例如透過 KVM 連接的觸控螢幕)基於上述所有原因,您的應用程式不應嘗試在觸控和滑鼠之間切換,只要同時支援兩者!

支援滑鼠和觸控手勢

#1 - 點擊和輕觸 - 「自然」物列

第一個問題是觸控介面通常會嘗試模擬滑鼠點選動作,因為觸控介面必須適用於先前只與滑鼠事件互動的應用程式!您可以將這項動作當做捷徑使用,因為無論使用者以滑鼠點選或輕觸畫面上的手指,「click」事件都會持續觸發。但這組快速鍵仍有幾個問題。

設計更進階的觸控互動時,您必須謹慎處理:使用者使用滑鼠時,系統會透過點擊事件回應,但是當使用者輕觸螢幕時,就會同時發生觸控和點擊事件。若一次點擊,事件的順序為:

  1. 觸控啟動
  2. 觸控移動
  3. 輕觸
  4. 滑鼠游標懸停
  5. mousemove
  6. 滑鼠下移
  7. 老鼠
  8. click

當然,這表示如果您正在處理觸控事件等觸控事件,請確認不會同時處理相應的滑鼠下移和/或點擊事件。如果取消觸控事件 (在事件處理常式中呼叫 preventDefault()),系統不會產生任何觸控事件。觸控處理常式最重要的規則之一如下:

不過,這麼做也會導致其他預設瀏覽器行為 (例如捲動) 無法執行。雖然一般來說,觸控事件通常是在處理常式中處理,但建議您停用預設動作。一般而言,建議您處理並取消所有觸控事件,或者避免為該事件使用處理常式。

其次,當使用者在行動裝置上輕觸網頁中的元素時,尚未設計用於行動互動的網頁在觸控開始事件與滑鼠事件處理 (滑鼠向下) 之間會有至少 300 毫秒的延遲時間。你可以使用 Chrome 執行這項作業,可以在 Chrome 開發人員工具中啟用「模擬觸控事件」功能,以便測試非觸控系統上的觸控介面!

這段延遲時間可讓瀏覽器時間判斷使用者是否正在執行其他手勢,尤其是輕觸兩下縮放。這是很明顯的問題,可幫助使用者即時回應手指輕觸動作。目前有「進行中的工作」,嘗試限制會自動產生延遲的情況。

Android 版 Google Chrome Android 瀏覽器 Opera Mobile Android 版) Android 版 Firefox Safari iOS 版
無法縮放的可視區域 無延遲 300 毫秒 300 毫秒 無延遲 300 毫秒
沒有可視區域 300 毫秒 300 毫秒 300 毫秒 300 毫秒 300 毫秒

要避免這種延遲,最簡單的方法就是向行動瀏覽器「告知」網頁不需縮放,可使用固定的檢視區 (例如將程式碼插入網頁中):

<meta name="viewport" content="width=device-width,user-scalable=no">

當然,這不一定是適當的,這會停用雙指撥動縮放功能 (基於存取原因而可能需要使用),因此請謹慎使用 (如果您停用使用者縮放功能,建議您以其他方式,提升應用程式的文字易讀性)。此外,如果是電腦類別裝置的 Chrome (支援觸控功能) 和行動平台上的其他瀏覽器 (當網頁設有無法擴充的可視區域時),則並不適用這項延遲情況

#2:觸控事件不會觸發滑鼠移動事件

請特別注意,這時在觸控介面中模擬滑鼠事件時,並不會延伸執行滑鼠移動事件。因此,如果您建構了一個使用滑鼠移動事件的精美滑鼠驅動控制項,除非您明確新增觸控移動處理常式,否則可能無法與觸控裝置搭配使用。

瀏覽器通常會自動在 HTML 控制項中實作適當的觸控互動機制,因此,只有在使用觸控互動時,HTML5 範圍控制項才能運作。不過,如果您實作自己的控制項,這些控制項可能無法用於點按及拖曳類型互動;事實上,某些常用的程式庫 (例如 jQueryUI) 尚未以這種方式原生支援觸控互動 (雖然關於 jQueryUI,這個問題還有一些 monkey-patch 修正程式)。這是我升級 Web Audio Playground 來配合使用時碰到的問題之一:滑桿是以 jQueryUI 為基礎,所以無法順利執行點選及拖曳互動。改用 HTML5 範圍控制項後,我恢復了原本的設定。當然,我也可以加入觸控移動處理常式來更新滑桿,但這個做法有個問題...

#3:觸控移動與滑鼠移動不是相同的項目

有些開發人員曾碰到一個陷入困境,那就是在相同程式碼路徑中呼叫觸控移動和滑鼠移動處理常式。這些事件的行為非常非常接近,但差異甚大,特別是觸控事件一律會指定輕觸「開始」的元素,而滑鼠事件則會指定滑鼠遊標目前下方的元素。這就是為什麼我們有滑鼠遊標懸停和滑鼠懸停事件,但沒有相應的觸控和觸控事件,只能觸碰。

最常見的做法是移除 (或重新調整位置) 使用者開始觸控的元素時,舉例來說,假設整個輪轉介面上有觸控處理常式的圖片輪轉介面,可支援自訂捲動行為。可用的圖片變更時,您會移除一些 <img> 元素並新增其他元素。如果使用者剛輕觸其中一張圖片,而您移除圖片,則處理常式 (位於 img 元素祖系) 只會停止接收觸控事件 (因為系統會將其分派給不在樹狀結構中的目標),看起來即使手指已移動,且最終將手指移開,這看起來會像使用者一直拿著手指。

當然,在觸控作業啟用時,移除包含 (或包含祖系) 觸控處理常式的元素,可以避免這個問題。或者,最佳做法是不要註冊靜態的觸控結束/Touchmove 處理常式,並等到收到觸控事件後,再將觸控移動/Touchend/Touchcancel 處理常式新增至觸控開始事件的 target (並在結束/取消時移除)。這樣一來,即使目標元素遭到移動/移除,您仍會繼續接收觸控事件。如想玩這個遊戲,請按一下這裡,輕觸紅色方塊並按住 Esc 鍵,即可從 DOM 中移除它。

#4:輕觸並:懸停

滑鼠遊標指標會將遊標位置與主動選取項目分隔,方便開發人員使用懸停狀態來隱藏及顯示可能與使用者相關的資訊。然而,大多數的觸控介面現在不會偵測到目標上有任何手指「懸停」的情形,因此除非您同時提供方便觸控存取的資訊,否則透過懸停操作提供語意上的重要資訊 (例如提供「這個控制項是什麼?」彈出式視窗) 並非不行。在使用懸停將資訊轉發給使用者時,請務必謹慎小心。

有趣的是,雖然某些情況下,觸控介面能夠觸發 CSS :hover 虛擬類別 - 輕觸元素會在手指往下移動時使其 :active 並獲取 :hover 狀態。(在 Internet Explorer 中,:hover 只有在使用者將手指往下移時才會生效,其他瀏覽器保持有效狀態,直到下一次輕觸或移動滑鼠為止。這個做法有助於讓彈出式選單在觸控介面上正常運作,讓元素啟用後產生的副作用是套用 :懸停狀態。例如:

<style>
img ~ .content {
  display:none;
}

img:hover ~ .content {
  display:block;
}
</style>

<img src="/awesome.png">
<div class="content">This is an awesome picture of me</div>

輕觸其他元素後,該元素就會失效,而懸停狀態也會消失,就像使用者使用滑鼠遊標並將其移出元素時一樣。建議您將內容納入 <a> 元素中,讓內容成為定位點,讓使用者無需任何 JavaScript,即可切換滑鼠懸停或點選、輕觸或按鍵的額外資訊。我很驚訝地發現,開始著手打造 Web Audio Playground,以搭配觸控介面使用,我的彈出式選單已經可以發揮良好成效,因為我採用了這種結構!

上述方法適用於滑鼠指標介面和觸控介面。這與在遊標懸停時使用「名稱」屬性有所不同,後者在元素啟用時「不會」顯示:

<img src="/awesome.png" title="this doesn't show up in touch">

#5:觸控與滑鼠的精確度

雖然滑鼠與現實之間存在概念上的關聯,但事實是完全準確,因為基礎作業系統一般會追蹤遊標的確切像素精確度。另一方面,行動應用程式開發人員發現在觸控螢幕上輕觸手指時會不夠準確,主要是因為與螢幕接觸時,手指的表面區域大小 (有部分是因為手指遮蓋了螢幕)。

許多個人和公司都針對如何設計可滿足手指互動的應用程式和網站進行了廣泛的使用者研究,其中許多書籍都已在這個主題撰寫。基本建議是透過增加邊框間距,增加觸控目標的大小,同時藉由增加元素之間的距離,降低輕觸位置不正確的機率。(在處理觸控和點擊事件的命中偵測作業中,邊界不包含邊界,而邊框間距為)。我對 Web Audio Playground 做出的一項主要修正之一是增加連接點的大小,以便更準確地觸碰。

許多處理觸控式介面的瀏覽器供應商也會在瀏覽器中導入邏輯,以便在使用者輕觸螢幕時指定正確的元素,並降低發生點擊錯誤的可能性,雖然通常只會修正點擊事件,不會改變,但 Internet Explorer 似乎也會修改滑鼠下移/滑鼠移動/滑鼠移動事件。

#6:保留觸控處理常式,否則對方會你的捲動頁面

此外,請務必只將觸控處理常式限制在必要元素的位置;觸控元素可能會極高,因此請避免在捲動元素上使用觸控處理常式 (因為您的處理程序可能會幹擾瀏覽器針對快速無卡的觸控捲動體驗進行最佳化調整 - 新式瀏覽器會嘗試捲動瀏覽 GPU 執行緒,但如果是必須先檢查 GPU 執行緒,就不可能進行此操作)。查看這項行為的範例

如要避免這個問題,其中一項指引是確保如果您只處理 UI 的少部分觸控事件,請只附加觸控處理常式 (而非頁面的 <body> 等)。簡而言之,請盡可能限制觸控處理常式的範圍。

#7:多點觸控

最後一個有趣的挑戰是,我們將這個介面稱為「觸控」使用者介面,但幾乎普遍支援多點觸控功能,也就是 API 一次會提供多個觸控輸入。開始支援應用程式時,建議您考量多次接觸應用程式可能對應用程式造成的影響。

如果建構的應用程式主要是由滑鼠驅動,而您會使用最多一個遊標點來建構應用程式,系統通常不支援多個滑鼠遊標。對於許多應用程式,您只會將觸控事件對應至單一遊標介面,但我們發現桌面觸控輸入的大多數硬體可以同時處理至少 2 項輸入,而且大多數新硬體似乎可以同時支援至少 5 個輸入來源。當然,為了開發螢幕鋼琴鍵盤,您可能會想要支援多個同時觸控輸入。

目前實作的 W3C Touch API 沒有 API 能判斷硬體支援的接觸點數量,因此您必須根據使用者可能想要的接觸點估算出最佳設定,或者註意實際顯示的接觸點數量並隨之調整。舉例來說,在鋼琴應用程式中,如果您從未看到超過兩個觸控點,建議您加入一些「和弦」使用者介面。PointerEvents API 確實具有可判斷裝置功能的 API。

修飾

希望本文介紹了實作觸控與滑鼠互動時常見的挑戰。當然,除了任何其他建議外,您必須在行動裝置、平板電腦,以及結合滑鼠和觸控式桌面環境中測試應用程式。如果您沒有觸控 + 滑鼠硬體,請使用 Chrome 的「模擬觸控事件」協助您測試各種情境。

不可能,但按照上述指南操作相對簡單,即可打造引人入勝的互動體驗,並同時支援觸控輸入、滑鼠輸入,甚至是兩種互動方式。