Internet Explorer MVP による IE Test Drive 技術解説 : Gesture イベント

Internet Explorer ブログ (日本語版) では、これから数回にわたり Internet Explorer Test Drive に掲載されているサンプルについて、Internet Explorer の MVP の方々からご寄稿いただいた技術解説をお送りします。

第一回目は、HTML5.JP の管理人としてもおなじみの羽田野 太巳さんによるGesture イベントの解説です。

Gestureイベント

近年、タッチデバイスの普及により、ウェブアプリケーションの操作方法が大きく変わってきました。マウスでは、クリック、ドラッグといった操作が中心でしたが、タッチデバイスでは、ジェスチャーと呼ばれるタッチ操作が求められるようになってきました。

ジェスチャーとは、一本の指でタッチパネルを触る操作だけでなく、二本以上の指を使った操作も加わります。これまでは、W3C DOM Level 3 Events 仕様に規定されたMouseEvent に基づいて、スクリプトを書いてきました。指一本でタップするなどの簡単な操作であれば、MouseEvent で代用できます。しかし、複数の指を使った操作をMouseEvent で処理するのは無理があります。

このような状況の中、Internet Explorer 10 では、ジェスチャーを扱うAPIが新たに実装されました。それを確かめるには、Internet Explorer Test Drive に公開されているBrowser Surface というデモを試すのが良いでしょう。

 

Internet Explorer Test Drive - Browser Surface
https://ie.microsoft.com/testdrive/Browser/BrowserSurface/

 

Browser Surface

images/gesture_ie_tset_drive_browser_surface.png

image

このデモでは、タッチデバイスであれば、指を使って、画面右下に重ねて表示された写真を自由に移動、回転、拡大、縮小することができます。そして、その写真を指で長押しすることで、画面に固定することができます。もちろん、マウスによる操作も可能ですが、マウスでできるのは移動と固定だけです。

残念ながら、ジェスチャーに関する API はウェブ標準として提案されていません。Internet Explorer 10 には、Windows 8 の原則に合わせた Gesture イベントの API が実装されています。Internet Explorer 10 専用の API ですが、前述のジェスチャー操作を簡単に JavaScript から扱うことができるようになります。

本記事では、このデモのうち、タッチ操作に使われる Gesture イベントにフォーカスして、サンプルを通して、その使い方を解説します。

 

ジェスチャーの種類

タッチデバイスでは、単にタップするだけではなく、さまざまな操作を駆使することになります。ここでは、タッチデバイスにおける代表的なジェスチャーを紹介しましょう。

 

代表的なジェスチャー

操作名      

説明

長押し

1本の指で画面をタッチし、しばらく押し続けます。

スライド

1本または複数の指で画面をタッチし、同じ方向に動かします。

スワイプ

1本または複数の指で画面をタッチし、同じ方向に少しだけ動かし、はじくように画面から指を離します。

回転

2本以上の指で画面をタッチし、タッチしたままの状態で、時計回りまたは反時計回りに回します。

ピンチ

2本以上の指で画面をタッチし、それらの指を近づけていきます。

ストレッチ

2本以上の指で画面をタッチし、タッチしたままの状態で、それらの指を広げていきます。

 

前述のデモは、スライドすることで写真を移動し、回転することで写真を回転し、ピンチすることで写真を縮小し、ストレッチすることで写真を拡大します。さらに、スワイプによる慣性動作もサポートしています。そして、長押しすることで写真を固定します。

こういったジェスチャーは、マウスイベントや Pointer イベントを駆使して何とか作り込むことも可能ですが、非常に面倒です。また、ジェスチャーの挙動を自作してしまうと、OSが定める挙動と一致しない場合もあり、ユーザーを混乱させてしまいます。たとえば、長押しに必要な待ち時間や慣性動作の減速の度合いなどです。できる限り、その OS の原則に従った方が良いのは言うまでもありません。

 

Gesture イベントの概要

Gesture イベントの特徴は、さまざまなジェスチャーを簡単な方法で検知できる点です。たとえば、回転であれば、二本以上の指によるタッチを同時に、かつ、リアルタイムに検知し、その 2 つのタッチの動きから回転角度を計算しなければいけません。ピンチやストレッチも同様です。このように2つ以上のタッチを同時に処理して、そのジェスチャーの挙動を作り込むのはとても面倒な作業です。しかし、Gesture イベントは、この面倒な計算処理をシステムに任せてしまい、スクリプトでは、その結果だけを受け取れば良いのです。

Gesture イベントで定義されているイベントのうち、いくつかを紹介しましょう。

 

Gesture イベント

イベント名

説明

MSGestureChange

タッチした指が動いている間に連続して発生します。スワイプなどで慣性動作を伴えば、その処理の間にも連続して発生します。

MSGestureEnd

すべての接触が無くなったときに発生します。ただし、スワイプなどで慣性動作を伴う場合は、その処理が終わったときに発生します。

MSGestureStart

画面をタッチしたときに発生します。

MSInertiaStart

所定の速度でスワイプしてから指が画面から離れたときに発生します。ゆっくりと指を動かしても発生しません。

MSGestureHold

画面をタッチしてから、同じ場所でしばらくタッチし続けると発生します。つまり長押しが検知されたら発生します。さらに、長押しの後、タッチを離したときにも発生します。

MSGestureTap

タッチ、マウス クリック、ペン タップしたときに発生します。基本的には、clickイベントと同じです。

 

次のコードは、html 要素で発生する MSGestureHold イベントを捕捉します

// html 要素の DOM オブジェクト var html_el = document.querySelector("html"); // MSGesture オブジェクトを生成 var gesture = new MSGesture(); // html 要素を MSGesture オブジェクトに結びづける gesture.target = html_el; // MSGesture オブジェクトにポインターを結びづける html_el.addEventListener("MSPointerDown", function(e) {    gesture.addPointer(e.pointerId); }, false); // html 要素に MSPointerDown イベントのリスナーをセット html_el.addEventListener("MSGestureHold", function(e) {    // イベントが発生したときの処理   console.log(e.type); }, false);

一般的なイベントとは異なり、Gesture  イベントは、単に特定の要素の DOM オブジェクトにイベントリスナーをセットするだけでは捕捉できません。いくつかの事前準備が必要となります。

まず MSGesture オブジェクト (変数 gesture ) を生成します。そして、MSGesture オブジェクト(変数 gesture )の target プロパティに、Gesture イベントを捕捉したい要素のDOM オブジェクト(変数 html_el )をセットします。最後に、DOM オブジェクト(変数 html_el )に、MSPointerDown イベントのイベントリスナーをセットします。このイベントが発生したら、MSGesture オブジェクト(変数 gesture )の addPointer() メソッドを使って、ポインターの識別 ID (e.pointerId) を登録します。

MSGesture オブジェクトとは、さまざまなジェスチャーの認識モジュールだと考えてください。そして、この MSGesture オブジェクトに、ジェスチャーを受け取りたい要素を登録しておきます。最後に、その要素で発生する MSPointerDown イベントを通して、それぞれのポインターを登録するのです。これによって、MSGesture オブジェクトは、指定された要素で発生した複数のタッチを同時に処理して、指定の要素に Gesture イベントを発生させるのです。スクリプトでは、ジェスチャーの認識処理を作り込む必要はなく、そのイベントを捕捉するだけで良いのです。

前述のコードは長押しを検知しましたが、実際には2回検知します。まず指でタッチしてしばらくして長押しが検知されたときと、その後に指を離したときです。該当のイベント発生が、どちらを指しているのかを判定する方法については、後述します。

Gesture イベントが発生したときに呼び出されるコールバック関数には、MSGestureEvent オブジェクト(変数e)が引数に与えられます。このオブジェクトから、イベントに関するさまざまな情報を取得することができます。ここでは、MSGestureEvent オブジェクトのプロパティのいくつかを紹介しましょう。

 

MSGestureEvent オブジェクトのプロパティ

プロパティ

説明

rotation

前回のイベント発生時からの回転角を返します。単位はラジアンです。時計回り(右回り)なら正の値が、反時計回り(左回り)なら負の値を返します。

scale

前回のイベント発生時からのズーム倍率を返します。

translationX

前回のイベント発生時からのx軸方向の移動距離を返します。

translationY

前回のイベント発生時からのy軸方向の移動距離を返します。

 

他にもいくつかのプロパティが利用できますが、おおむね、上記のプロパティだけで、ほとんどのシーンを扱うことが可能です。これらのプロパティの使い方については、サンプルを通して解説します。

 

サンプル

では、Gesture イベントを使ったサンプルを見てみましょう。次のサンプルは、Internet Explorer Test Drive に公開されている Browser Surface を簡素化したデモです。

サンプル
https://www.html5.jp/experiments/IE10_test_drive/gesture.html

画面には写真が一つだけ表示されています。

 

デフォルトの状態

images/gesture_sample_1.png
image

 

指で写真をタッチしてスライドすると、自由に移動することができます。さらに写真をスワイプすると、慣性動作が発生します。

 

移動した場合

images/gesture_sample_2.png
image 

写真を二本の指でピンチまたはストレッチすると、写真を縮小したり拡大したりすることができます。さらに回転することもできます。

 

回転および拡大した場合

images/gesture_sample_3.png
image 

写真を長押しすると、その写真を固定して、以降のジェスチャーを受け付けないようにします。

 

固定した場合

images/gesture_sample_4.png
image 

固定した写真を、もう一度長押しすると、再度、写真のジェスチャー操作を受け付けます。

まず、このサンプルのHTMLとCSSをご覧ください。

HTML
< body>
  <h1>Gestureイベントのサンプル</h1>
  <img id="pic" src="pic.jpg" alt="">
< /body>

CSS
html {
  -ms-touch-action: none;    

}

CSS は、実際にはさまざまなスタイルが記述されていますが、ここでは、ポイントを絞って解説します。

タッチデバイスでは、通常、二本の指でピンチ操作やストレッチ操作を行うと、ブラウザーに表示されたコンテンツを縮小したり拡大したりします。このサンプルのように、タッチ操作を扱うコンテンツにおいては、このデフォルトの挙動が邪魔をします。そのため、CSS を使って、これらの挙動を無効にします。これを忘れると、タッチ操作を上図に扱うことができなくなりますので、注意してください。

では、スクリプトを見ていきましょう。まずは、ブラウザーが Gesture イベントをサポートしているかを判定します。

 

Gesture イベントの実装の判定

document.addEventListener("DOMContentLoaded", function() {
    // Gesture イベントのサポートの検出
   if(window.MSGesture) {   
      gestureInit();
    }
}, false);

Gesture イベントをブラウザーが実装しているかどうかは、window.MSGesture が存在するかどうかで判定します。

このサンプルでは、Gesture イベントを実装している場合にのみ処理を続け、gestureInit() 関数を呼び出しています。

gestureInit() 関数では、このサンプルを動作させるための下準備を行います。

 

gestureInit() 関数

function gestureInit() {
     // html 要素の DOM オブジェクト
    var html_el = document.querySelector("html");
     // コンテキストメニュー表示をキャンセル
    html_el.addEventListener("contextmenu", function(e) {
        e.preventDefault();
     }, false);
     // マウスドラッグをキャンセル
    html_el.addEventListener("dragstart", function(e) {
        e.preventDefault();
     }, false);
     // テキスト選択をキャンセル
    html_el.addEventListener("selectstart", function(e) {
        e.preventDefault();
     }, false);
     // img 要素の DOM オブジェクト
    var img_el = document.getElementById("pic");
     // MSGesture オブジェクトを生成
    var gesture = new MSGesture();
     // img 要素を MSGesture オブジェクトに結びづける
    gesture.target = img_el;
     // img 要素に MSPointerDown イベントのリスナーをセット
    img_el.addEventListener("MSPointerDown", function (e) {
        // MSGesture オブジェクトにポインターを結びづける
       gesture.addPointer(e.pointerId);
     }, false);
     // img 要素に Gesture イベントのリスナーをセット
    img_el.addEventListener("MSGestureChange", imgTransform, false);
     img_el.addEventListener("MSGestureHold", imgHold, false);
}

タッチ操作を扱う場合、さまざまなデフォルトアクションが操作の邪魔をしてしまいます。そのため、いくつかのデフォルトアクションをキャンセルしておきます。ここでは、html 要素で発生する contextmenu イベント、dragstart イベント、selectstart イベントのデフォルトアクションをキャンセルします。

次に、前述の通り、img 要素で Gesture イベントを扱うための事前準備を行います。まず MSGesture オブジェクト(変数 gesture )を生成し、その target プロパティに img 要素の DOM オブジェクトをセットして、img 要素を MSGesture オブジェクトに結びづけます。そして、img 要素に MSPointerDown イベントのリスナーをセットし、MSPointerDown イベントが発生したら、まず MSGesture オブジェクト(変数 gesture )の addPointer() メソッドを呼び出して、ポインター( e.pointerId )を結びづけます。

最後に、img 要素に、各種 Gesture イベントのリスナーをセットします。ここでは、MSGestureChange イベント、MSGestureHold イベントのリスナーをセットします。

では、MSGestureChange イベントが発生したときに呼び出される imgTransform() 関数の処理を見てみましょう。

imgTransform()関数

function imgTransform(e) {
    if(e.target.classList.contains("fixed")) {
       return;
    }
    // 現在のCSS transformsのマトリックスを取得
   var matrix = new MSCSSMatrix(e.target.style.msTransform);
    // CSS transformのマトリックスに遷移分を追加
   e.target.style.msTransform = matrix.
       translate(e.translationX, e.translationY).
       rotate(e.rotation * 180 / Math.PI).
       scale(e.scale);
}

この関数では、まず、長押しによって固定されているかどうかを評価します。後に説明しますが、画像が長押しされたら、img 要素の class 属性のトークンとして "fixed" を追加します。そのため、ここでは、そのトークンが存在するかどうかを評価します。もしそのトークンが存在していれば、すでに固定済みを意味していますので、何もせずに関数を終了します。

この関数で重要な役割とは、ジェスチャーが発生したら、その動きに合わせて画像を移動、回転、拡大、縮小することです。この処理には、CSS Transforms を使います。ここでは、CSS のプロパティ値を文字列としてセットするのではなく、MSCSSMatrix オブジェクトを使っています。

ジェスチャーの動作に関する情報は、この関数の第一引数に与えられる MSGestureEvent オブジェクト(変数e) から得られます。スライドやスワイプが発生すると、translationX プロパティと translationY プロパティから移動距離が得られます。回転が発生すると、rotation プロパティから回転角(ラジアン)が得られます。そして、ピンチまたはストレッチが発生すると、scale プロパティからズーム倍率が得られます。これらの情報をまとめて MSCSSMatrix オブジェクトにセットすることで、指の動作に合わせた動きを画像に与えられます。

では、最後に、MSGestureHold イベントが発生したときに呼び出される imgHold() 関数の処理を見てみましょう。

imgHold()関数

function imgHold(e) {
    if(e.detail === e.MSGESTURE_FLAG_BEGIN) {
        e.target.classList.toggle("fixed");
     }
}

この関数の役割は、画像が長押しによって、固定と解除を行うことです。MSGestureHold イベントは、長押しがシステムに認識されたときだけではなく、指を離したときにも発生してしまいます。このサンプルでは前者の場合だけに処理を行うようにしなければいけません。そのためには、この関数の第一引数に与えられる MSGestureEvent オブジェクト(変数e)の detail プロパティの値を評価します。この値が e.MSGESTURE_FLAG_BEGIN(1) と一致すれば、長押しが確定した瞬間を意味します。そして、この値がe.MSGESTURE_FLAG_END(2) と一致すれば、指を離した瞬間を意味します。

このサンプルでは、img 要素の class 属性にトークンとして "fixed" を加えることで、画像が固定されたことを表しています。このトークンが加わることで、CSS のスタイルが適用され、見た目にも固定されたかのように見せかけています。

class 属性のトークンの切り替えには、img 要素の DOM オブジェクト(e.target)の classList オブジェクトの toggle() メソッドを使います。

 

まとめ

今後、タッチパネルがスマートフォンだけでなくパソコンにも普及するなか、アプリケーションには、タッチデバイス向けのジェスチャーのサポートが一層に求められてきます。Gestureイベントは、Internet Explorer 10に限定されてしまいますが、ジェスチャーを駆使したアプリケーション作成の参考になれば幸いです。

 

リファレンス

本記事のサンプル
https://www.html5.jp/experiments/IE10_test_drive/gesture.html

 

Internet Explorer Test Drive - Browser Surface
https://ie.microsoft.com/testdrive/Browser/BrowserSurface/

 

MSDN - MSGestureEvent object
https://msdn.microsoft.com/ja-jp/library/hh772076(v=vs.85).aspx

 

MSDN - MSCSSMatrix object
https://msdn.microsoft.com/ja-jp/library/windows/apps/hh453593.aspx

 

W3C CSS Transforms
https://www.w3.org/TR/css3-3d-transforms/

 

 

★お知らせ★

本記事を執筆されました 羽田野太巳さんが、この度 Windows 8 アプリの開発入門書を出版されました。

本書では、標準的な HTML + JavaScript だけでなく、 Windows ストア アプリ固有の Windows API、WinJS についても詳しく書かれており、MSDN ライブラリの開発チュートリアルなどを読んでいくうえで戸惑いがちなところも見事に解説されています。

現在、HTML + JavaScript の知識を持っており、これから Windows ストア アプリの開発をこれから始める人、既に初めているけどもう少し踏み込みたい人にもお勧めの 1 冊です。
ぜひお手に取ってご覧ください。

『HTMLとJavaScriptではじめるWindowsストアアプリ開発入門』 (秀和システム 羽田野太巳・著)
https://www.shuwasystem.co.jp/products/7980html/3572.html