再会を果たした
はじめに
30 年近くにわたり、デスクトップ コンピューティング エクスペリエンスは、キーボードとマウスまたはトラックパッドを中心に展開されてきました。しかし、この 10 年間でスマートフォンとタブレットは、タッチという新しいインタラクション パラダイムをもたらしました。タッチ対応の Windows 8 マシンの登場、そしてタッチ対応の Chromebook Pixel のリリースにより、タッチはデスクトップ エクスペリエンスの一部になりつつあります。最大の課題の一つは、タッチデバイスとマウスデバイスだけでなく、ユーザーが両方の入力方法を使用するデバイス(場合によっては同時に)でも機能するエクスペリエンスを構築することです。
この記事では、タップ機能がブラウザに組み込まれている仕組み、この新しいインターフェース メカニズムを既存のアプリに統合する方法、タップとマウス入力を連携させる方法について説明します。
ウェブ プラットフォームのタッチの現状
iPhone は、ウェブブラウザに専用のタップ API が組み込まれた最初の一般的なプラットフォームでした。他の複数のブラウザ ベンダーも、iOS の実装と互換性のある同様の API インターフェースを作成しています。これは、「タッチイベント バージョン 1」の仕様で説明されています。タッチイベントは、パソコンの Chrome と Firefox、iOS の Safari、Android の Chrome と Android ブラウザ、Blackberry ブラウザなどのモバイル ブラウザでサポートされています。
私の同僚である Boris Smus が、タッチイベントに関する HTML5Rocks チュートリアルを作成しました。タッチイベントについてまだ詳しくない方は、このチュートリアルから始めることをおすすめします。タップイベントを扱ったことがない場合は、先に進む前に、その記事を読んでください。よろしければ、お待ちしております。
準備ができていることを確認したら、タップイベントの基本を理解したところで、タップ対応の操作を記述する際の課題について説明します。タップ操作はマウス(およびマウスをエミュレートするトラックパッドとトラックボール)のイベントとは大きく異なる場合があります。タッチ インターフェースは通常、マウスをエミュレートしようとしますが、そのエミュレーションは完全ではありません。両方の操作スタイルを実際に扱う必要があり、各インターフェースを個別にサポートしなければならない場合もあります。
最も重要な点: ユーザーがタップとマウスを使用している可能性がある
多くのデベロッパーは、環境がタップ イベントをサポートしているかどうかを静的に検出し、タップ イベント(マウス イベントではない)のみをサポートする必要があるという前提でサイトを構築しています。これは誤った前提です。タッチイベントが存在するからといって、ユーザーがそのタッチ入力デバイスを主に使用しているわけではありません。Chromebook Pixel や一部の Windows 8 ノートパソコンなどのデバイスでは、マウスとタップ入力の両方がサポートされています。今後、さらに多くのデバイスでサポートされる予定です。このようなデバイスでは、ユーザーがマウスとタッチスクリーンの両方を使用してアプリを操作するのはごく自然なことです。そのため、「タッチをサポート」は「マウスのサポートは不要」とは異なります。「2 つの異なるインタラクション スタイルを記述して切り替える必要がある」という問題ではなく、両方のインタラクションが連携して機能する方法と、独立して機能する方法について考える必要があります。Chromebook Pixel では、トラックパッドを頻繁に使用しますが、画面をタップすることもあります。同じアプリやページで、そのとき最も自然に感じる方法で操作しています。一方、タッチスクリーン ノートパソコンのユーザーの中には、タッチスクリーンを使用しない、またはほとんど使用しない人もいます。そのため、タッチ入力の存在がマウス操作を無効にしたり、妨げたりしないことが重要です。
残念ながら、ユーザーのブラウザ環境がタップ入力をサポートしているかどうかを判断するのは困難です。理想的には、デスクトップ マシン上のブラウザがタップイベントを常にサポートしているため、タッチスクリーン ディスプレイをいつでも接続できるようにします(KVM を介して接続されたタッチスクリーンが利用可能になった場合など)。こうした理由から、アプリでタッチとマウスを切り替えようとせず、両方をサポートしてください。
マウスとタッチの同時サポート
1 - クリックとタップ - 自然な順序
最初の問題は、タッチ インターフェースは通常、マウスクリックをエミュレートしようとすることです。これは、タッチ インターフェースは、これまでマウスイベントのみを操作していたアプリで動作する必要があるためです。これはショートカットとして使用できます。ユーザーがマウスでクリックしたか、画面上で指でタップしたかにかかわらず、「クリック」イベントは引き続き発生します。ただし、このショートカットにはいくつかの問題があります。
まず、高度なタップ操作を設計する際には注意が必要です。ユーザーがマウスを使用すると、クリック イベントを介して応答しますが、ユーザーが画面にタップすると、タップ イベントとクリック イベントの両方が発生します。1 回のクリックの場合、イベントの順序は次のとおりです。
- touchstart
- touchmove
- touchend
- マウスオーバー
- mousemove
- mousedown
- mouseup
- クリック
つまり、touchstart などのタッチイベントを処理する場合は、対応する mousedown イベントや click イベントも処理しないようにする必要があります。タッチイベントをキャンセルできる場合(イベント ハンドラ内で preventDefault() を呼び出す場合)、タッチに対してマウスイベントは生成されません。タッチ ハンドラの最も重要なルールの 1 つは次のとおりです。
ただし、これにより、他のデフォルトのブラウザ動作(スクロールなど)も妨げられます。通常、タッチイベントはハンドラで完全に処理し、デフォルトのアクションを無効にします。通常は、すべてのタップイベントを処理してキャンセルするか、そのイベントのハンドラを作成しないかのいずれかになります。
2 つ目の理由は、モバイル デバイスのウェブページでユーザーが要素をタップしたときに、モバイル操作用に設計されていないページでは、touchstart イベントとマウスイベント(mousedown)の処理の間に 300 ミリ秒以上の遅延が生じることです。Chrome を使用して行うことができます。Chrome デベロッパー ツールで [タップイベントをエミュレート] をオンにすると、タップ以外のシステムでタップ インターフェースをテストできます。
この遅延は、ユーザーが別の操作(特にダブルタップによるズーム)を行っているかどうかをブラウザが判断するための時間です。指の操作に即座に応答する必要がある場合は、これは問題になる可能性があります。この遅延が自動的に発生するシナリオを制限するための作業が進行中です。
この遅延を回避する最も簡単な方法は、ページでズームが必要ないことをモバイル ブラウザに「伝える」ことです。これは、固定ビューポートを使用して行うことができます。たとえば、ページに次のように挿入します。
<meta name="viewport" content="width=device-width,user-scalable=no">
もちろん、この方法が適切であるとは限りません。ピンチ操作によるズームはユーザー補助の観点から必要となる場合があるため、この方法は必要に応じて慎重に使用してください(ユーザーによるスケーリングを無効にする場合は、アプリ内のテキストの読みやすさを高めるための他の方法を用意することをおすすめします)。また、タッチをサポートするデスクトップ クラスのデバイス上の Chrome や、ページにスケーリングできないビューポートがあるモバイル プラットフォーム上の他のブラウザでは、この遅延は適用されません。
#2: タッチでマウス移動イベントがトリガーされない
ここで注意すべき点は、タッチ インターフェースでのマウスイベントのエミュレーションは、通常、mousemove イベントのエミュレーションには拡張されないことです。そのため、mousemove イベントを使用する美しいマウス駆動型のコントロールを構築した場合、touchmove ハンドラも明示的に追加しない限り、タッチデバイスでは動作しない可能性があります。
通常、ブラウザは HTML コントロールにタップ操作に適した操作を自動的に実装します。たとえば、HTML5 の範囲コントロールは、タップ操作を使用すると機能します。ただし、独自のコントロールを実装している場合、クリック&ドラッグ タイプの操作では機能しない可能性があります。実際、よく使用されるライブラリ(jQueryUI など)では、このようなタッチ操作がネイティブにサポートされていません(ただし、jQueryUI では、この問題に対するモンキーパッチによる修正がいくつかあります)。これは、Web Audio Playground アプリケーションをタップ操作に対応するようにアップグレードしたときに最初に直面した問題の一つでした。スライダーは jQueryUI ベースだったため、クリック&ドラッグ操作に対応していませんでした。HTML5 の範囲コントロールに切り替えたところ、問題なく動作しました。もちろん、単に touchmove ハンドラを追加してスライダーを更新することもできますが、これには 1 つの問題があります。
#3: Touchmove と MouseMove は同じではない
一部のデベロッパーが陥りがちな落とし穴は、touchmove ハンドラと mousemove ハンドラが同じコードパスを呼び出すことです。これらのイベントの動作は非常に似ていますが、微妙に異なります。特に、タッチイベントは常にタッチが開始された要素をターゲットにしますが、マウスイベントは現在マウスカーソルの下にある要素をターゲットにします。そのため、mouseover イベントと mouseout イベントはありますが、対応する touchover イベントと touchout イベントはなく、touchend イベントのみがあります。
ユーザーがタップし始めた要素を削除(または移動)した場合に、この問題が発生する可能性が最も高くなります。たとえば、カルーセル全体にタップ ハンドラがあり、カスタムのスクロール動作をサポートする画像カルーセルについて考えてみましょう。使用可能な画像が変更されたら、一部の <img>
要素を削除し、他の要素を追加します。ユーザーが画像のいずれかをタップし始め、その画像を削除した場合、(img 要素の祖先にある)ハンドラはタップイベントの受信を停止します(イベントは、ツリーに存在しないターゲットにディスパッチされるためです)。ユーザーが指を動かして画像を削除したとしても、指を 1 か所に置いたままにしているように見えます。
もちろん、タップがアクティブなときにタップ ハンドラを持つ要素(またはタップ ハンドラを持つ祖先)を削除しないようにすれば、この問題を回避できます。静的な touchend ハンドラと touchmove ハンドラを登録するのではなく、touchstart イベントが受信されるまで待機し、touchstart イベントのターゲットに touchmove ハンドラ、touchend ハンドラ、touchcancel ハンドラを追加することをおすすめします(終了時またはキャンセル時に削除します)。これにより、ターゲット要素が移動または削除されても、タッチのイベントが引き続き受信されます。こちらで試すことができます。赤いボックスをタップし、その状態で Esc キーを押すと、DOM から削除されます。
#4: タップとホバー
マウス ポインタのメタファーは、カーソルの位置とアクティブな選択を分離しました。これにより、デベロッパーはホバー状態を使用して、ユーザーに関連する情報を非表示または表示できるようになりました。ただし、現在のところ、ほとんどのタッチ インターフェースでは、ターゲットの上に指が「ホバー」しているかどうかは検知されません。そのため、ホバーに基づいて意味的に重要な情報(「このコントロールは何ですか?」というポップアップの提供など)を提供することは、この情報にタッチ操作でアクセスする方法も提供しない限り、おすすめしません。ホバーを使用してユーザーに情報を伝える際は、その方法に注意する必要があります。
興味深いことに、CSS の :hover 擬似クラスは、タッチ インターフェースによってトリガーされる場合があります。要素をタップすると、指が離れるまで :active になり、:hover 状態も取得されます。(Internet Explorer では、:hover はユーザーの指が下がっている間のみ有効です。他のブラウザでは、:hover は次のタップまたはマウスの移動まで有効です)。これは、タッチ インターフェースでポップアウト メニューを機能させるのに適したアプローチです。要素をアクティブにすると、:hover 状態も適用されるという副作用があります。次に例を示します。
<style>
img ~ .content {
display:none;
}
img:hover ~ .content {
display:block;
}
</style>
<img src="/awesome.png">
<div class="content">This is an awesome picture of me</div>
別の要素をタップすると、その要素はアクティブではなくなり、ホバー状態は消えます。これは、ユーザーがマウス ポインタを使用して要素から移動した場合と同じです。コンテンツを <a>
要素でラップしてタブストップにすることもできます。これにより、ユーザーはマウスのホバーやクリック、タップ、キープレスで追加情報を切り替えることができ、JavaScript は必要ありません。Web Audio Playground をタッチ インターフェースで適切に動作させる作業を開始したとき、ポップアウト メニューがタッチですでに適切に動作していたことに驚きました。この種の構造を使用していたためです。
上記の方法は、マウス ポインタ ベースのインターフェースとタッチ インターフェースの両方に適しています。これは、ホバー時に「title」属性を使用する場合とは対照的です。この場合、要素がアクティブになっても表示されません。
<img src="/awesome.png" title="this doesn't show up in touch">
#5: タップとマウスの精度
マウスは概念的には現実とは関連性がありませんが、基盤となるオペレーティング システムは通常、カーソルのピクセル単位の正確な位置をトラッキングするため、非常に正確です。一方、モバイル デベロッパーは、タッチスクリーンでの指タップは精度が低いことを知っています。これは主に、画面に接触する指の表面積が原因です(指が画面を遮ることも原因の一つです)。
多くの個人や企業が、指による操作に対応したアプリやサイトを設計する方法について、広範なユーザー調査を実施しており、このトピックに関する書籍も多数出版されています。基本的なアドバイスとしては、パディングを増やすことでタッチ ターゲットのサイズを大きくし、要素間のマージンを増やすことで誤タップが発生する可能性を減らすことです。(ただし、タッチイベントとクリック イベントのヒット検出処理では、マージンは考慮されませんが、パディングは考慮されます)。Web Audio Playground で行った主な修正の 1 つは、接続ポイントのサイズを大きくして、正確にタップしやすくしたところです。
タップベースのインターフェースを扱う多くのブラウザ ベンダーは、ユーザーが画面をタップしたときに正しい要素をターゲットにし、誤ったクリックの発生を減らすためのロジックをブラウザに導入しています。ただし、通常はクリック イベントのみが修正され、移動イベントは修正されません(ただし、Internet Explorer では mousedown/mousemove/mouseup イベントも変更されているようです)。
6: タッチ ハンドラを制御しないと、スクロールがぎくしゃくする
また、タップ ハンドラは必要な要素にのみ限定することも重要です。タップ要素は帯域幅が非常に高くなる可能性があるため、スクロール要素にタップ ハンドラを配置しないことが重要です(処理が、ジャンクのない高速なタップ スクロールを実現するためのブラウザの最適化を妨げる可能性があるためです。最新のブラウザは GPU スレッドでスクロールしようとしますが、各タップ イベントがアプリによって処理されるかどうかを最初に JavaScript で確認する必要がある場合、これは不可能です)。この動作の例を確認できます。
この問題を回避するためのガイダンスとして、UI のごく一部でタッチイベントのみを処理する場合は、タッチハンドラをその部分にのみ接続します(ページの <body>
には接続しません)。つまり、タッチハンドラのスコープを可能な限り制限します。
#7: マルチタッチ
最後の興味深い課題は、このインターフェースを「タッチ」ユーザー インターフェースと呼んでいますが、実際にはほぼすべてマルチタッチに対応しているということです。つまり、API は一度に複数のタッチ入力を提供します。アプリでタップをサポートする際は、複数のタップがアプリに与える影響を考慮する必要があります。
主にマウスで操作するアプリを作成してきた場合、最大 1 つのカーソル ポイントで作成することに慣れています。通常、システムは複数のマウスカーソルをサポートしていません。多くのアプリケーションでは、タッチイベントを単一のカーソル インターフェースにマッピングするだけですが、デスクトップのタッチ入力で使用されているほとんどのハードウェアは、少なくとも 2 つの入力を同時に処理できます。また、新しいハードウェアのほとんどは、少なくとも 5 つの入力を同時にサポートしているようです。画面上のピアノ キーボードを開発する場合は、複数の同時タップ入力をサポートする必要があります。
現在実装されている W3C Touch API には、ハードウェアがサポートするタッチポイントの数を判断する API がないため、ユーザーが望むタッチポイントの数をできるだけ正確に推定する必要があります。または、実際に表示されるタッチポイントの数に注意を払って適応することもできます。たとえば、ピアノ アプリで、タップポイントが 2 つを超えない場合は、「コード」UI を追加することをおすすめします。PointerEvents API には、デバイスの機能を判断する API があります。
タッチアップ
この記事が、マウス操作とタップ操作を併用する際に発生する一般的な問題の解決に役立てば幸いです。他のアドバイスよりも重要なのは、モバイル、タブレット、マウスとタッチの組み合わせのパソコン環境でアプリをテストすることです。タップとマウスのハードウェアがない場合は、Chrome の [タップイベントをエミュレート] を使用して、さまざまなシナリオをテストします。
タップ入力、マウス入力、さらには両方の入力スタイルを同時に使用できる、魅力的なインタラクティブ エクスペリエンスを構築することは、これらのガイダンスに沿って行えば、可能であるだけでなく、比較的簡単です。