首次合作
簡介
近三十年來,桌上型電腦的運算體驗一直以鍵盤、滑鼠或觸控板為主要使用者輸入裝置。不過,過去十年來,智慧型手機和平板電腦帶來了新的互動模式:觸控。隨著支援觸控的 Windows 8 機器問世,以及支援觸控的 Chromebook Pixel 的推出,觸控功能已成為桌面電腦的預期體驗。其中一個最大挑戰,就是打造的體驗不僅適用於觸控裝置和滑鼠裝置,也適用於使用者同時使用這兩種輸入方式的裝置!
本文將說明瀏覽器如何內建觸控功能、如何將這個全新介面機制整合至現有應用程式,以及觸控功能如何與滑鼠輸入互動。
網路平台的觸控功能狀態
iPhone 是第一個在網路瀏覽器中內建專屬觸控 API 的熱門平台。其他幾家瀏覽器供應商也已建立類似的 API 介面,以便與 iOS 實作相容,而這項實作現在已由 「Touch Events version 1」規格說明。觸控事件支援 Chrome 和 Firefox 桌面版、iOS 上的 Safari、Chrome 和 Android 上的 Android 瀏覽器,以及 Blackberry 瀏覽器等其他行動瀏覽器。
我的同事 Boris Smus 撰寫了一篇 HTML5Rocks 教學課程,主題是觸控事件,如果您之前沒有接觸過觸控事件,這篇教學課程仍是個不錯的入門方式。事實上,如果您之前沒有使用過觸控事件,請先閱讀該篇文章,再繼續閱讀本文。繼續說,我會等。
完成了嗎?您現在已掌握觸控事件的基本知識,但撰寫支援觸控的互動時,會遇到以下挑戰:觸控互動與滑鼠 (以及模擬滑鼠的觸控板和軌跡球) 事件有相當大的差異。雖然觸控介面通常會模擬滑鼠,但模擬效果並非完美或完整,因此您必須同時處理這兩種互動樣式,且可能必須個別支援每種介面。
最重要的是:使用者可能會使用觸控和滑鼠
許多開發人員建立的網站會以靜態方式偵測環境是否支援觸控事件,然後假設只需要支援觸控 (而非滑鼠) 事件。這項假設現在已不正確,因為即使有觸控事件,也不代表使用者主要使用該觸控輸入裝置。Chromebook Pixel 和部分 Windows 8 筆電等裝置現在同時支援滑鼠和觸控輸入方式,不久後還會有更多裝置加入。在這些裝置上,使用者很自然地會同時使用滑鼠和觸控螢幕與應用程式互動,因此「支援觸控」與「不需要滑鼠支援」並不相同。您不能將問題視為「我必須編寫兩種不同的互動樣式,並在兩者之間切換」,而是要思考兩種互動如何同時運作,以及如何獨立運作。在 Chromebook Pixel 上,我經常使用觸控板,但也會伸手觸控螢幕。在同一個應用程式或網頁中,我會使用最自然的方式操作。另一方面,有些使用觸控螢幕筆記型電腦的使用者幾乎不會使用觸控螢幕,因此觸控輸入功能不應停用或阻礙滑鼠控制。
很遺憾,我們很難得知使用者的瀏覽器環境是否支援觸控輸入功能;理想情況下,電腦上的瀏覽器應一律支援觸控事件,這樣使用者隨時都能連接觸控螢幕 (例如透過 KVM 連接的觸控螢幕)。基於上述所有原因,應用程式不應嘗試在觸控和滑鼠之間切換,而是應同時支援這兩種方式!
同時支援滑鼠和觸控
#1 - 點按和輕觸 - 事物的「自然」順序
第一個問題是,觸控介面通常會嘗試模擬滑鼠點擊動作,因為觸控介面必須在只與滑鼠事件互動的應用程式上運作!您可以將這項功能用作捷徑,因為無論使用者是用滑鼠點擊,還是用手指輕觸螢幕,「點擊」事件都會繼續觸發。不過,這個捷徑有幾個問題。
首先,設計更進階的觸控互動時,請務必小心:使用者使用滑鼠時,系統會透過點擊事件回應,但使用者觸控螢幕時,系統會同時觸發觸控和點擊事件。單擊事件的事件順序如下:
- touchstart
- touchmove
- touchend
- 滑鼠游標懸停
- mousemove
- mousedown
- mouseup
- click
這當然表示,如果您要處理 touchstart 等觸控事件,請務必不要同時處理對應的 mousedown 和/或 click 事件。如果您可以取消觸控事件 (在事件處理常式中呼叫 preventDefault() 即可),系統就不會為觸控產生滑鼠事件。觸控處理常式最重要的規則之一是:
不過,這也會阻止其他預設瀏覽器行為 (例如捲動),雖然通常您會在處理常式中完全處理觸控事件,但您還是會想要停用預設動作。一般來說,您應該要處理並取消所有觸控事件,或是避免為該事件提供處理常式。
其次,當使用者在行動裝置上輕觸網頁中的元素時,如果網頁並非針對行動互動設計,則在觸發 touchstart 事件和處理滑鼠事件 (mousedown) 之間,會有至少 300 毫秒的延遲。您可以使用 Chrome 執行這項操作,在 Chrome 開發人員工具中開啟「模擬觸控事件」,即可在非觸控系統上測試觸控介面!
這個延遲時間可讓瀏覽器有時間判斷使用者是否正在執行其他手勢,特別是雙擊放大/縮小手勢。顯然,如果您希望系統對手指觸碰立即做出回應,這可能會造成問題。我們正在努力,試圖減少自動發生延遲的情況。
避免這項延遲的首要且最簡單的方法,就是「告知」行動瀏覽器您的網頁不需要縮放,這可以透過固定可視區域來實現,例如在網頁中插入以下內容:
<meta name="viewport" content="width=device-width,user-scalable=no">
當然,這並非一項萬能解決方案,因為這樣做會停用雙指縮放功能,而這項功能可能會因為無障礙性而變得必要,因此請盡量少用 (如果您確實要停用使用者縮放功能,建議您提供其他方式,讓使用者在應用程式中更容易閱讀文字)。此外,如果電腦類裝置支援觸控功能,且行動平台上的其他瀏覽器的網頁具有無法縮放的可視區域,就不會套用這項延遲時間。
#2:觸控事件未由觸控觸發
在此要特別注意,觸控介面中模擬滑鼠事件通常不會擴充至模擬 mousemove 事件,因此如果您建立使用 mousemove 事件的漂亮滑鼠控制項,除非您特別新增 touchmove 處理常式,否則可能無法在觸控裝置上運作。
瀏覽器通常會自動在 HTML 控制項上實作適當的觸控互動,舉例來說,HTML5 Range 控制項只會在使用觸控互動時運作。不過,如果您已實作自己的控制項,這些控制項可能無法在點選和拖曳類型的互動中運作;事實上,某些常用的程式庫 (例如 jQueryUI) 尚未原生支援這種方式的觸控互動 (不過,jQueryUI 有幾個猴子修補程式可修正這個問題)。這是我將 Web Audio Playground 應用程式升級為支援觸控功能時遇到的第一個問題 - 滑桿是基於 jQueryUI,因此無法搭配點選和拖曳互動。我改用 HTML5 範圍控制項,結果成功了。當然,我也可以直接新增 touchmove 處理常式來更新滑桿,但這樣做有一個問題…
#3:Touchmove 和 MouseMove 並非相同
我發現有幾位開發人員陷入了一個陷阱,就是讓 touchmove 和 mousemove 處理常式呼叫相同的程式碼路徑。這些事件的行為非常相似,但仍有細微差異,特別是觸控事件一律會指定觸控開始的元素,而滑鼠事件則會指定滑鼠游標目前所在的元素。因此,我們有 mouseover 和 mouseout 事件,但沒有對應的 touchover 和 touchout 事件,只有 touchend。
最常見的情況是,您不小心移除 (或重新定位) 使用者開始觸碰的元素。舉例來說,假設圖片輪轉介面在整個輪轉介面上有觸控處理常式,可支援自訂捲動行為。隨著可用的圖片變更,您會移除部分 <img>
元素並新增其他元素。如果使用者剛好開始觸碰其中一個圖片,而您隨後移除該圖片,則處理常式 (位於 img 元素的祖系) 就會停止接收觸控事件 (因為事件會分派至不再位於樹狀結構中的目標),使用者看起來就像是將手指放在同一個位置,即使他們可能已移動並最終移除手指也一樣。
當然,您也可以避免在觸控功能啟用時移除具有 (或具有具有) 觸控處理常式的元素,以免發生這個問題。另外,最佳做法是不要註冊靜態 touchend/touchmove 處理常式,而是等到收到 touchstart 事件後,再將 touchmove/touchend/touchcancel 處理常式新增至 touchstart 事件的 target (並在結束/取消時移除)。這樣一來,即使目標元素已移動/移除,您仍可繼續接收觸控事件。您可以稍微玩玩這個這裡 - 觸碰紅色方塊,並按住 Escape 鍵,將其從 DOM 中移除。
#4:觸控和懸停
滑鼠游標隱喻可將游標位置與主動選取分開,讓開發人員使用懸停狀態隱藏及顯示可能與使用者相關的資訊。不過,目前大多數的觸控介面都無法偵測手指是否在目標上「懸停」,因此除非您也提供觸控友善的方式來存取這項資訊,否則請勿根據懸停提供重要語意資訊 (例如提供「這項控制項是什麼?」彈出式視窗)。您必須謹慎使用懸停功能,以便向使用者傳達資訊。
不過有趣的是,在某些情況下,CSS :hover 擬類別可由觸控介面觸發,輕觸元素會在手指按下時使其處於 :active 狀態,並且取得 :hover 狀態。(在 Internet Explorer 中,:hover 只會在使用者手指按下時生效,其他瀏覽器則會在下一次輕觸或滑鼠移動時,讓 :hover 生效)。這是在觸控介面上讓彈出式選單運作的絕佳方法,因為讓元素處於活動狀態的副作用,就是也會套用 :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>
元素中,以便將其設為 Tab 停靠點。這樣一來,使用者只要將滑鼠游標懸停或點選、輕觸或按下按鍵,即可切換額外資訊,而不需要 JavaScript。當我開始著手讓 Web Audio Playground 與觸控介面搭配運作時,我發現彈出式選單在觸控時運作良好,這讓我感到驚喜,因為我使用了這種結構!
上述方法適用於以滑鼠游標為基礎的介面,也適用於觸控介面。這與在游標上方使用「title」屬性不同,後者在元素啟用時不會顯示:
<img src="/awesome.png" title="this doesn't show up in touch">
#5:觸控與滑鼠精確度
雖然滑鼠與實際情況不符,但實際上它們非常精確,因為基礎作業系統通常會追蹤游標的確切像素精確度。另一方面,行動裝置開發人員發現,手指觸碰觸控螢幕的準確度不高,主要是因為手指與螢幕接觸時的表面積大小 (以及手指遮住螢幕的部分)。
許多個人和公司已針對如何設計可支援手指互動的應用程式和網站進行廣泛的使用者研究,並且有許多書籍討論這個主題。基本建議是增加邊框間距,以便增加觸控目標的大小,並增加元素間的邊距,以降低誤觸的可能性。(邊距不會納入觸控和點擊事件的命中偵測處理,但邊框會納入)。我必須對 Web Audio Playground 進行的主要修正之一,就是增加連接點的大小,讓使用者更容易精準地觸碰。
許多處理以觸控為基礎的介面瀏覽器供應商也已在瀏覽器中導入邏輯,以便在使用者觸碰螢幕時鎖定正確的元素,並降低點按錯誤的可能性。不過,這項功能通常只會修正點擊事件,而非移動事件 (雖然 Internet Explorer 似乎也會修改 mousedown/mousemove/mouseup 事件)。
#6:請將觸控事件處理常式控制在範圍內,否則會影響捲動速度
請務必將觸控處理常式限制在所需元素上。觸控元素的頻寬可能非常高,因此請避免在捲動元素上使用觸控處理常式 (因為您的處理作業可能會干擾瀏覽器的最佳化作業,導致觸控捲動速度緩慢且不流暢 - 新式瀏覽器會嘗試在 GPU 執行緒上捲動,但如果先透過 JavaScript 檢查每個觸控事件是否會由應用程式處理,就無法執行這項作業)。您可以查看這項行為的示例。
為了避免發生這個問題,請務必遵循以下指引:如果您只處理 UI 的一小部分觸控事件,請只在該部分附加觸控處理常式 (例如,不要在網頁的 <body>
上附加);簡而言之,請盡可能限制觸控處理常式的範圍。
#7:多點觸控
最後一個有趣的挑戰是,雖然我們一直稱之為「Touch」使用者介面,但幾乎所有支援的 API 其實都是多點觸控,也就是說,API 一次提供多個觸控輸入。開始在應用程式中支援觸控功能時,請考量多點觸控可能對應用程式造成的影響。
如果您主要以滑鼠操作建構應用程式,那麼您習慣最多使用一個游標點來建構應用程式,因為系統通常不支援多個滑鼠游標。在許多應用程式中,您只需將觸控事件對應至單一游標介面,但我們發現,大多數的電腦觸控輸入硬體至少可處理 2 個同時輸入,而大多數新硬體似乎至少支援 5 個同時輸入。開發螢幕鋼琴鍵盤時,您當然會想支援多個同時觸控輸入。
目前已實作的 W3C Touch API 沒有用於判斷硬體支援多少觸控點的 API,因此您必須盡力估算使用者需要的觸控點數量,或者留意實際看到的觸控點數量,並加以調整。舉例來說,如果在鋼琴應用程式中,您從未看到超過兩個觸控點,建議您新增一些「和弦」UI。PointerEvents API 確實有 API 可判斷裝置的功能。
修飾
希望這篇文章能為您提供一些指引,協助您克服在實作觸控和滑鼠互動時遇到的常見問題。當然,比起其他建議,您更需要在行動裝置、平板電腦和結合滑鼠和觸控功能的電腦環境中測試應用程式。如果沒有觸控螢幕 + 滑鼠硬體,請使用 Chrome 的「模擬觸控事件」功能,測試不同的情境。
您可以按照這些指引,輕鬆建構出能與觸控輸入、滑鼠輸入,甚至同時支援這兩種互動方式的互動體驗。