はじめに
スマートフォンやタブレットなどのモバイル端末には一般に、高性能なタッチスクリーンが搭載されており、ユーザーの指による操作を取得できます。モバイル ウェブが発展し、アプリケーションがますます高度になる中で、ウェブ開発者にはタッチ イベントを扱う方法が必要になっています。たとえば、スピードの速いゲームではプレーヤーが複数のボタンを 1 度に押す必要がありますが、タッチスクリーンにとっては「マルチタッチ」というイベントになります。
アップル社は iOS 2.0 でタッチ イベント API を導入しました。Android もこの事実上の業界標準に追い付いて差を縮めています。W3C は最近、タッチ イベント仕様の策定に向けてワーキング グループを立ち上げました(以下、リンク先はすべて英語)。
この記事では、iOS や Android 端末で提供されるタッチ イベント API をテーマに、作成できるアプリケーションの内容やベスト プラクティスを探ります。タッチ対応アプリケーションの開発が簡単になる便利なテクニックもご紹介します。
タッチ イベント
次の 3 つの基本的なタッチ イベントが仕様に示されており、モバイル端末で広く実装されています。
- touchstart: DOM 要素に指が置かれる。
- touchmove: DOM 要素に沿って指がドラッグされる。
- touchend: DOM 要素から指が離れる。
各タッチ イベントには 3 つのタッチのリストがあります。
- touches: 現在画面上にあるすべての指のリスト。
- targetTouches: 現在の DOM 要素上にある指のリスト。
- changedTouches: 現在のイベントに関与している指のリスト。たとえば、touchend イベントでは、離れた指のリストになります。
- 識別子: タッチ セッションで現在の指を一意に識別する番号。
- 対象: 操作の対象となった DOM 要素。
- クライアント/ページ/画面の座標: 操作が発生した画面上の場所。
- 半径座標と rotationAngle: 指の形に近い楕円を表します。
タッチ対応アプリケーション
touchstart、touchmove、touchend イベントは豊富な機能セットを提供しており、ピンチズームや回転などの通常のマルチタッチ ジェスチャーを含めて、実質的にあらゆる種類のタッチ操作に対応しています。
次のサンプル コードでは、1 本指のタッチを使って DOM 要素をドラッグできます。
var obj = document.getElementById('id'); obj.addEventListener('touchmove', function(event) { // If there's exactly one finger inside this element if (event.targetTouches.length == 1) { var touch = event.targetTouches[0]; // Place element where the finger is obj.style.left = touch.pageX + 'px'; obj.style.top = touch.pageY + 'px'; } }, false);
次のサンプルでは、現在のすべてのタッチが画面に表示されます。端末が反応する感覚を試してみるのに便利です。

// Setup canvas and expose context via ctx variable canvas.addEventListener('touchmove', function(event) { for (var i = 0; i < event.touches.length; i++) { var touch = event.touches[i]; ctx.beginPath(); ctx.arc(touch.pageX, touch.pageY, 20, 0, 2*Math.PI, true); ctx.fill(); ctx.stroke(); } }, false);
デモ
マルチタッチのデモについては、既に面白い作品が多数公開されています。次のキャンバス ベースの描画デモ(制作者 Paul Irish 氏、他)もその 1 つです。

Browser Ninja は「Fruit Ninja」のクローン ゲームで、CSS3 の変形(transform)と遷移(transition)機能やキャンバスを使用した技術デモです。

ベスト プラクティス
ズームの防止
ユーザーのスワイプやジェスチャーはブラウザの動作(スクロールやズームなど)に関連付けられていることが多いため、デフォルトの設定ではマルチタッチにうまく対応できません。
ズームを無効にするには、次のような meta タグを使用して、ユーザーによる拡大縮小(user scalable)ができないようにビューポート(viewport)を設定します。
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">ビューポートの設定について詳しくは、モバイル用 HTML5 に関するこちらの記事をご覧ください。
スクロールの防止
一部のモバイル端末では、touchmove に対してデフォルトの動作が設定されています。たとえば、従来の iOS のオーバースクロール効果(コンテンツの境界を越えてスクロールすると表示が戻る機能)などです。これはマルチタッチ アプリケーションでは混乱することが多く、次のように簡単に無効にすることができます。
document.body.addEventListener('touchmove', function(event) { event.preventDefault(); }, false);
慎重なレンダリング
複数の指での複雑なジェスチャーを含むマルチタッチ アプリケーションを記述する場合は、1 度に多くのイベントを処理することになるため、タッチ イベントへの対応を慎重に検討してください。すべてのタッチを画面に描画する、前のセクションのサンプルを考えてみましょう。次の場合は、タッチ入力があるとすぐに描画できます。
canvas.addEventListener('touchmove', function(event) { renderTouches(event.touches); }, false);
しかし、この方法は画面上に複数の指がある場合に対応していません。代わりに、すべての指を追跡してループでレンダリングすると、パフォーマンスが向上します。
var touches = [] canvas.addEventListener('touchmove', function(event) { touches = event.touches; }, false); // Setup a 60fps timer timer = setInterval(function() { renderTouches(touches); }, 15);
ヒント: setInterval は、ブラウザ独自のレンダリング ループを考慮に入れていないため、アニメーションには適していません。最新のデスクトップ ブラウザでは requestAnimationFrame を提供しています。パフォーマンスや電池の寿命の点で、はるかに優れたオプションです。モバイル ブラウザでサポートされた場合は、こちらの方法をおすすめします。
targetTouches と changedTouches の活用
event.touches は、対象の DOM 要素にある指だけでなく、画面と接触したすべての指の配列です。代わりに event.targetTouches や event.changedTouches を使用する方が便利な場合もあります。
最後に、モバイル向けの開発を行う場合は、モバイルの一般的なベスト プラクティスも考慮してください。Eric Bidelman 氏の記事や、こちらの W3C のドキュメントで紹介されています。
端末のサポート
残念ながら、タッチ イベントは実装によって完全性や品質が大きく異なります。そこで、診断用スクリプトを作成しました。サポートされるイベントや、touchmove の開始についての解決策など、タッチ API の実装に関する基本情報を示しています。Nexus One および Nexus S ハードウェア上の Android 2.3.3、Xoom 上の Android 3.0.1、iPad および iPhone 上の iOS 4.2 でテストを実施しました。
簡単に述べると、テストしたすべてのブラウザが touchstart、touchend、touchmove イベントに対応しています。
仕様では 3 つの追加のタッチ イベントも提供していますが、テストしたブラウザではサポートしていません。
- touchenter: 動いている指が DOM 要素に入る。
- touchleave: 動いている指が DOM 要素から離れる。
- touchcancel: タッチが中断される(実装に固有)。
各タッチ リスト内で、テストしたブラウザでは touches、targetTouches、changedTouches タッチ リストも提供します。ただし、画面をタッチしている指の形状を指定する radiusX、radiusY、rotationAngle には対応していません。
テストしたすべてのブラウザで、touchmove の際には 1 秒間に約 60 回のイベントが開始されます。
Android 2.3.3(Nexus)
Android Gingerbread のブラウザ(Nexus One と Nexus S でテスト)では、マルチタッチのサポートはありません。これは確認済みの問題です。
Android 3.0.1(Xoom)
Xoom のブラウザでは、基本的なマルチタッチのサポートがありますが、1 つの DOM 要素に対してのみ機能します。別々の DOM 要素に対する 2 つの同時タッチには正しく反応しません。つまり、次のような場合は、2 つの同時タッチに反応しますが、
obj1.addEventListener('touchmove', function(event) { for (var i = 0; i < event.targetTouches; i++) { var touch = event.targetTouches[i]; console.log('touched ' + touch.identifier); } }, false);
次の場合には反応しません。
var objs = [obj1, obj2]; for (var i = 0; i < objs.length; i++) { var obj = objs[i]; obj.addEventListener('touchmove', function(event) { if (event.targetTouches.length == 1) { console.log('touched ' + event.targetTouches[0].identifier); } }, false); }
iOS 4.x(iPad、iPhone)
iOS はマルチタッチを完全にサポートしており、かなりの数の指を追跡できます。ブラウザでのタッチに非常にすばやく反応します。
開発者向けツール
モバイル開発では、パソコンで試作を開始してから、対応予定の端末でモバイル固有の部分に取り組むとやりやすい場合があります。ほとんどのパソコンはタッチ入力に対応していないため、マルチタッチはパソコン上でテストするのが難しい機能の 1 つです。
モバイル端末でのテストが必要な場合、開発サイクルが長くなる可能性があります。変更を加えるたびにサーバーに送信し、端末に読み込む必要があるためです。さらに、タブレットやスマートフォンにはウェブ開発者向けのツールが不足しているので、稼動後はアプリケーションのデバッグをほとんど行えません。
この問題の解決策は、開発用パソコンでタッチ イベントをシミュレートすることです。シングルタッチの場合、マウス イベントに基づいてタッチ イベントをシミュレートできます。最新の Apple MacBook など、タッチ入力に対応した端末がある場合は、マルチタッチ イベントをシミュレートできます。
シングルタッチ イベント
パソコンでシングルタッチ イベントをシミュレートする場合は、Phantom Limb をお試しください。ページ上でタッチ イベントをシミュレートするツールで、大きな手の画像も表示されます。
プラットフォーム間でタッチ イベントとマウス イベントを統一する Touchable jQuery プラグインもあります。
マルチタッチ イベント
マルチタッチ トラックパッド(Apple MacBook や MagicPad など)を使ってマルチタッチ ウェブ アプリケーションをブラウザで操作できるようにするため、MagicTouch.js ポリフィルを作成しました。トラックパッドからのタッチ イベントを取得して、標準に対応したタッチ イベントに変換します。
- npTuioClient NPAPI プラグインをダウンロードして、インストールします。
- Mac の MagicPad 用の TongSeng TUIO アプリケーションをダウンロードして、サーバーを起動します。
- MagicTouch.js をダウンロードします。npTuioClient コールバックに基づいて、仕様に対応したタッチ イベントをシミュレートする javascript ライブラリです。
- 次のように、アプリケーションに magictouch.js スクリプトと npTuioClient プラグインを組み込みます。
<head> ... <script src="/path/to/magictouch.js"></script> </head> <body> ... <object id="tuio" type="application/x-tuio" style="width: 0px; height: 0px;"> Touch input plugin failed to load! </object> </body>
この方法は Chrome 10 でしかテストしていませんが、わずかな調整だけで他の最新ブラウザでも動作するはずです。
パソコンがマルチタッチ入力に対応していない場合は、他の TUIO トラッカー(reacTIVision など)を使用して、タッチ イベントをシミュレートすることができます。詳しくは、TUIO プロジェクトのページをご覧ください。
ユーザーのジェスチャーを OS レベルのマルチタッチ ジェスチャーと同じにしてもかまいません。OS X では、[システム環境設定] の [トラックパッド] 設定ペインに移動すると、システム全体のイベントを設定できます。
マルチタッチ機能はモバイル ブラウザで広くサポートされるようになりました。この多機能な API を最大限活用した新しいウェブ アプリケーションに出会うのを楽しみにしています。