RSS

2011/07/22

可以觸摸的網頁

HTML5 之中定義了對於 Touch Device 相應的功能. 其中最重要的大概是在 JavaScript 之中的 touch event.

Touch event 跟 mouse event 某程度上相當相似. 三個 event 分別是 touchstart, touchmovetouchend. 這就好比 mouse event 當中的 mousedown, mousemovemouseup 三個 event.

不過, touch event 跟 mouse event 的最大分別, 就是它本身設計支援 multi-touch, 所以每個 event handler 接收回來的 event 會包含每一隻手指(或接觸點)相關的資訊.

先拿 touchstart event 做例子:


document.getElementById('someid')
.addEventListener('touchstart', function(event){
var firstFinger = event.targetTouches[0];
document.getElementById('messageBox').innerHTML = 'point ' + firstFinger.pageX + ',' + firstFinger.pageY + ' touched!';

}, false);


以上例子不太實用, 不過旨在先提供一點概念. 首先留意到的, 是 event.targetTouches 是一個 array, 代表著接觸這個 DOM element 的每一隻手指. (另外還有 event.touches 代表所有手指, 以及 event.changedTouches 代表著跟這個 event 有關的所有手指, 這個稍後再說明)

假設只會有 single touch 的情況下, 我可以直接抽起 array 之中的第一個項目使用. 在以上例子, 就當成是普通 mouse event 一樣, 利用 pageXpageY 找出座標使用.

touchmove 情況差不多, 就當手指在對應 DOM element 上移動時, touchmove event 就會包含每隻手指的位置. 舉個例:


document.getElementById('someid')
.addEventListener('touchmove', function(event){
var centroid = { x: 0, y: 0 };
for(var i=0; i<event.targetTouches.length; ++i) {
centroid.x += event.targetTouches[i].pageX;
centroid.y += event.targetTouches[i].pageY;
}
centroid.x /= event.targetTouches.length;
centroid.y /= event.targetTouches.length;

document.getElementById('messageBox').innerHTML =
'touch centroid: ' + centroid.x + ',' + centroid.y;

event.preventDefault();
}, false);



以上例子每當手指移動時, 就會計算出所有手指的中間位置然後顯示出來. 最主要的部份, 還是經過 event.targetTouches 這個 array 找出所有點然後處理.

使用 touchmove 時必須留意, 現時大部份 mobile browser 都會對應 touchmove 做 scrolling 效果, 尤其是當到達頁面盡頭時候的 bounch back 效果會影響到 touchmove event 的運作. 所以建議有需要時在最後加上 event.preventDefault() 去避免問題發生.

最後到 touchend event, 使用方法就有點不同了:


document.getElementById('someid')
.addEventListener('touchend', function(event){
var touch = event.changedTouches[0];
document.getElementById('messageBox').innerHTML
= 'touch end: ' + touch.pageX + ',' + touch.pageY;
}, false);


由於當 touchend 發生之時手指已經離開螢幕, 所以要知道真正 trigger touchend event 的手指位置的話, 就要靠 event.changedTouches 了.

組合三個 event 就可以為網頁設計 gesture 了. 方法是利用 touchstart 記錄 gesture 的開始, 用 touchmove 去記錄軌跡, 最後在 touchend event 分析 gesture 並作出反應. 舉例說, 要處理 single touch gesture 的話, 可以這樣做:


var Touch = {
startPos: null,
endPos: null,
init: function(elementID) {
document.getElementById(elementID)
.addEventListener('touchstart',
function(event) { Touch.start(event); }, false);
document.getElementById(elementID)
.addEventListener('touchmove',
function(event) { Touch.update(event); }, false);
document.getElementById(elementID)
.addEventListener('touchend',
function(event) { Touch.stop(event); }, false);
},
start: function(event) {
var touch = event.targetTouches[0];
this.startPos = { x:touch.pageX, y:touch.pageY };
},
update: function(event) {
var touch = event.targetTouches[0];
// TODO: record gesture
},
stop: function(event) {
var touch = event.changedTouches[0];
this.endPos = { x:touch.pageX, y:touch.pageY };

var gesture = this.analyseGesture();

alert("The gesture is: " + gesture);
},
analyseGesture: function() {
if(this.startPos && this.endPos) {
if(this.endPos.x > this.startPos.x+200)
return "swipe right";
else if(this.endPos.x < this.startPos.x-200)
return "swipe left";
else
return "unknown";
}
}
};


以上例子很懶惰的只利用開始及結束的位置去估計是向左還是向右 swipe, 要更準確的話就要想一想應該在 touchmove 時記錄甚麼來分析了. 要使用這個例子的話, 可以把 Touch.init 加到 onload event 那裡:


window.onload = function() { Touch.init('someElementID'); };


(有關 Object function 在 event handler 上的應用, 可參考上一篇 令人又愛又恨的 JavaScript function :P)

可惜的是, 現時只有少部份軟件在 JavaScript touch event 之中支援 multi-touch, 期待將來 multi-touch 為 mobile web page 所帶來的更好的 user interface.

有關 touch event, 我推介 HTML5ROCKS 的相關介紹, 當中有提及更多 touch event 相關資料, 包括已定義但未有軟件支援的部份, 以及各主要軟硬件的支援情況, 做得相當不錯.