Dotýkejte se, prosím, na více místech

V předchozích dvou článcích jsme si ukazovali, jak lze obsloužit dotyková gesta a jak zpracovat jednotlivé body dotyku. Naznačili jsme si strukturu informací, které má aplikace k dispozici. V posledním pokračování si ukážeme, jak je využít pro sledování dotyků více prsty.
Seriál: Dotykové ovládání a JavaScript (3 díly)
- Dotýkejte se, prosím… 18. 1. 2012
- Dotýkejte se, prosím, jemněji… 25. 1. 2012
- Dotýkejte se, prosím, na více místech 15. 2. 2012
Nálepky:
Ukážeme si na závěr jednoduchý příklad, jak sledovat dotyky více prsty najednou (příklad se hodí i jako jednoduchý test, zda zařízení, například tablet, podporuje multitouch).
Příklad vychází z kódu na adrese http://sebleedelisle.com/2011/04/multi-touch-game-controller-in-javascripthtml5-for-ipad/ od Seba Lee-Delisle)
Zdrojový kód:
<style> … canvas { background-color:#111133; display:block; position:absolute; } #container { width:auto; text-align:center; background-color:#ff0000; } </style> <script> "use strict"; var c, canvas, touches; function touchstart(e){ touches = e.touches; } function touchmove(e){ e.preventDefault(); touches = e.touches; } function touchend(e){ touches = e.touches; } function draw() { if (touches && touches.length>0) { c.clearRect(0,0,canvas.width, canvas.height); for(var i=0; i<touches.length; i++) { var touch = touches[i]; c.fillStyle = "white"; c.fillText("touch id : "+touch.identifier+" x:"+touch.clientX+" y:"+touch.clientY, touch.clientX+30, touch.clientY-30); c.beginPath(); c.strokeStyle = "magenta"; c.lineWidth = "6"; c.arc(touch.clientX, touch.clientY, 40, 0, Math.PI*2, true); c.stroke(); } } else { c.clearRect(0,0,canvas.width, canvas.height); } } function resetCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; window.scrollTo(0,0); } function onload(){ canvas = document.getElementById( 'c' ); c = canvas.getContext( '2d' ); canvas.width = window.innerWidth; canvas.height = window.innerHeight; canvas.ontouchstart = touchstart; canvas.ontouchmove = touchmove; canvas.ontouchend = touchend; window.onorientationchange = resetCanvas; window.onresize = resetCanvas; setInterval(draw, 1000/35); } </script> </head> <body onload="onload()"> <div id="container"> <canvas id="c"></canvas> </div> </body></html>
Obsluha událostí spočívá pouze v tom, že jsou aktuální souřadnice dotyků (e.touches) uloženy do globální proměnné. U pohybu je ještě zablokováno vykonávání systémové události. Pamatujte: obsluha událostí by měla být co nejrychlejší, což je tu dodrženo.
function touchstart(e){ touches = e.touches; } function touchmove(e){ e.preventDefault(); touches = e.touches; } function touchend(e){ touches = e.touches; }
Na začátku (onload) je získán 2D kontext pro canvas. Canvas je přes celou plochu, proto navěsíme obsluhu touch událostí právě na něj. Důležité je periodické vyvolání funkce draw, která se postará o vizualizaci míst dotyku.
Funkce draw()
vezme souřadnice dotyků (z globální proměnné) a vykreslí do patřičných míst kroužky a k nim připojí text s informacemi o souřadnicích a o ID dotykové události.
Vykreslení je implementováno „naivně“ – funkce se vyvolává pravidelně v daném intervalu, vždy smaže celý canvas a vykreslí vše znovu. Samotná animace je poměrně jednoduchá, takže „problikávání“ není vidět – pokud byste ale implementovali nějaké složitější vykreslování, bylo by samozřejmě na místě použít vyspělejší animační techniky (double / triple buffering atd.)
Poznámka k animaci
V době psaní této kapitoly bohužel ještě iOS nepodporoval animační novinku – JS funkci requestAnimationFrame()
. Tato funkce je velmi podobná funkci setInterval() s tím rozdílem, že interval je daný „systémovou“ animační smyčkou, v níž systém vykonává např. CSS animace. Pokud řešíte animace v JavaScriptu, je výhodné použít právě tuto funkci. Prohlížeč ji pak vyvolává tak často jako ostatní animační rutiny, vykreslování je tedy synchronizované a s optimální rychlostí.
Vlastní kód lze upravit tak, aby byl připravený na okamžik, kdy iOS tuto funkci implementuje:
var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || (function(x){setInterval(x,1000/60);}); requestAnimationFrame(draw);
Tento shim definuje funkci requestAnimationFrame. Pokud je systémová, použije ji. Pokud prohlížeč nabízí funkci s vendor prefixem, použije tu. Pokud nemá ani s prefixem, použije náhradní řešení se setInterval.
Pro podobné krátké kódy, které řeší problémy s kompatibilitou mezi prohlížeči, se vžilo označení shim. Kódy, které pomáhají používat nové funkce i v prohlížečích, kde nejsou implementované, se označují jako polyfill.
Ve chvíli, kdy prohlížeč Safari v iOS implementuje requestAnimationFrame, použije kód tuto implementaci, do té doby si vystačí se setInterval.
Autor děkuje společnosti Czech Computer za laskavé zapůjčení tabletu iPad 2.
requestAnimationFrame nemozete predsa nahradzat setIntervalom, pretoze RAF spusta svoj prvy argument iba raz, nie cyklicky. Chcelo by to setTimeout.