Pointer events
今日のウェブコンテンツの多くは、ユーザーのポインティングデバイスがマウスであることを前提としています。 しかしながら、多くのデバイスがペン/スタイラスおよびタッチ面のような他のタイプのポインティング入力デバイスをサポートしているので、既存のポインティングデバイスのイベントモデルへの拡張が必要であり、そしてポインタイベントはその必要性に対処します。
ポインタイベントは、ポインティングデバイスに対して発生する DOM イベントです。 これらは、マウス、ペン/スタイラス、(1本以上の指でなどの)タッチなどのポインティング入力デバイスを処理する単一の DOM イベントモデルを作成するように設計されています。 ポインタは、特定の画面座標セットをターゲットにできるハードウェアにとらわれないデバイスです。 ポインタに単一のイベントモデルを使用すると、ウェブサイトやウェブアプリの作成が簡単になり、ユーザーのハードウェアに関係なく優れたユーザーエクスペリエンスを提供できます。 しかしながら、デバイス固有の処理が必要なシナリオのために、ポインタイベントはイベントを生成したデバイスタイプを調べるための pointerType
プロパティを定義しています。
一般的なポインタ入力を処理するために必要なイベントは、マウスイベント
に似ています(mousedown/pointerdown
、mousemove/pointermove
など)。 したがって、ポインタイベントタイプは意図的にマウスイベントタイプと似せています。 さらに、ポインタイベントには、マウスイベントに存在する通常のプロパティ(クライアント座標、ターゲット要素、ボタンの状態など)、およびその他の入力形式のための新しいプロパティ(圧力、接触ジオメトリ、傾きなど)が含まれています。 実際、PointerEvent
インターフェイスはすべての MouseEvent
のプロパティを継承するため、マウスイベントからポインタイベントへのコンテンツの移行が容易になります。
用語
- ポインタが
buttons
プロパティに対してゼロ以外の値を持つ場合の条件。 例えば、ペンの場合は、ペンがデジタイザと物理的に接触しているか、ホバー中に少なくとも1つのボタンが押されているときです。 - アクティブポインタ(active pointer)
- イベントを生成できる任意のポインタ入力デバイス。 ポインタがそれ以上のイベントを生成する可能性がある場合、ポインタはアクティブと見なされます。 例えば、ペンが持ち上げられたり動かされたりすると追加のイベントが発生する可能性があるため、ペンがダウン状態であるとアクティブであると見なされます。
- デジタイザ(digitizer)
- 接触を検出できる表面を備えた検知デバイス。 最も一般的な検知デバイスは、ペン、スタイラス、または指などの入力デバイスからの入力を検知することができるタッチ対応画面です。 検知デバイスの中には、入力デバイスの近接を検出できるものもあり、その状態をマウスにならってホバーと表現します。
- ヒットテスト(hit test)
- ブラウザーがポインタイベントのターゲット要素を決定するために使用するプロセス。 通常、これはポインタの位置と、画面媒体上のドキュメント内の要素の視覚的なレイアウトを考慮して決定されます。
- ポインタ(pointer)
- 画面上の特定の座標(または座標のセット)をターゲットにできる、入力デバイスのハードウェアにとらわれない表現。 ポインタ入力デバイスの例は、マウス、ペン/スタイラス、およびタッチ接触です。
- ポインタキャプチャ(pointer capture)
- ポインタキャプチャでは、ポインタイベントをポインタの位置による通常のヒットテストの結果以外の特定の要素にターゲットしなおすことができます。
- ポインタイベント(pointer event)
- ポインタに対して発生した DOM
イベント
。
インターフェイス
主なインターフェイスは、コンストラクタ
といくつかのイベントタイプおよび関連するグローバルイベントハンドラを持つ PointerEvent
インターフェイスです。 この標準には、Element
インターフェイスと Navigator
インターフェイスの拡張も含まれています。 以下のサブセクションでは、各インターフェイスとプロパティについて簡単に説明します。
PointerEvent インターフェイス
PointerEvent
インターフェイスは、MouseEvent
インターフェイスを拡張したもので、次のプロパティがあります(それらはすべて読取専用 です)。
pointerId
- イベントの原因となっているポインタの一意の識別子。width
- ポインタの接触ジオメトリの幅(X 軸上の大きさ、CSS ピクセル単位)。height
- ポインタの接触ジオメトリの高さ(Y 軸上の大きさ、CSS ピクセル単位)。pressure
- 0 から 1 までの範囲のポインタ入力の正規化された圧力。 ここで、0 と 1 は、それぞれハードウェアが検出できる最小圧力と最大圧力を表します。tangentialPressure
- ポインタ入力の正規化された接線圧力(バレル圧力またはシリンダー応力(cylinder stress)とも呼ばれます)は -1 から 1 の範囲で、0 はコントロールの中立位置です。tiltX
- Y-Z 平面と、ポインタ(ペン/スタイラスなど)の軸と Y 軸の両方を含む平面との間の平面角度(度単位で、-90 から 90 の範囲)。tiltY
- X-Z 平面と、ポインタ(ペン/スタイラスなど)の軸と X 軸の両方を含む平面との間の平面角度(度単位で、-90 から 90 の範囲)。twist
- ポインタ(ペン/スタイラスなど)の長軸を中心とした時計回りの回転の度数(0 から 359の範囲の値)。pointerType
- イベントの原因となったデバイスタイプ(マウス、ペン、タッチなど)を示します。isPrimary
- ポインタがこのポインタタイプのプライマリポインタを表すかどうかを示します。
イベントタイプとグローバルイベントハンドラ
ポインタイベントには10のイベントタイプがあり、そのうち7つはマウスイベントと同等の意味を持ちます(ダウン(down
)、アップ(up
)、移動(move
)、オーバー(over
)、アウト(out
)、進入(enter
)、離脱(leave
))。 以下は、各イベントタイプとそれに関連するグローバルイベントハンドラ
の簡単な説明です。
イベント | イベントハンドラ | 説明 |
---|---|---|
pointerover |
onpointerover |
ポインタが要素のヒットテスト境界内に移動したときに発生します。 |
pointerenter |
onpointerenter |
ポインタが要素またはその子孫の1つのヒットテスト境界内に移動したときに発生します。 これには、ホバーをサポートしていないデバイスからの pointerdown イベントの結果も含まれます(pointerdown を参照)。 |
pointerdown |
onpointerdown |
ポインタがアクティブボタン状態になったときに発生します。 |
pointermove |
onpointermove |
ポインタが座標を変更したときに発生します。 また、ポインタの状態の変化がこれ以外のイベントで報告できない場合もこのイベントが使われます。 |
pointerup |
onpointerup |
ポインタがアクティブボタン状態でなくなったときに発生します。 |
pointercancel |
onpointercancel |
ブラウザーは、ポインタがイベントを生成できなくなったと判断した場合(例えば、関連デバイスが無効になった場合)、このイベントを発生させます。 |
pointerout |
onpointerout |
次のようないくつかの理由で発生します。 ポインタが要素のヒットテスト境界外に移動した。 ホバーをサポートしていないデバイスの pointerup イベントが発生した(pointerup を参照)。 pointercancel イベントの発生後(pointercancel を参照)。 ペン/スタイラスがデジタイザで検出可能なホバー範囲を離脱したとき。 |
pointerleave |
onpointerleave |
ポインタが要素のヒットテスト境界外に移動したときに発生します。 ペンデバイスの場合、このイベントは、スタイラスがデジタイザで検出可能なホバー範囲を離脱したときに発生します。 |
gotpointercapture |
ongotpointercapture |
要素がポインタキャプチャを受け取ったときに発生します。 |
lostpointercapture |
onlostpointercapture |
ポインタに対するポインタキャプチャが解放された後に発生します。 |
Element の拡張
Element
インターフェイスには次の3つの拡張機能があります。
setPointerCapture()
- このメソッドは、将来のポインタイベントのキャプチャターゲットとして特定の要素を指定します。releasePointerCapture()
- このメソッドは、特定のポインタイベントに対して以前に設定されたポインタキャプチャを解放(停止)します。
Navigator の拡張
Navigator.maxTouchPoints
プロパティは、任意の時点でサポートしている同時タッチポイントの最大数を決定するために使用します。
例
このセクションでは、ポインタイベントのインターフェイスを使用した基本的な使い方の例を紹介します。
イベントハンドラの登録
この例では、特定の要素のすべてのイベントタイプに対してハンドラを登録します。
<html>
<script>
function over_handler(event) { }
function enter_handler(event) { }
function down_handler(event) { }
function move_handler(event) { }
function up_handler(event) { }
function cancel_handler(event) { }
function out_handler(event) { }
function leave_handler(event) { }
function gotcapture_handler(event) { }
function lostcapture_handler(event) { }
function init() {
var el=document.getElementById("target");
// ポインタイベントハンドラの登録
el.onpointerover = over_handler;
el.onpointerenter = enter_handler;
el.onpointerdown = down_handler;
el.onpointermove = move_handler;
el.onpointerup = up_handler;
el.onpointercancel = cancel_handler;
el.onpointerout = out_handler;
el.onpointerleave = leave_handler;
el.gotpointercapture = gotcapture_handler;
el.lostpointercapture = lostcapture_handler;
}
</script>
<body onload="init();">
<div id="target"> Touch me ... </div>
</body>
</html>
イベントのプロパティ
この例では、タッチイベントのすべてのプロパティにアクセスする方法を示します。
<html>
<script>
var id = -1;
function process_id(event) {
// イベントの識別子に基づいて、このイベントを処理する
}
function process_mouse(event) {
// マウスポインタイベントを処理する
}
function process_pen(event) {
// ペンポインタイベントを処理する
}
function process_touch(event) {
// タッチポインタイベントを処理する
}
function process_tilt(tiltX, tiltY) {
// 傾斜データハンドラ
}
function process_pressure(pressure) {
// 圧力ハンドラ
}
function process_non_primary(event) {
// 非プライマリハンドラ
}
function down_handler(ev) {
// タッチポイントの接触面積を計算する
var area = ev.width * ev.height;
// キャッシュされた id とこのイベントの id を比較し、それに応じて処理する
if (id == ev.identifier) process_id(ev);
// 適切なポインタタイプのハンドラを呼び出す
switch (ev.pointerType) {
case "mouse":
process_mouse(ev);
break;
case "pen":
process_pen(ev);
break;
case "touch":
process_touch(ev);
break;
default:
console.log("pointerType " + ev.pointerType + " はサポートしていません");
}
// 傾斜ハンドラを呼び出す
if (ev.tiltX != 0 && ev.tiltY != 0) process_tilt(ev.tiltX, ev.tiltY);
// 圧力ハンドラを呼び出す
process_pressure(ev.pressure);
// このイベントがプライマリではない場合は、非プライマリハンドラを呼び出す
if (!ev.isPrimary) process_non_primary(ev);
}
function init() {
var el=document.getElementById("target");
// pointerdown ハンドラの登録
el.onpointerdown = down_handler;
}
</script>
<body onload="init();">
<div id="target"> Touch me ... </div>
</body>
</html>
プライマリポインタの決定
シナリオによっては、複数のポインタ(例えば、タッチ画面とマウスの両方を備えたデバイス)やポインタが複数の接触点をサポートする(例えば、複数の指でのタッチをサポートするタッチ画面)ことがあります。 アプリは、isPrimary
プロパティを使用して、各ポインタタイプのアクティブポインタのセットの中からマスターポインタを識別できます。 アプリがプライマリポインタのみをサポートしたい場合は、プライマリではないすべてのポインタイベントを無視できます。
マウスの場合、ポインタは1つだけなので、常にプライマリポインタになります。 タッチ入力の場合、他にアクティブなタッチがなかったときにユーザーが画面にタッチした場合、ポインタはプライマリと見なされます。 ペンとスタイラスの入力では、他のアクティブなペンが画面に接触していないときにユーザーのペンが最初に画面に接触した場合、ポインタはプライマリと見なされます。
ボタンの状態の判断
マウスやペンなどの一部のポインタデバイスは複数のボタンをサポートしており、ボタンの押下で和音を弾くことができます。 すなわち、ポインタデバイス上の別のボタンが既に押されている間に、追加のボタンを押すことができます。 ボタンが押された状態を判断するために、ポインタイベントは(PointerEvent
の継承元の)MouseEvent
インターフェイスの button
プロパティおよび buttons
プロパティを使用します。 次の表は、さまざまなデバイスボタンの状態に対応する button
および buttons
の値を示しています。
デバイスボタンの状態 | button | buttons |
---|---|---|
最後のイベント以降、ボタンもタッチ/ペンの接触も変更されませんでした | -1 | — |
ボタンを押さずにマウスを動かした、ボタンを押さずにホバー中にペンを動かした | — | 0 |
左マウス、タッチ接触、ペン接触 | 0 | 1 |
中マウス | 1 | 4 |
右マウス、ペンのバレルボタン | 2 | 2 |
X1(戻る)マウス | 3 | 8 |
X2(進む)マウス | 4 | 16 |
ペンの消しゴムボタン | 5 | 32 |
button
プロパティは、ボタンの状態の変化を示していることに注意してください。 ただし、タッチの場合のように、1つのイベントに伴って複数のイベントが発生する場合は、それらはすべて同じ値になります。
ポインタキャプチャ
ポインタキャプチャでは、ポインタの位置での通常のヒットテストではなく、特定のポインタイベント
のイベントを特定の要素にターゲットしなおすことができます。 これは、ポインタデバイスの接触が要素から外れた場合でも、要素がポインタイベントを受信し続けるようにするために使用できます(例えば、スクロールで)。
次の例では、要素にポインタキャプチャを設定しています。
<html>
<script>
function downHandler(ev) {
var el=document.getElementById("target");
// 要素 'target' はそれ以上のイベントを受信/キャプチャします
el.setPointerCapture(ev.pointerId);
}
function init() {
var el=document.getElementById("target");
el.onpointerdown = downHandler;
}
</script>
<body onload="init();">
<div id="target"> Touch me ... </div>
</body>
</html>
次の例は、(pointercancel
イベントが発生したときに)ポインタキャプチャを解放しています。 pointerup
イベントや pointercancel
イベントが発生すると、ブラウザーはこれを自動的に行います。
<html>
<script>
function downHandler(ev) {
var el=document.getElementById("target");
// 要素 "target" はそれ以上のイベントを受信/キャプチャします
el.setPointerCapture(ev.pointerId);
}
function cancelHandler(ev) {
var el=document.getElementById("target");
// ポインタキャプチャを解放する
el.releasePointerCapture(ev.pointerId);
}
function init() {
var el=document.getElementById("target");
// pointerdown と pointercancel のハンドラの登録
el.onpointerdown = downHandler;
el.onpointercancel = cancelHandler;
}
</script>
<body onload="init();">
<div id="target"> Touch me ... </div>
</body>
</html>
touch-action プロパティ
touch-action
CSS プロパティは、ブラウザーがデフォルトの(ネイティブな)タッチの振る舞い(ズームやパンなど)を領域に適用するかどうかを指定するために使用します。 このプロパティは、置換されていないインライン要素、テーブル行、行グループ、テーブル列、および列グループを除くすべての要素に適用できます。
auto
の値は、ブラウザーがそのデフォルトのタッチの振る舞いを(指定された領域に)自由に適用できることを意味し、none
の値はブラウザーのその領域に対するデフォルトのタッチの振る舞いを無効にします。 値 pan-x
および pan-y
は、指定された領域で始まるタッチがそれぞれ水平スクロールおよび垂直スクロール専用であることを意味します。 値 manipulation
は、ブラウザーがその要素から始まるタッチをスクロールとズームのみを考慮していることを意味します。
次の例では、ブラウザーのデフォルトのタッチの振る舞いは div
要素に対して無効になります。
<html>
<body>
<div style="touch-action:none;">Can't touch this ... </div>
</body>
</html>
次の例では、一部の button
要素ではデフォルトのタッチの振る舞いが無効になります。
button#tiny {
touch-action: none;
}
次の例では、target
要素にタッチすると、水平方向にのみパンします。
#target {
touch-action: pan-x;
}
マウスイベントとの互換性
ポインタイベントのインターフェイスを使用すると、アプリはポインタ対応デバイスで高度なユーザーエクスペリエンスを作成できますが、現実での今日のウェブコンテンツの大部分はマウス入力でのみ機能するように設計されています。 その結果、ブラウザーがポインタイベントをサポートしていても、マウスのみの入力が直接変更せずにそのまま機能することを前提として、ブラウザーはマウスイベントを処理する必要があります。 理想的には、ポインタ対応アプリはマウス入力を明示的に処理する必要はありません。 ただし、ブラウザーはマウスイベントを処理する必要があるため、対処する必要がある互換性の問題がいくつかあるかもしれません。 このセクションでは、ポインタイベントとマウスイベントの相互作用、およびアプリ開発者への影響について説明します。
ブラウザーは、マウスベースのコンテンツとの互換性のために、汎用ポインタ入力をマウスイベントにマッピングすることができます。 このイベントのマッピングは、互換性マウスイベントと呼ばれます。 作成者は pointerdown
イベントをキャンセルすることで特定の互換性マウスイベントの生成を防ぐことができますが、次の点に注意してください。
- マウスイベントは、ポインタがダウンしているときにのみ防ぐことができます。
- ホバーしているポインタ(例えばボタンが押されていないマウス)は、それらのマウスイベントを防ぐことができません。
mouseover
、mouseout
、mouseenter
、mouseleave
の各イベントが防止されることはありません(ポインタがダウンしている場合でも)。
ベストプラクティス
ポインタイベントを使用する際に考慮すべき、いくつかのベストプラクティスは次のとおりです。
- イベントハンドラで行われる作業量を最小限に抑えます。
- イベントハンドラを特定のターゲット要素に追加します(ドキュメント全体またはドキュメントツリーの上位のノードではなく)。
- ターゲット要素(ノード)は、(通常は指で触れた)最大接触表面積を収容するのに十分な大きさであるべきです。 ターゲット領域が小さすぎる場合、それに触れると隣接する要素に対して他のイベントが発生する可能性があります。
実装と展開の状態
ポインタイベントのブラウザーの互換性のデータ(英語)は、デスクトップブラウザーとモバイルブラウザーの間のポインタイベントのサポートが、Safari を除くほぼすべてで利用可能であることを示しています。
Pointer Events Level 2 仕様の一部として、CSS の touch-action
プロパティに新しい値が提案されていますが、現在これらの新しい値には実装サポートがありません。
デモと例
- タッチ/ポインタのテストとデモ(Patrick H. Lauke 著、英語)
コミュニティ
- ポインタイベントのワーキンググループ(英語)
- メールリスト(英語)
- W3C の #pointerevents IRC チャンネル(英語)
関連トピックとリソース
- タッチイベント標準(英語)