和 Firefox OS 介面的第一次親密接觸 – 基本觸控手勢看招!

你習慣用手指在螢幕上滑來滑去,用手勢 ( gesture ) 代替你處理大小事嗎?

在現今行動裝置的世界裡,手勢 ( gesture ) 儼然成為與裝置溝通的最主要管道。無須其他的輸入設備,動動手指就可輕易將手中的手機或平板操控自如。不管是 Android 、iOS 甚至 Windows 都有針對手勢操控,提供了完整的 library 供開發者使用。至於在標準 HTML 前端架構的 Firefox OS(以下簡稱 FFOS )上該如何活用手勢呢?筆者接下來針對手勢功能的基本介紹,希望讓大家對於 FFOS 的介面開發更加輕鬆上手。

相信 web 開發者一定對 mouse event 不陌生。不過隨著時代的演進,touch event 後來也列入 HTML5 的標準,大致上 touch event 的用法跟 mouse event 相去不遠。 Mouse event 當中的 mousedown、mousemove 及 mouseup 就同等於 touch event 中的 touchstart、touchmove 及 touchend。差別在於 touch 還有 touches(所有觸控點) / targetTouche(所有在 target 上的觸控點) / changedTouches(狀態有改變的觸控點) 以 array 型態來紀錄觸控點的資料。假設現在有一張圖片需套用到縮放的手勢:

var startDistance, startScale; image.addEventListener("touchstart", handleStart, false); image.addEventListener("touchmove", handleMove, false); image.addEventListener("touchend", handleEnd, false);

function distance(finger1, finger2) { // 利用觸控點的相對座標( finger.pageX , finger.pageY )計算兩點之間的距離 ... return result; }

function queryScale(image) { // 從 style 中擷取縮放比例: ... return scale; }

function handleStart(evt) { // 當有兩指在圖片上,我們要先計算兩點之間的初始距離 if (event.targetTouches.length == 2) { startDistance = distance(event.targetTouches[0], event.targetTouches[1]); startScale = queryScale(image); ... } }

function handleMove (evt) { // 計算目前兩指間的距離,並設定圖片的縮放比例 if (event.targetTouches.length == 2) { var currentDistance = distance(event.targetTouches[0], event.targetTouches[1]); var newScale = currentDistance / startDistance * startScale; image.style.transform = 'scale(' + newScale + ')'; } ... }

function handleEnd (evt) { // 當結束touch event時,可以在這裡定義自己想要的動作 ... }

Touch event 的概念也許不算複雜,但麻煩的部份在於邏輯計算。上述功能只是簡單的縮放,如果還要考慮上旋轉可能就令人頭大了。若你對 iOS 上的 web framework 開發有所涉獵,可能會馬上想到用 gesture event 的 gesturestart / gesturechange / gestureend 來進行其他常用的手勢操作,而縮放與旋轉等等的資訊,則會包含在事件回傳的物件裡,無須再從觸控點做進一步的運算,回傳的 gesture event 資料也能一目了然,所以問題就這麼迎刃而解了…嗎?答案當然是否定的,因為 gesture event 並非標準的 HTML 規範,而是 iOS 在 webkit 中從 touch event 再包裝出來的。這麼一來開發者就只能從最基本的 touch object 中,擷取資料做複雜的運算,不然就得再花時間去研究其他第三方的 gesture library 了嗎?先別急!其實 FFOS 內部就有一個共用的 gesture library,供核心應用程式使用,而開發者也可以下載來自行應用,雖然目前還沒有正式的文件,但以下你可參考簡單介紹以及如何使用和操作。

這個 Gesture Detector (原始碼請按此)目前可以支援的手勢有:tap(單點)、dbltap(雙點擊)、pan(拖移)、swipe(快速滑動,會接在 pan event 結束之後觸發)、holdstart / holdmove / holdend(長按之後觸發)、transform(兩指的縮放與旋轉)。只要簡單的幾個步驟就能輕鬆上手:

  • 下載 Gesture Detector library 並載入至開發中的 app

使用 Gesture Detector library 不需要其他 JavaScript framework ,只要載入 gesture_detector.js 這個檔案就對了。

  • 對 target element 註冊 event detector

// 針對 targetElement 跟 GestureDetector 註冊 new GestureDetector(targetElement).startDetecting(); // 如果 targetElement 有需要 hold gesture 相關 event,則需要這麼宣告: // new GestureDetector(targetElement, {holdEvents:true}).startDetecting(); new GestureDetector(targetElement).stopDetecting();

  • 對 element 新增 gesture type event listerner 以及對應的 handler function

// 對特定的 gesture event 增加對應的 event handler function: targetElement.addEventListener('pan', panHandler); targetElement.addEventListener('swipe', swipeHandler); ... // 實作 pan gesture 的 event handler function function panHandler(event) { var dx = event.detail.relative.dx; var dy = event.detail.relative.dy; ... }

這部份就跟一般的 event handler 實作一樣了。比較令人困擾的是每個 gesture event 回傳的 event object 並不完全一樣,偏偏這個 libray 目前還沒有完整的文件,直接看原始碼是最直接的方法,不過這裡還是簡單介紹幾種常用的,有興趣的開發者可以在 gesture_detector 中搜尋 “emitEvent” ,觀察各種 gesture event 在發送之前是怎麼包裝的。先介紹有旋轉和縮放相關的 transform(待會我們也會使用到):

absolute: { // 相對於 gesture 起始點的縮放比例和角度 scale: 縮放比例, rotate: 旋轉角度 }, relative: { // 相對於上一個 gesture change 的縮放比例和角度 scale: distance / d.lastDistance, rotate: touchRotation(d.lastDirection, direction) }, midpoint: midpoint // 兩指的中心點位置

其他像是 tap / dbltap / holdstart 則有 start (觸發點位置),而 pan/holdmove 則是有 absolute / relative 的 dx / dy 來表示相對於起始點或是上一個 gesture change event 改變的 x / y 相對距離。 swipe 則帶有較多的資訊: start、end 表示起始和結束的座標位置,dx、dy、dt 是起始至結束的相對移動 x,y 距離以及時間,vx,vy是 x,y 方向的速率,而direction,angle 則是 swipe 的方向(left , right , up , down )和角度。

有了新的 gesture event ,圖片的縮放可以這樣改寫:

// 別忘了要先載入 gesture_detector.js new GestureDetector(image).startDetecting(); image.addEventListener('transform', transformHandler); function transformHandler(evt) { var currentScale = evt.detail.relative.scale * queryScale(img); image.style.transform = 'scale(' + currentScale + ')'; }

利用 Gesture Detector,開發起來就輕鬆多了,不需要計算縮放的比例、角度等等的邏輯,而且它並沒有使用 preventDefault 或是 stopPropagation 將元件的 touch event 吃掉,也就是套用了 Gesture Detector 的元件,依然可以收到基本的 touch event 來進行其他應用,例如定義兩指以上的手勢操作。這麼輕鬆且有彈性的特色,是否讓你迫不期待的想要設計一些方便的手勢操作,讓應用程式操作起來更靈活呢?

以上就是在 FFOS 開發中對手勢應用的一些基本介紹了,如果在開發的過程中有任何問題也歡迎在這裡留言,苦命熱情的工程師會盡可能的回答你的問題喔~