Pointer Lock API (以前は Mouse Lock API と呼ばれていました) は、ビューポート内のマウスカーソルの絶対位置だけでなく、時間の経過に伴うマウスの動き (すなわち、デルタ) に基づく入力方法を提供します。これにより、マウスの動きそのものを知る、マウスイベントのターゲットをひとつの要素にロックする、マウスが一方向へどれだけ移動できるかの制限を除去する、視野からカーソルを取り除くことができます。これらは、本人視点の 3D ゲームなどで理想的です。
さらにこの API は、動きのコントロールやオブジェクトの回転、エントリの変更にかなりのマウス操作が必要になるアプリケーションで役立ちます。例えばなんらかのボタンをクリックすることなく、マウスを動かすだけで視野角を制御できます。ボタンは他の操作のために使用できます。また、地図や衛星画像を見るアプリでも役に立ちます。
Pointer Lock では、カーソルがブラウザやスクリーンの境界を通り過ぎるときでもマウスイベントにアクセスできます。例えばユーザは限りなくマウスを動かすことで、3D モデルの回転や操作を続けることができます。Pointer Lock がなければ、ポインタがブラウザまたはスクリーンの端に達したときに回転や操作が止まります。ゲームのプレイヤーはマウスカーソルがゲームのプレイ領域から外れて、別のアプリケーションを意図せずクリックしてゲームからマウスのフォーカスが外れてしまうことを心配せずに、熱中してボタンのクリックやマウスカーソルをあちこちに動かすことができるようになります。
基本概念
Pointer Lock は Mouse capture と関係があります。Mouse capture はマウスのドラッグ中にターゲットの要素へ継続的にイベントを提供しますが、マウスのボタンを離すとイベントが停止します。Pointer Lock は Mouse capture と以下の点が異なります:
- Pointer Lock は永続的です。明示的に API が呼び出されるかユーザが特定の解放ジェスチャを行うまで、マウスを解放しません。
- Pointer Lock はブラウザまたはスクリーンの境界に制限されません。
- Pointer Lock はマウスボタンの状態に関係なく、イベントが発生し続けます。
- Pointer Lock はカーソルを隠します。
メソッド/プロパティの概要
ここでは、Pointer Lock 仕様に関係するプロパティやメソッドを簡単に説明します。
requestPointerLock()
Pointer Lock API は Fullscreen API と同様に、新たなメソッド requestPointerLock
を追加することで DOM 要素を拡張しています。なお現在は、ベンダー接頭辞を付加しています。例えば canvas
要素で Pointer Lock を要求したい場合は、現在は以下のように宣言します:
canvas.requestPointerLock = canvas.requestPointerLock || canvas.mozRequestPointerLock || canvas.webkitRequestPointerLock; canvas.requestPointerLock()
pointerLockElement と exitPointerLock()
Pointer Lock API は Document
インターフェイスも拡張しており、新たなプロパティやメソッドを追加しています。新たなプロパティ pointerLockElement
は、現在ロックしている要素 (もしあれば) へアクセスするために使用します。なお、現在は接頭辞を付加しています。Document
の新たメソッド exitPointerLock
は、名前がほのめかすとおり Pointer Lock を終えるために使用します。
pointerLockElement
プロパティは要素が現在 Pointer Locked 状態であるかを判断する (例えば真偽チェックを行う) ために、またはロックされた要素があればその要素への参照を得るために有用です。
pointerLockElement
の使用例を示します:
if(document.pointerLockElement === canvas ||
document.mozPointerLockElement === canvas ||
document.webkitPointerLockElement === canvas) {
console.log('The pointer lock status is now locked');
} else {
console.log('The pointer lock status is now unlocked');
}
Document.exitPointerLock
メソッドは Pointer Lock を終えるために使用され、requestPointerLock
と同様に pointerlockchange
イベントや pointerlockerror
イベントを用いて非同期的に動作します。使用例は以下のとおりです。
document.exitPointerLock = document.exitPointerLock || document.mozExitPointerLock || document.webkitExitPointerLock; // ロック解除を試みる document.exitPointerLock();
pointerlockchange イベント
Pointer Lock の状態が変化したとき、例えば requestPointerLock
あるいは exitPointerLock
を呼び出したときや、ユーザが ESC キーを押下したときなどに、pointerlockchange
イベントが document
に発生します。これはシンプルなイベントであり、付加的なデータは含まれません。
if ("onpointerlockchange" in document) { document.addEventListener('pointerlockchange', lockChangeAlert, false); } else if ("onmozpointerlockchange" in document) { document.addEventListener('mozpointerlockchange', lockChangeAlert, false); } else if ("onwebkitpointerlockchange" in document) { document.addEventListener('webkitpointerlockchange', lockChangeAlert, false); } function lockChangeAlert() { if(document.pointerLockElement === canvas || document.mozPointerLockElement === canvas || document.webkitPointerLockElement === canvas) { console.log('The pointer lock status is now locked'); // Do something useful in response } else { console.log('The pointer lock status is now unlocked'); // Do something useful in response } }
pointerlockerror イベント
requestPointerLock
または exitPointerLock
の呼び出しによりエラーが発生したときは、pointerlockerror
イベントが document
に発生します。これはシンプルなイベントであり、付加的なデータは含まれません。
document.addEventListener('pointerlockerror', lockError, false); document.addEventListener('mozpointerlockerror', lockError, false); document.addEventListener('webkitpointerlockerror', lockError, false); function lockError(e) { alert("Pointer lock failed"); }
moz
、Chrome で webkit
の接頭辞を付加しています。Mouse イベントの拡張
Pointer Lock API は通常の MouseEvent
インターフェイスを、movement 属性で拡張します。
partial interface MouseEvent {
readonly attribute long movementX;
readonly attribute long movementY;
};
.mozMovementX
および .mozMovementY
、Chrome で .webkitMovementX
および .webkitMovementY
のように接頭辞が付加されています。mouse イベントの新たなパラメータである movementX
および movementY
は、マウスポインタの位置の変化を提供します。パラメータの値は MouseEvent
のプロパティである screenX
および screenY
の値同士の差と同じであり、それらのプロパティは 2 つ続いて発生する mousemove
イベント eNow
およびePrevious
に保存されます。言い換えると、Pointer Lock のパラメータ movementX
は、eNow.screenX - ePrevious.screenX
になります。
ロックされた状態
Pointer Lock が有効であるとき、標準 MouseEvent
のプロパティである clientX
、clientY
、screenX
、screenY
は、マウスが動いていないかのように値が固定されます。movementX
プロパティおよび movementY
プロパティが、マウスの位置の変化を提供し続けます。マウスが一方向へ連続的に移動するとしても、movementX
および movementY
の値に制限はありません。マウスカーソルは存在せず、ウィンドウから外れたりスクリーンの端で止まったりしないという考え方です。
ロックが解除された状態
パラメータ movementX
および movementY
はマウスのロック状態にかかわらず有効であり、ロックされていない状態でも利便性のために使用できます。
マウスのロックが解除されると、システムカーソルが存在するようになり、ブラウザがウィンドウ表示に戻ります。このとき、movementX
および movementY
は 0 に設定されるかもしれません。
例
Pointer Lock の使用方法やシンプルな制御システムの設定方法を示すため、シンプルな Pointer Lock のデモを作成しました (ソースコードを確認する)。デモは以下のようなものです:
このデモでは、JavaScript を使用して <canvas>
要素上にボールを描画します。canvas をクリックすると Pointer Lock がマウスポインタを取り除いて、マウスを使用してボールを直接動かすことができます。このデモの仕組みを見ていきましょう。
canvas 内の、x および y の初期位置を設定します:
var x = 50; var y = 50;
canvasDraw()
関数が、現在の x および y の位置にボールを描画します。ただし、ボールが canvas の端からはみ出すかをチェックする if()
文が含まれています。ボールがはみ出す場合は、反対側の端にボールを描画します。
function canvasDraw() { if(x > canvas.clientWidth+20) { x = 0; } if(y > canvas.clientHeight+20) { y = 0; } if(x < -20) { x = canvas.clientWidth; } if(y < -20) { y = canvas.clientHeight; } ctx.fillStyle = "black"; ctx.fillRect(0,0,canvas.clientWidth,canvas.clientHeight); ctx.fillStyle = "#f00"; ctx.beginPath(); ctx.arc(x,y,20,0,degToRad(360), true); ctx.fill(); }
現在は Pointer Lock のメソッドに接頭辞がついていますので、ブラウザ実装ごとに処理を分けています。
canvas.requestPointerLock = canvas.requestPointerLock || canvas.mozRequestPointerLock || canvas.webkitRequestPointerLock; // pointer lock object forking for cross browser document.exitPointerLock = document.exitPointerLock || document.mozExitPointerLock || document.webkitExitPointerLock; //document.exitPointerLock();
canvas がクリックされたときに、canvas で requestPointerLock() メソッドを実行するイベントリスナを設定します。これは、Pointer Lock を開始します。
canvas.onclick = function() { canvas.requestPointerLock(); }
Pointer Lock イベント pointerlockchange
のイベントリスナを設定します。イベントが発生したら、Pointer Lock の変更を制御するために lockChangeAlert()
という名前の関数を実行します。
// pointer lock event listener // Hook pointer lock state change events for different browsers document.addEventListener('pointerlockchange', lockChangeAlert, false); document.addEventListener('mozpointerlockchange', lockChangeAlert, false); document.addEventListener('webkitpointerlockchange', lockChangeAlert, false);
この関数は、pointLockElement プロパティが canvas を示しているかを確認します。示している場合は、マウスの移動を扱うためにイベントリスナへ canvasLoop()
関数を設定します。示していない場合は、イベントリスナを再び削除します。
function lockChangeAlert() { if(document.pointerLockElement === canvas || document.mozPointerLockElement === canvas || document.webkitPointerLockElement === canvas) { console.log('The pointer lock status is now locked'); document.addEventListener("mousemove", canvasLoop, false); } else { console.log('The pointer lock status is now unlocked'); document.removeEventListener("mousemove", canvasLoop, false); } }
参照用に、X および Y の位置を表示するための tracker を設定します。
var tracker = document.createElement('p'); var body = document.querySelector('body'); body.appendChild(tracker); tracker.style.position = 'absolute'; tracker.style.top = '0'; tracker.style.right = '10px'; tracker.style.backgroundColor = 'white';
canvasLoop()
関数は始めに、movementX
および movementY
プロパティの処理を分岐します。これは現在、接頭辞が付加されているブラウザが存在するためです。そして、これらのプロパティの値を x および y に加算して、新たな値で canvasDraw()
を実行します。これにより、ボールの位置が更新されます。最後に、処理をループするために requestAnimationFrame()
を使用します。
function canvasLoop(e) { var movementX = e.movementX || e.mozMovementX || e.webkitMovementX || 0; var movementY = e.movementY || e.mozMovementY || e.webkitMovementY || 0; x += movementX; y += movementY; canvasDraw(); var animation = requestAnimationFrame(canvasLoop); tracker.innerHTML = "X position: " + x + ', Y position: ' + y; }
iframe の制限
Pointer Lock は一度に 1 つだけの iframe をロックできます。iframe をひとつロックすると、別の iframe をロックしてターゲットを切り替えようとすることはできません。Pointer Lock はエラーになります。この制限を避けるため、始めにロックされた iframe のロックを解除してから別の iframe をロックしてください。
iframe の規定の動作では、"サンドボックス化された" iframe が Pointer Lock をブロックします。<iframe sandbox="allow-pointer-lock">
という属性/値の組み合わせによってこの制限を避けられることが、Chrome でまもなく可能になる予定です。
仕様
仕様書 | 策定状況 | コメント |
---|---|---|
Pointer Lock | 勧告候補 | 最初の仕様 |
ブラウザ実装状況
機能 | Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari (WebKit) |
---|---|---|---|---|---|
基本サポート | Yes webkit | Yes gecko | 未サポート | 未サポート | 未サポート |
機能 | Android | Firefox Mobile (Gecko) | Firefox OS | IE Phone | Opera Mobile | Safari Mobile |
---|---|---|---|---|---|---|
基本サポート | 未サポート | 未サポート | 未サポート | 未サポート | 未サポート | 未サポート |