マウス・タッチ・ペンのタッチ操作イベント
Updated / Published
Windows 8のようなOSの登場により、Web制作においてもデスクトップであれマウスとタッチの両方を同時にサポートする必要が出てきました。加えて、Surfaceを筆頭にペン圧やペンの傾きなども検知できる入力メカニズムとしてペン入力も今後重要になってくるということで、従来のマウスイベント(mouse*)やタッチイベント(touch*)を包括し、なおかつペン入力にも対応するポインタイベント(pointer*)がMicrosfot社より提唱されました。IE10よりこれを先攻実装、2012年11月にW3CにてPointer Eventsワーキンググループが発足、翌2013年5月に勧告候補となり、IE11よりこの勧告候補仕様をサポートしており、今後他の主要ブラウザにおいてもサポートが見込まれます。
本エントリーでは現状のタッチ操作周りのイベントについて、フリック操作のスクリプトを実装する形を想定してそれぞれのイベントをクロスブラウザでフォールバックしながら用いる方法を解説していきます。解説のコードには、フォールバックを含めてわかりやすく簡素化するためにjQueryを用いるものとします。
タッチ操作のイベント比較
新たにペン入力を含めた総合的なポインタイベントは従来のデスクトップのマウスイベント、タブレットやスマートフォンのタッチイベントと同じように使えるので、基本的にはイベント名を変えるだけで対応はできます。それぞれに相当するイベントは次のようにまとめることができます。
マウス | タッチ | 総合(ポインタ) |
---|---|---|
mousedown | touchstart | pointerdown |
mouseenter | touchenter | pointerenter |
mouseleave | touchleave | pointerleave |
mousemove | touchmove | pointermove |
mouseout | - | pointerout |
mouseover | - | pointerover |
mouseup | touchend | pointerup |
- | touchcancel | pointercancel |
UAが各イベントに対応しているかどうか
var _ua = (function(){
return {
Touch:typeof document.ontouchstart != "undefined",
Pointer:window.navigator.pointerEnabled,
MSPoniter:window.navigator.msPointerEnabled
}
})();
UAがタッチイベントをサポートしているかどうかはdocument.ontouchstart
がundefined
を返さないことを条件にし、ポインタイベントをサポートしているかどうかは、navigator.pointerEnabled
(window
オブジェクトは省略可)を用います。なおIE10は先攻実装のため、ベンダー識別子を用いる必要があります。
対応するイベント名を割り当てる
先の比較表に応じて、必要なイベントをわかりやすい変数名に格納して使うようにすると良いでしょう。フリック操作であれば、pointerdown
, pointermove
, pointerup
に応じた他のマウスとタッチのイベントを割り当てます。
var _start = _ua.Pointer ? 'pointerdown' : _ua.MSPointer ? 'MSPointerDown' : _ua.Touch ? 'touchstart' : 'mousedown';
var _move = _ua.Pointer ? 'pointermove' : _ua.MSPointer ? 'MSPointerMove' : _ua.Touch ? 'touchmove' : 'mousemove';
var _end = _ua.Pointer ? 'pointerup' : _ua.MSPointer ? 'MSPointerUp' : _ua.Touch ? 'touchend' : 'mouseup';
- 変数_startにUAの対応状況に応じて順に
pointerdown
,MSPointerDown
,touchstart
,mousedown
のいずれかのイベント名が格納されるようにします。 - 変数_moveにUAの対応状況に応じて順に
pointermove
,MSPointerMove
,touchmove
,mousemove
のいずれかのイベント名が格納されるようにします。 - 変数_endにUAの対応状況に応じて順に
pointerup
,MSPointerUp
,touchend
,mouseup
のいずれかのイベント名が格納されるようにします。
デフォルトの挙動を無効化しておく
#target {
-ms-touch-action : none ; /* for *IE10 */
touch-action : none ;
}
タッチデバイスは、タッチしながら画面をスクロールしたり、二本の指でピンチ操作やストレッチ操作を行うとページ全体を縮小したり拡大したりしますが、タッチ操作を扱うコンテンツにおいては、このデフォルトの挙動が邪魔をします。そのため、これらの既定のタッチ処理をCSSで無効にする必要があります。この設定をしておかないと、ポインタイベントでタッチ操作を意図通りに扱うことができなくなります(ポインタイベントが正常に発生しない)ので、注意してください。
touch-action
プロパティは、IE11よりサポートしています。IE10では、ベンダー識別子つきの-ms-touch-action
プロパティを用います。
各イベントを設定する
var _target = $('#target');
//変数_startに格納されたイベント
_target.on(_start, function(e){
var point = e.originalEvent.changedTouches ? e.originalEvent.changedTouches[0] : e;
var startX = point.pageX;
var startY = point.pageY;
var setX = 0;
var setY = 0;
//変数_moveに格納されたイベント
_target.on(_move, function(e){
point = e.originalEvent.changedTouches ? e.originalEvent.changedTouches[0] : e;
setX = point.pageX - startX;
setY = point.pageY - startY;
if(Math.abs(setX) > 5){
//for Android
e.preventDefault();
e.stopPropagation();
}else if(Math.abs(setY) > 5){
//縦スクロール時
return;
}
/*
フリック中の処理
*/
});
//変数_endに格納されたイベント
//このイベントは対象要素ではなく、documentを対象にする
$(document).on(_end, function(e){
point = e.originalEvent.changedTouches ? e.originalEvent.changedTouches[0] : e;
setX = point.pageX - startX;
setY = point.pageY - startY;
if(setX < -5){
/*
左にフリックされた場合の処理
*/
}else if(setX > 5){
/*
右にフリックされた場合の処理
*/
}
//最後にイベントを削除
_target.off(_move);
$(document).off(_end);
});
});
point = e.originalEvent.changedTouches ? e.originalEvent.changedTouches[0] : e;
は、タッチイベントから座標を取得するには、changedTouches
プロパティから取得する必要があり、またjQueryを用いているのであわせてoriginalEvent
オブジェクトにアクセスする必要があります。そこで、e.originalEvent.changedTouches
が存在するかどうかで座標を取得するための変数point
にタッチイベント用の座標とマウスイベント&ポインタイベント用の座標とをわけて格納できるようにします。
_moveに格納されたイベントの中でAndroidではtouchmove
中にe.preventDefault();
を設定しなければ、touchend
が正常に発生しません。ただし、縦スクロールまでを無効化してしまうため、X軸方向にフリックされているMath.abs(setX) > 5
を条件にe.preventDefault();
を設定するようにします。
以上ここまで紹介してきた点を抑えておけば、クロスブラウザでのフォールバックをしながら、マウスイベント、タッチイベント、ポインタイベントをそれぞれ上手く使い分けることができるでしょう。
以降はポインタイベントのみに該当する内容となります。
マウス・タッチ・ペンの検出
if(_ua.Pointer || _ua.MSPointer){
if( e.pointerType == "mouse"||4 ) {
/*マウスの場合*/
}else if( e.pointerType == "touch"||2 ) {
/*タッチの場合*/
}else if( e.pointerType == "pen"||3 ) {
/*ペンの場合*/
}
}
pointerType
プロパティを用いて、ポインタイベントのイベントソースがマウスかタッチかペンかを検出できます。勧告候補仕様では、mouse
, touch
, pen
の文字列を返しますが、先攻実装のIE10ではマウスの場合は4
, タッチの場合は2
, ペンの場合は3
の整数値を返します。
ペンやタッチの場合にマルチタッチ機能に対応しているかどうか
if( window.navigator.maxTouchPoints && window.navigator.maxTouchPoints > 1) {
...
}
navigator.maxTouchPoints
プロパティ(window
オブジェクトは省略可)でペンやタッチの場合の最大タッチ数を取得できます。最大タッチ数が2以上あることを条件にマルチタッチ機能に対応しているハードウェアかどうかを判別できます。
なお、ポインタイベントにはpointercancel
がありますが、このイベントが発生するのは稀で、画面上で同時タッチポイントが2つまでサポートされているハードウェアであれば、3つ目のポイントが画面に追加されたときに、同時に3つのポイントは追跡できないために、ポイントの1つが取り消されて、pointercancel
のイベントが発生することになります。
参考リソース
navigatorオブジェクトのポインタに関するプロパティ
pointerEnabled
- ユーザエージェントがポインタイベントを実装していればtrueを、そうでなければfalseを返します。
maxTouchPoints
- ハードウェアがサポートしているペンやタッチの場合の最大タッチ数を取得できます。
ポインタイベント
pointerdown
- 指がタッチパネルに触れた、または、マウスのボタンを押した、または、ペンがデジタイザーに触れたときに発生します。
pointerenter
- ポインタが対象要素または子孫要素の境界に入ったときに発生します。
pointerover
と似ていますがこのイベントは、イベントバブリングを補足しませんので、対象要素の上に別の要素が重なる場合は1度しか発生しない違いがあります。 pointerleave
- ポインタが対象要素または子孫要素の境界を越えて中から外に出たときに発生します。
pointerout
と似ていますがこのイベントは、イベントバブリングを補足しませんので、対象要素の上に別の要素が重なる場合は1度しか発生しない違いがあります。 pointermove
- 指をタッチパネルに触れたまま動かしたとき、または、マウスのボタンを押さない状態でカーソルを動かしたとき、または、ペンをデジタイザーに触れた状態で動かしたときに発生します。
pointerout
- 指をタッチパネルに触れたままで動かした状態で、または、マウスのボタンを押しているかどうかにかかわらずマウスのカーソルを動かしている状態で、または、ペンがデジタイザーに触れているかどうかにかかわらずペンを動かしている状態で、それらのポインタが要素の境界を越えて中から外に出たときに発生します。
pointerover
- 指をタッチパネルに触れたままで動かした状態で、または、マウスのボタンを押しているかどうかにかかわらずマウスのカーソルを動かしている状態で、または、ペンがデジタイザーに触れているかどうかにかかわらずペンを動かしている状態で、それらのポインタが要素の境界を超えて外から中に入ったときに発生します。
pointerup
- 指をタッチパネルから離した、または、マウスのボタンを離した、または、ペンをデジタイザーから離したときに発生します。
pointercancel
- システムがポインタを破棄したときに発生します。このイベントが発生することは希ですが、具体的には、タッチ入力またはペン入力を使っているとき、画面上のポインタが取り消される場合があります。たとえば、画面上で同時タッチポイントが2つまでサポートされているときに3つ目のポイントを画面に追加した場合、ハードウェアは3つのポイントを追跡できないので、ポイントの1つが取り消されます。いくつまでのタッチポイントをサポートできるかは
window.navigator.maxTouchPoints
プロパティを使用。
ポインタイベントオブジェクトのプロパティ
isPrimary
- プライマリーポインタならtrueを、そうでなければfalseを返します。プライマリーポインタとは、最初の接触を意味します。たとえば、二本の指でタッチした場合、必ず一方が最初に接触するはずです。この最初の接触ではtrueを、二本目の接触ではfalseを返します。マウスの場合は常にtrueを返します。
pointerId
- タッチ、マウス、ペンの接触の一意的な識別子を数値で返します。
pointerType
- イベントのソースがタッチ、ペン、マウスのどれに相当するのかを文字列で返します。touchはタッチ, penはペン, mouseはマウスを表します。なお、IE10は整数値を返し2はタッチ、3はペン、4はマウスを表します。
pressure
- ペン圧を0~1の範囲の数値で返します。指でタッチした場合や、マウスのボタン押すなどのアクティブな接触の場合は値 0.5 を返し、それ以外の場合、筆圧をサポートしないハードウェアでは 0 を返します。IE10ではペン以外は常に0を返します。
tiltX
- ペンの左右の傾き角度を-90~90の範囲の数値で返します。右方向に傾けば正の値になり、左方向に方向けば負の値になります。タッチ、マウスの場合は常に0となります。
tiltY
- ペンの前後の傾き角度を-90~90の範囲の数値で返します。手前に傾けば正の値になり、前方に傾けば負の値になります。タッチ、マウスの場合は常に0となります。