Join MDN and developers like you at Mozilla's View Source conference, 12-14 September in Berlin, Germany. Learn more at https://viewsourceconf.org

Pointer Lock API

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"); 
}
注記: 上記のイベントは現在、Firefox で moz、Chrome で webkit の接頭辞を付加しています。

Mouse イベントの拡張

Pointer Lock API は通常の MouseEvent インターフェイスを、movement 属性で拡張します。

partial interface MouseEvent {
    readonly attribute long movementX;
    readonly attribute long movementY;
};
現在 movement 属性は Firefox で .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 のプロパティである clientXclientYscreenXscreenY は、マウスが動いていないかのように値が固定されます。movementX プロパティおよび movementY プロパティが、マウスの位置の変化を提供し続けます。マウスが一方向へ連続的に移動するとしても、movementX および movementY の値に制限はありません。マウスカーソルは存在せず、ウィンドウから外れたりスクリーンの端で止まったりしないという考え方です。

ロックが解除された状態

パラメータ movementX および movementY はマウスのロック状態にかかわらず有効であり、ロックされていない状態でも利便性のために使用できます。

マウスのロックが解除されると、システムカーソルが存在するようになり、ブラウザがウィンドウ表示に戻ります。このとき、movementX および movementY は 0 に設定されるかもしれません。

Pointer Lock の使用方法やシンプルな制御システムの設定方法を示すため、シンプルな Pointer Lock のデモを作成しました (ソースコードを確認する)。デモは以下のようなものです:

A red circle on top of a black background.

このデモでは、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
基本サポート 未サポート 未サポート 未サポート 未サポート 未サポート 未サポート

関連情報

ドキュメントのタグと貢献者

 このページの貢献者: yyss
 最終更新者: yyss,