觸摸事件:
三種在規(guī)范中列出并獲得跨移動(dòng)設(shè)備廣泛實(shí)現(xiàn)的基本觸摸事件:
1.touchstart:手指放在一個(gè)DOM元素上。
2.touchmove:手指拖曳一個(gè)DOM元素。
3.touchend:手指從一個(gè)DOM元素上移開。
每個(gè)觸摸事件都包括了三個(gè)觸摸列表:
1.touches:當(dāng)前位于屏幕上的所有手指的一個(gè)列表。
2.targetTouches:位于當(dāng)前DOM元素上的手指的一個(gè)列表。
3.changedTouches:涉及當(dāng)前事件的手指的一個(gè)列表。
例如,在一個(gè)touchend事件中,這就會(huì)是移開的手指。
這些列表由包含了觸摸信息的對(duì)象組成:
1.identifier:一個(gè)數(shù)值,唯一標(biāo)識(shí)觸摸會(huì)話(touchsession)中的當(dāng)前手指。
2.target:DOM元素,是動(dòng)作所針對(duì)的目標(biāo)。
3.客戶/頁面/屏幕坐標(biāo):動(dòng)作在屏幕上發(fā)生的位置。
4.半徑坐標(biāo)和rotationAngle:畫出大約相當(dāng)于手指形狀的橢圓形。
可觸控應(yīng)用:
touchstart、touchmove和touchend事件提供了一組足夠豐富的功能來支持幾乎是任何類型的基于觸摸的交互——其中包括常見的多點(diǎn)觸摸手勢,比如說捏縮放、旋轉(zhuǎn)等待。下面的這段代碼讓你使用單指觸摸來四處拖曳一個(gè)DOM元素:
var obj = document.getElementById('id');
obj.addEventListener( 'touchmove',function(event){
? ? ?//如果這個(gè)元素的位置內(nèi)只有一個(gè)手指的話
? ? ?if( event.targetTouches.length == 1 ){
? ? ? ? ? var touch = event.targetTouches[ 0 ];
? ? ? ? ? //把元素放在手指所在的位置
? ? ? ? ? obj.style.left = touch.pageX+'px';
? ? ? ? ? obj.style.top = touch.pageY+'px';
? ? ? }
}, false );
下面是一個(gè)示例,該例子顯示了屏幕上當(dāng)前所有的觸點(diǎn),它的作用就是用來感受一下設(shè)備的響應(yīng)性。
//設(shè)置畫布并通過ctx變量來暴露上下文復(fù)制代碼
canvas.addEventListener( 'touchmove',function( event ){
? ? ?for(var i=0;i){
? ? ?var touch=event.touches;
? ? ?ctx.beginPath();
? ? ?ctx.arc(touch.pageX,touch.pageY,20,0,2*Math.PI,true);
? ? ? ctx.fill();
? ? ? ctx.stroke();
? ? ?}
},false);
到處都有著許多有意思的多點(diǎn)觸摸演示,比如說這個(gè)由PaulIrish和其他人實(shí)現(xiàn)的基于畫布的繪畫演示。
還有BrowserNinja,一個(gè)技術(shù)演示,是一個(gè)使用了CSS3的轉(zhuǎn)換、過渡和畫布的FruitNinja克隆。
最佳做法
阻止縮放:
缺省的多點(diǎn)觸摸設(shè)置不是特別的好用,因?yàn)槟愕幕瑒?dòng)和手勢往往與瀏覽器的行為有關(guān)聯(lián),比如說滾動(dòng)和縮放。
要禁用縮放功能的話,使用下面的元標(biāo)記設(shè)置你的視圖區(qū)(viewport),這樣其對(duì)于用戶來說就是不可伸縮的了:
content = "width=device-width,initial-scale=1.0,user-scalable=no">
阻止?jié)L動(dòng):
一些移動(dòng)設(shè)備有缺省的touchmove行為,比如說經(jīng)典的iOSoverscroll效果,當(dāng)滾動(dòng)超出了內(nèi)容的界限時(shí)就引發(fā)視圖反彈。這種做法在許多多點(diǎn)觸控應(yīng)用中會(huì)帶來混亂,但要禁用它很容易。
document.body.addEventListener('touchmove',function(event){
? ? ? ?event.preventDefault();
},false);
細(xì)心渲染:
如果你正在編寫的多點(diǎn)觸控應(yīng)用涉及了復(fù)雜的多指手勢的話,要小心地考慮如何響應(yīng)觸摸事件,因?yàn)橐淮我幚磉@么多的事情??紤]一下前面一節(jié)中的在屏幕上畫出所有觸點(diǎn)的例子,你可以在有觸摸輸入的時(shí)候就立刻進(jìn)行繪制:
canvas.addEventListener('touchmove',function(event){
? ? ?renderTouches(event.touches);
},
不過這一技術(shù)并不是要隨著屏幕上的手指個(gè)數(shù)的增多而擴(kuò)充,替代做法是,可以跟蹤所有的手指,然后在一個(gè)循環(huán)中做渲染,這樣可獲得更好的性能:
vartouches=[ ]
canvas.addEventListener('touchmove',function(event){
? ? ? touches=event.touches;
},false);
//設(shè)置一個(gè)每秒60幀的定時(shí)器
timer=setInterval(function(){
? ? ?renderTouches(touches);
},15);
提示:setInterval不太適合于動(dòng)畫,因?yàn)樗鼪]有考慮到瀏覽器自己的渲染循環(huán)。現(xiàn)代的桌面瀏覽器提供了requestAnimationFrame這一函數(shù),基于性能和電池工作時(shí)間原因,這是一個(gè)更好的選擇。一但瀏覽器提供了對(duì)該函數(shù)的支持,那將是首選的處理事情的方式。
使用targetTouches和changedTouches
要記住的一點(diǎn)是,event.touches是與屏幕接觸的所有手指的一個(gè)數(shù)組,而不僅是位于目標(biāo)DOM元素上的那些。你可能會(huì)發(fā)現(xiàn)使用event.targetTouches和event.changedTouches來代替event.touches更有用一些。
最后一點(diǎn),因?yàn)槟闶窃跒橐苿?dòng)設(shè)備做開發(fā),因此你應(yīng)該要留心移動(dòng)的最佳做法,這些在EricBidelman的文章中有論及,以及要了解這一W3C文檔。
設(shè)備支持:
遺憾的是,觸摸事件的實(shí)現(xiàn)在完備性和質(zhì)量方面的差別很大。我編寫了一個(gè)診斷腳本來顯示一些關(guān)于觸摸API實(shí)現(xiàn)的基本信息,其中包括哪些事件是支持的,以及touchmove事件觸發(fā)的解決方案。我在NexusOne和NexusS硬件上測試了Android2.3.3,在Xoom上測試了Android3.0.1,以及在iPad和iPhone上測試了iOS4.2。
簡而言之,所有被測試的瀏覽器都支持touchstart、touchend和touchmove事件。
規(guī)范提供了額外的三個(gè)觸摸事件,但被測試的瀏覽器沒有支持它們:
1.touchenter:移動(dòng)的手指進(jìn)入一個(gè)DOM元素。
2.toucheleave:移動(dòng)手指離開一個(gè)DOM元素。
3.touchcancel:觸摸被中斷(實(shí)現(xiàn)規(guī)范)。
被測試的瀏覽器還在每個(gè)觸摸列表內(nèi)部都提供了touches、targetTouches和changedTouches列表。不過,被測試的瀏覽器沒有支持radiusX、radiusY或是rotationAngle屬性,這些屬性指明觸摸屏幕的手指的形狀。在一次touchmove期間,事件大約一秒鐘觸發(fā)60次,所有的被測試設(shè)備都是這樣。
Android2.3.3(Nexus)
Android的Gingerbread瀏覽器(在NexusOne和NexusS上測試)不支持多點(diǎn)觸摸,這是一個(gè)已已知的問題。
Android3.0.1(Xoom)
Xoom的瀏覽器對(duì)多點(diǎn)觸摸有一個(gè)基本的支持,不過只能是在單個(gè)的DOM元素上起作用。瀏覽器不能正確響應(yīng)同時(shí)發(fā)生在不同DOM元素上的兩處觸摸,換句話說,下面的代碼會(huì)對(duì)兩個(gè)同時(shí)發(fā)生的觸摸的給出反應(yīng):復(fù)制代碼
obj1.addEventListener('touchmove',function(event){
for(var i=0;i
? ? ?var touch = event.targetTouches;
? ? console.log('touched'+touch.identifier);
? ? }
},false);
但下面的代碼則不會(huì):復(fù)制代碼
var objs=[obj1,obj2];
for(vari=0;i
? ? var ?obj=objs;
? ? obj.addEventListener('touchmove',function(event){
? ? if(event.targetTouches.length==1){
? ? console.log('touched'+
? ? event.targetTouches[0].identifier);
? ? }
? ?},false);
}
iOS4.x(iPad,iPhone)
iOS設(shè)備完全支持多點(diǎn)觸摸,能夠跟蹤多個(gè)手指,并在瀏覽器中提供一個(gè)非常敏感的觸摸體驗(yàn)。
開發(fā)者工具:
在移動(dòng)開發(fā)中,一種較為容易的做法是,先在桌面上開始原型設(shè)計(jì),然后再在打算要支持的設(shè)備上處理移動(dòng)特有的部分。多點(diǎn)觸摸正是難以在PC上進(jìn)行測試的那些功能之一,因?yàn)榇蟛糠值腜C都沒有觸摸輸入。
不得不在移動(dòng)設(shè)備上進(jìn)行的測試有可能會(huì)拉長你的開發(fā)周期,因?yàn)槟闼龅拿宽?xiàng)改變都需要提交代碼到服務(wù)器上,接著再加載到設(shè)備上。然后,一旦運(yùn)行后,對(duì)應(yīng)用也就沒有太多的調(diào)試了,因?yàn)槠桨咫娔X和智能手機(jī)都很缺乏web開發(fā)者所用的工具。
這個(gè)問題的一個(gè)解決方案是在開發(fā)機(jī)器上模擬觸發(fā)事件。對(duì)于單點(diǎn)觸摸,觸摸事件可以基于鼠標(biāo)事件來模擬。如果你有觸摸輸入設(shè)備的話,比如說現(xiàn)代的AppMacBook,那么多點(diǎn)觸摸也可以被模擬。
單點(diǎn)觸摸事件:
如果你想在桌面上模擬單點(diǎn)觸摸事件的話,試一下PhantomLimb,該程序在網(wǎng)頁上模擬觸摸事件并提供一只巨手來引導(dǎo)。
另外還有Touchable這一jQuery插件,該插件跨平臺(tái)地統(tǒng)一了觸摸和鼠標(biāo)事件。
多點(diǎn)觸摸事件:
為了能夠讓你的多點(diǎn)觸摸web應(yīng)用在你的瀏覽器或是多點(diǎn)觸摸控板(比如說AppleMacBook或是MagicPad)上起作用,我創(chuàng)建了這一個(gè)MagicTouch.js填充工具,其捕捉來自觸控板的觸摸事件,然后把它們轉(zhuǎn)換成標(biāo)準(zhǔn)兼容的觸摸事件。
1.下載npTuioClientNPAPI插件并把它安裝到~/Library/InternetPlug-Ins/目錄下。
2.下載這一MacMagicPad的TongSengTUIO應(yīng)用并啟動(dòng)這一服務(wù)器。
3.下載MagicTouch.js這一javascript庫來基于npTuioClient回調(diào)模擬規(guī)范兼容的觸摸事件
4.以如下方式把magictouch.js腳本和npTuioClient插件包含到你的應(yīng)用中:
...
kesrc="/path/to/magictouch.js">
...
Touchinputpluginfailedtoload!
我只在Chrome10上測試了這一方法,不過只要稍做調(diào)整它應(yīng)該能夠在其他的現(xiàn)代瀏覽器上工作。
如果你的計(jì)算機(jī)沒有多點(diǎn)觸摸輸入的話,你可以使用其他的TUIO跟蹤器,比如
說reacTIVision來模擬觸摸事件。欲了解更多信息,請(qǐng)參閱TUIO項(xiàng)目頁面。
需要注意的一點(diǎn)是,你的手勢可以是和OS層面的多點(diǎn)觸摸手勢相同的。在OSX上,你可以通過進(jìn)入SystemPreferences中的Trackpad偏好設(shè)定版面來配置系統(tǒng)范圍的事件。