タップとマウス

初めて一緒に集まれる

はじめに

デスクトップ コンピューティングは 30 年近くにわたり、主要なユーザー入力デバイスとしてキーボード、マウス、トラックパッドを中心に据えてきました。しかし、過去 10 年間でスマートフォンやタブレットは、新たなインタラクションのパラダイムである「タップ」という新しいインタラクションのパラダイムをもたらしました。タッチ対応 Windows 8 搭載のパソコンと優れたタッチ対応 Chromebook Pixel の登場により、デスクトップでのタッチ機能は期待に応えるものになりつつあります。最大の課題の一つは、タッチデバイスとマウスデバイスだけでなく、ユーザーが両方の入力方法(場合によっては同時に)を使用するデバイスでも動作するエクスペリエンスを構築することです。

この記事では、ブラウザにタッチ機能が組み込まれる仕組み、この新しいインターフェース メカニズムを既存のアプリに統合する方法、マウス入力でタップ操作を適切に行う方法について説明します。

ウェブ プラットフォームの現状

iPhone は、ウェブブラウザに組み込まれた専用のタッチ API を搭載した最初の人気プラットフォームです。他の複数のブラウザ ベンダーも、iOS の実装と互換性を持つように構築された同様の API インターフェースを作成しています。これについては、「Touch Events バージョン 1」仕様で説明されています。タッチイベントは、パソコンの Chrome と Firefox、iOS と Chrome の Safari、Android の Android ブラウザ、および Blackberry ブラウザなどの他のモバイル ブラウザでサポートされています。

同僚の Boris Smus が、タッチイベントに関する HTML5Rocks チュートリアルを執筆しています。タッチイベントをご覧になったことがない方も、このチュートリアルをぜひご参照ください。タッチイベントを使用したことがない場合は、先に進む前にこの記事をお読みください。待ってるよ。

完了しましたか?タッチイベントの基礎知識を得たので、タッチ対応操作を記述する際の課題は、タッチ操作がマウスイベント(およびマウスでトラックパッドやトラックボールをエミュレートする)イベントとはかなり異なる可能性があることです。タッチ インターフェースは通常マウスをエミュレートしようとしますが、エミュレーションは完璧ではありません。両方の操作スタイルに取り組む必要があり、各インターフェースを個別にサポートする必要があります。

最も重要なこと: タップ操作とマウス操作操作

多くのデベロッパーは、環境がタッチイベントをサポートしているかどうかを静的に検出するサイトを構築しており、(マウスではなく)タッチイベントのみをサポートする必要があると仮定しています。これは誤りです。タップイベントが存在するからといって、ユーザーが主にそのタップ入力デバイスを使用しているとは限りません。現在、Chromebook Pixel や一部の Windows 8 ノートパソコンはマウスとタップの両方の入力方法に対応しており、近い将来、さらに多くの入力方法に対応する予定です。このようなデバイスでは、ユーザーがマウスとタッチ スクリーンの両方を使用してアプリを操作することはごく自然なことです。そのため、「タッチ対応」と「マウスのサポートは不要」は同じではありません。この問題を「2 種類のインタラクション スタイルを作成して切り替える必要がある」とは考えられません。両方のインタラクションがどのように連携するか、独立して連携するかについて考える必要があります。Chromebook Pixel では、トラックパッドを頻繁に使用しますが、手を伸ばして画面をタップします。そのとき、同じアプリやページで、最も自然に感じられることは何でもします。一方、タッチスクリーン搭載のノートパソコン ユーザーは、タッチスクリーンをまったく使用することがほとんどないユーザーもいます。そのため、タップ入力があることでマウス操作が無効になったり妨げられたりすることはありません。

残念なことに、ユーザーのブラウザ環境がタップ入力をサポートしているかどうかを知るのは困難です。理想的には、デスクトップ マシンのブラウザは常にタッチイベントのサポートを示しているため、いつでもタッチスクリーン ディスプレイを接続できます(KVM を介して接続されたタッチスクリーンが使用可能になったときなど)。以上の理由から、アプリケーションではタップとマウスの切り替えを試行すべきではありません。タップとマウスの両方をサポートすることだけです。

マウスとタッチ操作のサポート

#1 - クリックとタップ - 物事の「自然」な順序

1 つ目の問題は、通常、タッチ インターフェースはマウスクリックをエミュレートしようとすることです。なぜなら、タッチ インターフェースは、これまでマウスイベントとしか対話したことのないアプリケーションで動作する必要があるためです。これをショートカットとして使用できます。これは、ユーザーがマウスでクリックしたり、画面上で指をタップしたりしても、「クリック」イベントが発行され続けるためです。ただし、このショートカットにはいくつか問題があります。

まず、より高度なタップ操作を設計する場合は注意が必要です。ユーザーがマウスを使用するとクリック イベントで応答しますが、画面にタッチすると、タップイベントとクリック イベントの両方が発生します。クリックが 1 回の場合、イベントの順序は次のようになります。

  1. タッチスタート
  2. タッチ移動
  3. タッチエンド
  4. マウスオーバー
  5. mousemove
  6. マウスダウン
  7. マウスアップ
  8. click

つまり、touchstart などのタッチイベントを処理する場合は、対応するマウスダウン イベントやクリック イベントも処理しないようにする必要があります。タッチイベントをキャンセルできる場合(イベント ハンドラ内で preventDefault() を呼び出す場合)、タッチに対するマウスイベントは生成されません。タッチハンドラの最も重要なルールの一つは次のとおりです。

ただし、これにより、ブラウザの他のデフォルトの動作(スクロールなど)も防止されます。ただし、通常はタッチイベントをすべてハンドラ内で処理しているため、デフォルトのアクションを無効にすることをおすすめします。一般的には、すべてのタッチイベントを処理してキャンセルするか、そのイベントのハンドラは作成しないようにします。

第 2 に、ユーザーがモバイル デバイスでウェブページの要素をタップすると、モバイル インタラクション用に設計されていないページでは、タッチスタート イベントからマウスイベントの処理(マウスダウン)までに 300 ミリ秒以上の遅延が発生します。これは Chrome を使用して行うことができます。Chrome デベロッパー ツールの [タッチイベントをエミュレート] をオンにすると、タップ以外のシステムでタッチ インターフェースをテストできます。

この遅延は、ユーザーが別の操作(特にダブルタップによるズーム)を行っているかどうかをブラウザが判断できるようにするためです。指のタップに即座に反応したい場合には、これが問題となることは明らかです。この遅延が自動的に発生するシナリオを抑えるための継続的な取り組みがあります。

Chrome for Android Android ブラウザ Android 版 Opera Mobile) Android 版 Firefox Safari(iOS)
拡大縮小できないビューポート 遅延なし 300ms 300ms 遅延なし 300ms
ビューポートなし 300ms 300ms 300ms 300ms 300ms

この遅延を回避する 1 つ目の最も簡単な方法は、ページがズームする必要がないことをモバイル ブラウザに「伝える」ことです。これを行うには、固定のビューポートを使用します。たとえば、ページに挿入します。

<meta name="viewport" content="width=device-width,user-scalable=no">

もちろん、これは常に適切とは限りません。ピンチズームが無効になります。ピンチズームはユーザー補助の理由から必要になる可能性があるため、使用する場合は慎重に使用してください(ユーザー スケーリングを無効にする場合は、アプリケーションでテキストを読みやすくする別の方法を提供してください)。また、タップをサポートするデスクトップ クラスの Chrome デバイスや、モバイル プラットフォーム上のその他のブラウザ(ページに拡張不可のビューポートがある)の場合、この遅延は該当しません

#2: タップでマウス移動イベントが実行されない

この時点では、通常、タッチ インターフェースのマウス イベントのエミュレーションは、マウス移動イベントのエミュレーションには拡張されないことに注意してください。そのため、マウス移動イベントを使用する美しいマウス駆動のコントロールを作成した場合、特にタッチムーブ ハンドラも追加しない限り、タッチデバイスではおそらく動作しないことに注意してください。

ブラウザは通常、タップ操作に対して適切な操作を自動的に HTML コントロールに実装します。そのため、たとえば HTML5 範囲コントロールは、タップ操作を使用した場合にのみ機能します。ただし、独自のコントロールを実装している場合、クリックとドラッグによる操作では動作しない可能性があります。実際、よく使用される一部のライブラリ(jQueryUI など)では、この方法でのタップ操作がネイティブにサポートされていません(ただし、jQueryUI については、この問題に対するモンキー パッチによる修正がいくつかあります)。これは、私が Web Audio Playground のアプリケーションをタッチ操作に対応させる際に直面した最初の問題のひとつでした。スライダーは jQueryUI ベースであり、クリックとドラッグの操作では動作しませんでした。HTML5 Range コントロールに切り替えると、問題なく機能するようになりました。もちろん、単に touchmove ハンドラを追加してスライダーを更新することもできましたが、そこには 1 つの問題があります。

#3: Touchmove と MouseMove は同じではない

私が経験したいくつかのデベロッパーが陥りやすい落とし穴に、touchmove ハンドラと mousemove ハンドラで同じコードパスを呼び出さなければならないという落とし穴がありました。これらのイベントの動作はかなり似ていますが、微妙に異なります。タッチイベントは常にタップが開始した要素をターゲットにし、マウスイベントは、現在マウスカーソルの下にある要素をターゲットとします。そのため、マウスオーバー イベントとマウスアウト イベントはありますが、対応するタッチオーバー イベントとタッチアウト イベントは存在せず、タッチエンドのみとなっています。

この問題が最もよく見られるのは、ユーザーが触れ始めた要素をたまたま削除(または移動)した場合です。たとえば、カスタムのスクロール動作をサポートするために、カルーセル全体にタッチハンドラを備えた画像カルーセルを想像してください。使用可能な画像の変化に応じて、一部の <img> 要素を削除し、他の要素を追加します。ユーザーが画像の 1 つに触れて削除した場合、ハンドラ(img 要素の祖先にある)はタッチイベントの受信を停止します(ツリー内に存在しないターゲットにディスパッチされるため)。これは、ユーザーが 1 か所で指を置いているように見えます。

当然ながら、この問題を回避するには、タップがアクティブなときに、タッチハンドラを持つ(またはタッチハンドラを持つ祖先を持つ)要素を削除しないようにします。または、静的な touchend ハンドラや touchmove ハンドラを登録するのではなく、touchstart イベントを取得するまで待ってから、touchmove / touchend / touchcancel ハンドラを touchstart イベントの target に追加する(終了時またはキャンセル時にも削除する)ことをおすすめします。こうすると、ターゲット要素が移動または削除されても、引き続きタッチのイベントを受け取ることができます。こちらでお試しください。赤いボックスをタップし、Esc キーを押したまま DOM から削除してください。

4: タップと :Hover

マウスポインタのメタファーによって、カーソルの位置がアクティブに選択される状況が分離され、デベロッパーはホバー状態を使用して、ユーザーに関連する情報の表示と非表示を切り替えられるようになりました。ただし、現在のところ、ほとんどのタッチ インターフェースは、ターゲットに指が「カーソルを合わせる」ことは検出しません。そのため、タップ操作でこの情報にアクセスする方法を提供しない限り、カーソルを合わせたときに意味的に重要な情報(たとえば「このコントロールの内容」ポップアップの表示など)を提供することは認められていません。ホバーを使用してユーザーに情報を伝える方法については、注意が必要です。

興味深いことに、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 を必要とせずに、マウスオーバー、クリック、タップ、キー操作で追加情報を切り替えることができます。ウェブオーディオ プレイグラウンドをタッチ インターフェースで適切に動作させる作業を始めたとき、私はうれしい驚きでした。この種の構造を使用していたので、ポップアウト メニューはタップ操作でうまく機能していました。

上記のメソッドは、マウスポインタ ベースのインターフェースでもタッチ インターフェースでも適切に機能します。これは、カーソルを合わせたときに「タイトル」属性を使用する場合とは対照的です。「タイトル」属性は、要素がアクティブになっても表示されません。

<img src="/awesome.png" title="this doesn't show up in touch">

5: タップとマウスの精度の比較

マウスは概念的には現実から切り離されていますが、基盤となるオペレーティング システムがカーソルの正確なピクセル精度を追跡するのが一般的であるため、非常に正確であることがわかりました。一方、モバイル デベロッパーは、タッチ スクリーン上での指でのタップ操作の精度がそれほど高くないことを知っています。これは主に、画面に接触したときの指の表面領域のサイズ(および指が画面を遮ること)が原因です。

多くの個人や企業が、指による操作に対応するアプリケーションやサイトの設計方法について広範なユーザー調査を行っており、このトピックに関する多くの書籍が執筆されています。基本的なアドバイスとして、パディングを大きくしてタップ ターゲットのサイズを大きくし、要素間のマージンを大きくしてタップの誤りの可能性を減らすことをおすすめします。(タップイベントとクリック イベントのヒット検出処理に余白は含まれませんが、パディングは含まれます)。ウェブ オーディオ プレイグラウンドに対して行った主な修正の 1 つは、接続ポイントのサイズを大きくして、より正確にタップしやすくすることでした。

タッチベースのインターフェースを扱う多くのブラウザ ベンダーは、ユーザーが画面にタッチしたときに正しい要素をターゲットにしてクリックの可能性を低減するロジックをブラウザに導入しています。ただし、これは通常、クリック イベントを修正し、移動は修正しません(ただし、Internet Explorer はマウスダウン/マウス移動/マウスアップ イベントも変更しているようです)。

6: タップハンドラを封じ込めないとスクロールがジャンクする

また、タッチハンドラを必要な要素だけに制限することも重要です。タッチ要素の帯域幅は非常に広いため、スクロール要素のタッチハンドラを使用しないようにすることが重要です(処理が、高速でジャンクのないタッチ スクロールのためのブラウザ最適化の妨げになる可能性があるためです。最新のブラウザは GPU スレッドでのスクロールを試みますが、各タッチイベントがアプリで処理されるかどうかを最初に確認しなければならない場合、これは不可能です)。この動作のをご確認ください。

この問題を回避するために従うべきガイダンスの一つは、UI のごく一部でのみタッチイベントを処理する場合、そこに(ページの <body> などではなく)タッチハンドラのみをアタッチすることです。つまり、タッチハンドラの範囲を可能な限り制限します。

#7: マルチタッチ

最後の興味深い課題は、これまで「タッチ」ユーザー インターフェースと呼んできましたが、ほぼほぼ例外なくサポートされているのはマルチタッチです。つまり、API は一度に複数のタッチ入力を提供します。アプリケーションでタップのサポートを開始する場合は、複数のタップがアプリケーションに及ぼす影響を考慮する必要があります。

これまでマウスで操作するアプリを作成してきた場合は、カーソル ポイントは 1 つしか使用していないアプリに慣れているでしょう。システムは通常、複数のマウスカーソルをサポートしていません。多くのアプリケーションでは、タッチイベントを単一のカーソル インターフェースにマッピングするだけで済みますが、デスクトップのタップ入力で見てきたハードウェアのほとんどは、少なくとも 2 つの同時入力を処理でき、ほとんどの新しいハードウェアは少なくとも 5 つの同時入力をサポートしているようです。オンスクリーン ピアノ キーボードを開発する場合は、複数の同時タップ入力をサポートすることも検討してください。

現在実装されている W3C Touch API には、ハードウェアがサポートするタッチポイントの数を判定する API がないため、ユーザーが求めるタッチポイントの数を可能な限り正確に見積もる必要があります。もちろん、実際のタッチポイントの数にも注意を払います。たとえば、ピアノのアプリでは、タッチポイントが 3 つしか表示されない場合は、「コード」UI を追加することをおすすめします。PointerEvents API には、デバイスの機能を判別するための API があります。

レタッチ

この記事で、マウス操作とともにタッチ操作を実装する際の一般的な課題についてのガイダンスがお役に立てば幸いです。他のアドバイスよりも重要なのは、モバイル、タブレット、マウスとタッチの組み合わせのデスクトップ環境でアプリをテストする必要があるということです。タッチとマウスのハードウェアをお持ちでない場合は、Chrome の「タッチイベントのエミュレート」を使用してさまざまなシナリオをテストできます。

これらのガイダンスに従うだけでは、タップ入力、マウス入力、さらには両方の操作スタイルを同時にうまく機能させる魅力的なインタラクティブ エクスペリエンスを構築できるだけでなく、比較的簡単です。