WebVR API の使用 - Web API

WebVR API の使用
=============

**非推奨:** この機能は非推奨になりました。まだ対応しているブラウザーがあるかもしれませんが、すでに関連するウェブ標準から削除されているか、削除の手続き中であるか、互換性のためだけに残されている可能性があります。使用を避け、できれば既存のコードは更新してください。このページの下部にある互換性一覧表を見て判断してください。この機能は突然動作しなくなる可能性があることに注意してください。

**メモ:** WebVR API は WebXR API に置き換えられました。 WebVR は標準として批准されることはなく、ごく少数のブラウザーでしか既定で実装・有効化されず、少数の端末しか対応していませんでした。

WebVR API はウェブ開発者のツールキットへのすばらしい追加機能で、 Oculus Rift のようなバーチャルリアリティハードウェアへのアクセスが可能となります。そして出力された動きや向きはウェブアプリの描画更新に変換されます。しかし VR アプリを開発はどのようにやればいいのでしょうか？ この記事では、それに関する基礎的な解説を行います。

始めましょう
------

始めるには、次のものが必要です。

* 対応している VR ハードウェア
    + 最も安価なオプションは、モバイル端末、対応しているブラウザー、および機器マウント（例えば Google Cardboard）を使用することです。これは、専用のハードウェアほど良い使い勝手ではありませんが、強力なコンピューターや専用の VR ディスプレイを購入する必要はありません。
    + 専用のハードウェアはコストがかかりますが、より良い使い勝手を提供することができます。現時点で最も WebVR と互換性のあるハードウェアは、 HTC VIVE と Oculus Rift です。 webvr.info のトップページには、利用できるハードウェアや対応しているブラウザーについて、さらに有益な情報が掲載されています。
* 使用した場合、 VR 専用ハードウェアを使用して VR シーンのレンダリング/表示を処理するのに十分な性能のコンピューター。購入する VR の関連ガイド（例： VIVE READY コンピューター）を見ていただくと、必要なものがわかると思います。
* 対応しているブラウザーがインストールされていること - 最新のFirefox Nightly または Chrome が、デスクトップでもモバイルでも、正しい選択となります。

すべての組み立てが完了したら、 simple A-Frame demo にアクセスすると、シーンがレンダリングされるか、右下のボタンを押して VR 表示モードに入ることができるかどうかで、セットアップによって WebVR が正しく動作するかどうかをテストすることができます。

新しい JavaScript コードの束を理解する必要なく、WebVR 互換の 3D シーンをすばやく作成したい場合は、A-Frame が断然最適なオプションと言えます。しかし、これは生の WebVR API がどのように動作するのかを教えてはくれませんので、次に取りかかるのはこの点です。

簡単なデモ
-----

WebVR API がどのように動作するのかを説明するために、次のような raw-webgl-example を勉強してみましょう。

![灰色の回転する 3D 立方体](/ja/docs/Web/API/WebVR_API/Using_the_WebVR_API/capture1.png)

**メモ:** このデモのソースコード は GitHub で、ライブで見ることができます also.

**メモ:** ブラウザーで WebVR が動作しない場合、グラフィックカードを通して実行しているかどうかを確認する必要がある場合があります。例えば NVIDIA のカードの場合、 NVIDIA コントロールパネルが正常に設定されていれば、利用できるコンテキストメニューオプションがあります - Firefox を右クリックし、 *Run with graphics processor > High-performance NVIDIA processor* を選んでください。

このデモでは、WebGL のデモの聖杯である、回転する 3D 立方体を扱っています。私たちはこれを生の WebGL API コードを使用して実装しています。基本的な JavaScript や WebGL は一切教えず、WebVR の部分のみを教える予定です。

このデモでは、次のような機能も備えています。

* VR ディスプレイに存在するシーンを開始（および停止）するためのボタン。
* VR の正の値、すなわちヘッドセットの位置と方向をリアルタイムで更新して表示（および非表示）するボタン。

このデモのメインの JavaScript ファイルのソースコードを見ていくと、先行するコメントで "WebVR" という文字列を探せば、簡単に WebVR に特化した部分を見つけることができます。

**メモ:** 基本的な JavaScript と WebGL については、 JavaScript 学習素材、WebGL チュートリアルを参照してください。

アプリはどう動くか
---------

この点で、コードの WebVR 部分がどのように動作するのかを見ていきましょう。

典型的な（単純な） WebVR アプリはこのように作業します。

1. `Navigator.getVRDisplays()` を使用して、 VRディスプレイへの参照を取得します。
2. `VRDisplay.requestPresent()` を使用して VR ディスプレイへの表示を開始します。
3. WebVR 専用の `VRDisplay.requestAnimationFrame()` メソッドを使用して、ディスプレイの正しいリフレッシュレートでアプリのレンダリングループが実行されます。
4. レンダリングループ内では、現在のフレームを表示するために必要なデータを取得し（`VRDisplay.getFrameData()`）、表示シーンを両眼で 2 回描画し、レンダリングしたビューをディスプレイに送信してユーザに表示します (`VRDisplay.submitFrame()`)。

下記の節では、この raw-webgl-demo を詳しく見ていき、上記の機能が具体的にどこで使用されるかを見ていきます。

### いくつかの変数で始める

最初に出会う WebVR 関連のコードは、以下のブロックです。

```js
// WebVR 変数

const frameData = new VRFrameData();
let vrDisplay;
const btn = document.querySelector(".stop-start");
let normalSceneFrame;
let vrSceneFrame;

const poseStatsBtn = document.querySelector(".pose-stats");
const poseStatsSection = document.querySelector("section");
poseStatsSection.style.visibility = "hidden"; // hide it initially

const posStats = document.querySelector(".pos");
const orientStats = document.querySelector(".orient");
const linVelStats = document.querySelector(".lin-vel");
const linAccStats = document.querySelector(".lin-acc");
const angVelStats = document.querySelector(".ang-vel");
const angAccStats = document.querySelector(".ang-acc");
let poseStatsDisplayed = false;
```

これらについて簡単に説明します。

* `frameData` には `VRFrameData` オブジェクトを入れ、これは `VRFrameData()` コンストラクターで作成します。これは初期状態では空ですが、後に VR ディスプレイに表示する各フレームのレンダリングに必要なデータを格納し、レンダリングループの実行に合わせて常に更新されます。
* `vrDisplay` は初期化されていませんが、後に VR ヘッドセット（`VRDisplay` — API の中心的な制御オブジェクト）への参照を保持するようになります。
* `btn` と `poseStatsBtn` には、アプリを制御するために使用する 2 つのボタンへの参照が格納されています。
* `normalSceneFrame` と `vrSceneFrame` は初期化されずに始まりますが、後で `Window.requestAnimationFrame()` と `VRDisplay.requestAnimationFrame()` の呼び出しへの参照を格納します。これらは通常のレンダリングループと特別な WebVR レンダリングループを実行するようになりますが、後でこの 2 つの違いを説明することになります。
* 他にも、UI の右下に表示される VR ポーズデータの表示ボックスの異なる形への参照を格納する変数があります。
### VR ディスプレイへの参照の取得

コード内の主要な関数の一つは `start()` で、本体の読み込みが完了したときにこの関数を実行しています。

```js
// start
//
// 本体が読み込まれたときに呼び出され、ボールを取得するために作成されます。

document.body.onload = start;
```

まず始めに、`start()` は WebGL コンテキストを取得して、3D グラフィックを HTML の `<canvas>` 要素にレンダリングするために使用します。次に、`gl` コンテキストが利用できるかどうかを調べます。利用できる場合は、表示するシーンを設定するためにいくつかの関数を実行します。

```js
function start() {
  canvas = document.getElementById("glcanvas");

  initWebGL(canvas);      // Initialize the GL context

  // WebGL setup code here
```

次に、キャンバスをブラウザーのビューポートいっぱいに設定し、レンダリングループ (`drawScene()`) を最初に実行して、シーンをキャンバスに実際にレンダリングする処理を始めます。これは WebVR ではない、通常のレンダリングループです。

```js
// draw the scene normally, without WebVR - for those who don't have it and want to see the scene in their browser

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
drawScene();
```

これで最初の WebVR 固有のコードに入ります。まず最初に、 `Navigator.getVRDisplays` が存在するかどうかを調べます - これは API へのエントリーポイントであり、したがって WebVR の基本的な機能を適切に検出することができます。ブロックの最後（`else` 句の中）に、これが存在しない場合、 WebVR 1.1 がブラウザーで対応していないことを示すメッセージをログ出力していることがわかります。

```js
  // WebVR: Check to see if WebVR is supported
  if (navigator.getVRDisplays) {
    console.log('WebVR 1.1 supported');
```

`if () { }` ブロックの中で、 `Navigator.getVRDisplays()` という関数を実行しています。この関数は、コンピューターに接続されているすべての VR ディスプレイ機器を格納した配列で履行されるプロミスを返します。 1 台も接続されていない場合は、配列は空になります。

```js
    // Then get the displays attached to the computer
    navigator.getVRDisplays().then((displays) => {
```

プロミスの `then()` ブロックの中で、配列の長さが 0 以上かどうかを調べます。0 以上であれば、変数 `vrDisplay` の値を配列の 0 番目のインデックスに集合させます。これで `vrDisplay` には、接続されたディスプレイを表す `VRDisplay` オブジェクトが格納されました。

```js
      // If a display is available, use it to present the scene
      if (displays.length > 0) {
        vrDisplay = displays[0];
        console.log('Display found');
```

**メモ:** コンピューターに複数の VR ディスプレイを保有することはまずないでしょうし、このデモでは単純なものなので、とりあえずはこれで大丈夫でしょう。

### VR 表示の開始と停止

これで `VRDisplay` オブジェクトを取得し、このオブジェクトを使用してさまざまなことを行うことができます。次に行うことは、ディスプレイへの WebGL コンテンツの表示を始めたり、止めたりするための機能を設定することです。

前のコードブロックに引き続き、今度は開始/停止ボタン (`btn`) にイベントリスナーを追加します。このボタンがクリックされたときに、ディスプレイにすでに表示されているかどうかを確認します（これはかなり間抜けな方法で、ボタン `textContent` が何を格納しているかを調べることによって行われます）。

ディスプレイがまだ表示されていない場合、 `VRDisplay.requestPresent()` メソッドを使用して、ブラウザーがディスプレイへのコンテンツの表示を始めるようにリクエストします。これは、引数として、ディスプレイに表示したいレイヤーを表す `VRLayerInit` オブジェクトの配列を取ります。

現在、表示できるレイヤーの最大数は 1 で、必要なオブジェクトのメンバーは `VRLayerInit.source` プロパティ(これは、そのレイヤーで表示したい `<canvas>` への参照です。他の引数は、感覚的な既定値として与えられています - `leftBounds` および `rightBounds`)) で、引数は [{ source: canvas }] になっています。］

`requestPresent()` は表示が正常に始まったときに履行されるプロミスを返します。

```js
        // Starting the presentation when the button is clicked: It can only be called in response to a user gesture
        btn.addEventListener('click', () => {
          if (btn.textContent === 'Start VR display') {
            vrDisplay.requestPresent([{ source: canvas }]).then(() => {
              console.log('Presenting to WebVR display');
```

表示リクエストが成功したので、今度は VRDisplay に表示しているコンテンツをレンダリングするための設定を始めたいと思います。最初の設定として、キャンバスで VR ディスプレイと同じ大きさに設定します。これは、`VRDisplay.getEyeParameters()` を使用して両目の `VREyeParameters` を取得することによって行われます。

次に、単純な計算を行って、目の `VREyeParameters.renderWidth` と `VREyeParameters.renderHeight` に基づいて VRDisplay 描画領域の合計幅を計算します。

```js
// Set the canvas size to the size of the vrDisplay viewport

const leftEye = vrDisplay.getEyeParameters("left");
const rightEye = vrDisplay.getEyeParameters("right");

canvas.width = Math.max(leftEye.renderWidth, rightEye.renderWidth) \* 2;
canvas.height = Math.max(leftEye.renderHeight, rightEye.renderHeight);
```

次に、前回 `drawScene()` 関数内の `Window.requestAnimationFrame()` 呼び出しによって設定されたアニメーションのループをキャンセルし、代わりに `drawVRScene()` を呼び出すことにします。この関数は前と同じシーンをレンダリングしますが、WebVR の特別なマジックが行われます。ここでのループは WebVR の特別な `VRDisplay.requestAnimationFrame` メソッドによって維持されています。

```js
// stop the normal presentation, and start the vr presentation
window.cancelAnimationFrame(normalSceneFrame);
drawVRScene();
```

最後に、ボタンテキストを更新し、次にボタンが押された時刻に VR ディスプレイへの表示を停止するようにします。

```js
              btn.textContent = 'Exit VR display';
            });
```

続いてボタンが押されたときに VR 表示を停止するために、 `VRDisplay.exitPresent()` を呼び出しています。また、ボタンのテキストコンテンツを反転させ、 `requestAnimationFrame` の呼び出しを入れ替えました。ここで、 `VRDisplay.cancelAnimationFrame` を使用して VR レンダリングのループを停止し、 `drawScene()` を使用して通常のレンダリングループを再び開始していることが分かります。

```js
          } else {
            vrDisplay.exitPresent();
            console.log('Stopped presenting to WebVR display');

            btn.textContent = 'Start VR display';

            // Stop the VR presentation, and start the normal presentation
            vrDisplay.cancelAnimationFrame(vrSceneFrame);
            drawScene();
          }
        });
      }
    });
  } else {
    console.log('WebVR API not supported by this browser.');
  }
}
```

プレゼンテーションを始めるには、ブラウザーに表示される立体視を確認します。

![3D 立方体の立体視画像](/ja/docs/Web/API/WebVR_API/Using_the_WebVR_API/capture2.png)

実際にどのように立体視が行われるかは下記をご覧ください。

### なぜ WebVR は独自の requestAnimationFrame() を持つのか

これは良い質問です。なぜなら、 VR ディスプレイ内でスムーズなレンダリングを行うには、コンピューターのリフレッシュレートではなく、ディスプレイのネイティブリフレッシュレートでコンテンツをレンダリングする必要があるからです。 VR ディスプレイのリフレッシュレートは PC のリフレッシュレートよりも大きく、通常は最大で 90fps です。このレートは、コンピューターのコアリフレッシュレートとは異なる形になります。

VR ディスプレイが表示されていないときは、`VRDisplay.requestAnimationFrame` は `Window.requestAnimationFrame` と同じように動作するので、必要に応じて、このアプリで使用している 2 つのレンダリング ループではなく、1 つのレンダリング ループだけを使用できることに留意してください。2 つ使用したのは、VR ディスプレイが存在するかどうかに応じて異なることを行い、理解しやすいように物事を別個のものにしたかったからです。

### レンダリングと表示

この点で、 VR ハードウェアにアクセスし、ハードウェアにシーンを表示するようリクエストし、レンダリング ループを実行し始めるために必要なすべてのコードを見てきました。これで、レンダリング ループのコードを見て、 WebVR 固有の部分がどのように動作するのかを説明します。

まずすべて、レンダリングループ関数である `drawVRScene()` の定義から始めます。この内部で最初に行うことは `VRDisplay.requestAnimationFrame()` を呼び出して、ループが一度呼ばれた後（これはコードの前半で VR ディスプレイに表示を開始したときに発生します）、ループを実行し続けるようにすることです。この呼び出しをグローバル変数 `vrSceneFrame` の値として設定し、VR プレゼンテーションを終了したら `VRDisplay.cancelAnimationFrame()` の呼び出しでループをキャンセルできるようにしています。

```js
function drawVRScene() {
  // WebVR: Request the next frame of the animation
  vrSceneFrame = vrDisplay.requestAnimationFrame(drawVRScene);
```

次に `VRDisplay.getFrameData()` を呼び出して、フレームデータを格納するために使用したい変数名を渡します。先ほど、`frameData`という変数で初期化しました。呼び出された後、この変数には次のフレームを VR 機器にレンダリングするために必要なデータが `VRFrameData` オブジェクトとしてパッケージングされて格納されます。これには、左目用と右目用のシーンを正しくレンダリングするための投影およびビューマトリックス、そして方向や位置などVRディスプレイのデータを格納する現在の `VRPose` オブジェクトといったものが含まれています。

これは、レンダリングされたビューが常に最新の状態になるように、フレームごとに呼び出される必要があります。

```js
// Populate frameData with the data of the next frame to display
vrDisplay.getFrameData(frameData);
```

これで、`VRPose` プロパティから現在の `VRFrameData.pose` を取得し、後で使用するために位置と方向を格納し、変数 `poseStatsDisplayed` が true の場合は現在の pose を pose stats ボックスに送って表示することができました。

```js
// You can get the position, orientation, etc. of the display from the current frame's pose

const curFramePose = frameData.pose;
const curPos = curFramePose.position;
const curOrient = curFramePose.orientation;
if (poseStatsDisplayed) {
  displayPoseStats(curFramePose);
}
```

これで、キャンバスには描画を始める前にクリアされ、次のフレームがはっきりと見えるようになり、前回のレンダリングフレームも見えなくなりました。

```js
// Clear the canvas before we start drawing on it.

gl.clear(gl.COLOR\_BUFFER\_BIT | gl.DEPTH\_BUFFER\_BIT);
```

これで、左目と右目の両方のビューをレンダリングすることができます。最初に、レンダリングに使用する投影位置と表示位置を作成する必要があります。これらは `WebGLUniformLocation` (en-US) オブジェクトで、 `WebGLRenderingContext.getUniformLocation()` (en-US) メソッドを使用して作成し、引数としてシェーダープログラムの識別子と識別名 を渡します。

```js
// WebVR: Create the required projection and view matrix locations needed
// for passing into the uniformMatrix4fv methods below

const projectionMatrixLocation = gl.getUniformLocation(
  shaderProgram,
  "projMatrix",
);
const viewMatrixLocation = gl.getUniformLocation(shaderProgram, "viewMatrix");
```

次のレンダリング手順では、次のことを行います。

* 左目のビューポートサイズを `WebGLRenderingContext.viewport` (en-US) で指定します - これは論理的にはキャンバス幅の最初の半分と、キャンバス高さいっぱいになります。
* 左目のレンダリングに使用するビューおよび投影行列の値を指定します - これは `WebGLRenderingContext.uniformMatrix4fv` メソッドで行います。このメソッドには、上で取得した位置情報と `VRFrameData` オブジェクトで得た左行列の値が渡され、使用します。
* 実際のシーンをレンダリングする `drawGeometry()` 関数を実行します。前回の2つの手順で指定した内容から、左目用にのみレンダリングすることになります。

```js
// WebVR: Render the left eye's view to the left half of the canvas
gl.viewport(0, 0, canvas.width \* 0.5, canvas.height);
gl.uniformMatrix4fv(
  projectionMatrixLocation,
  false,
  frameData.leftProjectionMatrix,
);
gl.uniformMatrix4fv(viewMatrixLocation, false, frameData.leftViewMatrix);
drawGeometry();
```

これで、まったく同じことを右目で行います。

```js
// WebVR: Render the right eye's view to the right half of the canvas
gl.viewport(canvas.width \* 0.5, 0, canvas.width \* 0.5, canvas.height);
gl.uniformMatrix4fv(
  projectionMatrixLocation,
  false,
  frameData.rightProjectionMatrix,
);
gl.uniformMatrix4fv(viewMatrixLocation, false, frameData.rightViewMatrix);
drawGeometry();
```

次に、 `drawGeometry()` 関数を定義します。この関数のほとんどは、 3D 立方体を描画するために必要な一般的な WebGL コードです。 `mvTranslate()` と `mvRotate()` 関数呼び出しに WebVR 固有の部分があります。これらは、現在のフレームにおける立方体の移動と回転を定義する行列を WebGL プログラムに渡すものです。

これらの値を `VRPose` オブジェクトから取得した VR ディスプレイの位置 (`curPos`) と方向 (`curOrient`) によって変更していることがわかると思います。その結果、例えば頭を左に動かしたり回転させたりすると、x 位置値 (`curPos[0]`) と y 回転値 (`[curOrient[1]`) が x 移動値に追加されます。つまり、何かを見ているときに頭を左に移動/回転させると、キューブは右に移動することを意味します。

これは、VR ポーズデータを使用するためのすばやく汚い方法ですが、基本的な原理を説明しています。

```js
function drawGeometry() {
  // Establish the perspective with which we want to view the
  // scene. Our field of view is 45 degrees, with a width/height
  // ratio of 640:480, and we only want to see objects between 0.1 units
  // and 100 units away from the camera.
  perspectiveMatrix = makePerspective(45, 640.0 / 480.0, 0.1, 100.0);

  // Set the drawing position to the "identity" point, which is
  // the center of the scene.
  loadIdentity();

  // Now move the drawing position a bit to where we want to start
  // drawing the cube.
  mvTranslate([
    0.0 - curPos[0] \* 25 + curOrient[1] \* 25,
    5.0 - curPos[1] \* 25 - curOrient[0] \* 25,
    -15.0 - curPos[2] \* 25,
  ]);

  // Save the current matrix, then rotate before we draw.
  mvPushMatrix();
  mvRotate(cubeRotation, [0.25, 0, 0.25 - curOrient[2] \* 0.5]);

  // Draw the cube by binding the array buffer to the cube's vertices
  // array, setting attributes, and pushing it to GL.
  gl.bindBuffer(gl.ARRAY\_BUFFER, cubeVerticesBuffer);
  gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);

  // Set the texture coordinates attribute for the vertices.
  gl.bindBuffer(gl.ARRAY\_BUFFER, cubeVerticesTextureCoordBuffer);
  gl.vertexAttribPointer(textureCoordAttribute, 2, gl.FLOAT, false, 0, 0);

  // Specify the texture to map onto the faces.
  gl.activeTexture(gl.TEXTURE0);
  gl.bindTexture(gl.TEXTURE\_2D, cubeTexture);
  gl.uniform1i(gl.getUniformLocation(shaderProgram, "uSampler"), 0);

  // Draw the cube.
  gl.bindBuffer(gl.ELEMENT\_ARRAY\_BUFFER, cubeVerticesIndexBuffer);
  setMatrixUniforms();
  gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED\_SHORT, 0);

  // Restore the original matrix
  mvPopMatrix();
}
```

次のコードは WebVR とは何の関係もなく、各フレームで立方体の回転を更新するだけです。

```js
// Update the rotation for the next draw, if it's time to do so.
let currentTime = new Date().getTime();
if (lastCubeUpdateTime) {
  const delta = currentTime - lastCubeUpdateTime;

  cubeRotation += (30 \* delta) / 1000.0;
}
lastCubeUpdateTime = currentTime;
```

レンダリングループの最後の部分で `VRDisplay.submitFrame()` を呼び出します。このメソッドでは、すべての作業が完了し、 `<canvas>` に表示がレンダリングされると、フレームを VR ディスプレイに送信して、そこに表示することができます。

```js
  // WebVR: Indicate that we are ready to present the rendered frame to the VR display
  vrDisplay.submitFrame();
}
```

### ポーズ（位置、向きなど）データの表示

この節では、各フレームで更新されたポーズデータを表示する `displayPoseStats()` 関数について説明します。この関数はかなり単純です。

すべて `VRPose` オブジェクトから得られる 6 つの異なる形のプロパティ値を自分自身で変数に格納します。それぞれ `Float32Array` とします。

```js
function displayPoseStats(pose) {
  const pos = pose.position;
  const orient = pose.orientation;
  const linVel = pose.linearVelocity;
  const linAcc = pose.linearAcceleration;
  const angVel = pose.angularVelocity;
  const angAcc = pose.angularAcceleration;
```

そして、そのデータを情報ボックスに書き出し、フレームごとに更新しています。そうしないと値が読みにくいので、 `toFixed()` で各値を小数点以下 3 桁に固定しています。

データを表示する前に、線形加速度と角加速度の配列が正常に返されたかどうかを検出するために、条件式を使用していることにメモしておいてください。これらの値はまだほとんどの VR ハードウェアで報告されないので、これを行わないとコードがエラーを発生します（配列は正常に報告されないと `null` を返します）。

```js
  posStats.textContent = `Position: ` +
    `x ${pos[0].toFixed(3)}, ` +
    `y ${pos[1].toFixed(3)}, ` +
    `z ${pos[2].toFixed(3)}`;
  orientStats.textContent = `Orientation: ` +
    `x ${orient[0].toFixed(3)}, ` +
    `y ${orient[1].toFixed(3)}, ` +
    `z ${orient[2].toFixed(3)}`;
  linVelStats.textContent = `Linear velocity: ` +
    `x ${linVel[0].toFixed(3)}, ` +
    `y ${linVel[1].toFixed(3)}, ` +
    `z ${linVel[2].toFixed(3)}`;
  angVelStats.textContent = `Angular velocity: ` +
    `x ${angVel[0].toFixed(3)}, ` +
    `y ${angVel[1].toFixed(3)}, ` +
    `z ${angVel[2].toFixed(3)}`;

  if (linAcc) {
    linAccStats.textContent = `Linear acceleration: ` +
      `x ${linAcc[0].toFixed(3)}, ` +
      `y ${linAcc[1].toFixed(3)}, ` +
      `z ${linAcc[2].toFixed(3)}`;
  } else {
    linAccStats.textContent = 'Linear acceleration not reported';
  }

  if (angAcc) {
    angAccStats.textContent = `Angular acceleration: ` +
    `x ${angAcc[0].toFixed(3)}, ` +
    `y ${angAcc[1].toFixed(3)}, ` +
    `z ${angAcc[2].toFixed(3)}`;
  } else {
    angAccStats.textContent = 'Angular acceleration not reported';
  }
}
```

WebVR イベント
----------

WebVR の仕様では、 VR ディスプレイの状態の変化にアプリのコードが反応できるように、いくつかのイベントが発行される機能があります (Window イベント を参照してください)。例として、

* `vrdisplaypresentchange` - VR ディスプレイの表示状態が変化したとき、つまり表示から非表示、または非表示から表示へと変化したときに発行されま す。
* `vrdisplayconnect` - 対応する VR ディスプレイがコンピューターに接続されたときに発行されます。
* `vrdisplaydisconnect` - 対応する VR ディスプレイがコンピュータから切断されたときに発行されます。

これらがどのように動作するのかを示すために、この単純なデモでは以下のような例を記載しています。

```js
window.addEventListener("vrdisplaypresentchange", (e) => {
  console.log(
    `Display ${e.display.displayId} presentation has changed. Reason given: ${e.reason}.`,
  );
});
```

このプロパティには、イベントが発行された `VRDisplay` への参照が格納され、イベントが発生した理由を人間が読み取り可能な値で示します。

これはとても有益なイベントです。ディスプレイが予期せず切断された場合を処理するために使用することができ、エラーが発生するのを阻止し、ユーザーに状況を認識させることができます。 Google の Webvr.info プレゼンテーションのデモでは、このイベントを使用して `onVRPresentChange()` function が実行され、UI コントロールが適宜更新されてキャンバスのサイズが変更されます。

まとめ
---

この記事では、単純な WebVR 1.1 アプリを作成する方法について、取得するのに役立つ、ごく基本的なことを説明しました。


MediaRecorderErrorEvent.error - Web API

MediaRecorderErrorEvent.error
=============================

**非推奨:** この機能は非推奨になりました。まだ対応しているブラウザーがあるかもしれませんが、すでに関連するウェブ標準から削除されているか、削除の手続き中であるか、互換性のためだけに残されている可能性があります。使用を避け、できれば既存のコードは更新してください。このページの下部にある互換性一覧表を見て判断してください。この機能は突然動作しなくなる可能性があることに注意してください。

**非標準:** この機能は標準ではなく、標準化の予定もありません。公開されているウェブサイトには使用しないでください。ユーザーによっては使用できないことがあります。実装ごとに大きな差があることもあり、将来は振る舞いが変わるかもしれません。

`error` は **`MediaRecorderErrorEvent`** インタフェースの読み取り専用のプロパティで、`MediaRecorder` インスタンスによって発生した例外に関する詳細を提供する `DOMException` オブジェクトです。

`MediaRecorderErrorEvent` が発生した場合、`MediaRecorder` の `error` イベントハンドラーである `onerror` によって受けた `MediaRecorderErrorEvent` 内の `error` プロパティを調べることによって、何が問題であるかをある程度判断できます。

### 値

イベントによって表されるエラーを記述する `DOMException`。 エラーの `name` プロパティの値は、仕様で明確に識別されているものも含め、メディア収録の処理中に意味をなすものであればどんな例外でもかまいません。 ここでの説明は一般的なものです。 あなたはそれらが対応するメソッドのリファレンスで起こるかもしれない様々なシナリオにもっと具体的なものを見つけるでしょう。

`InvalidStateError`

操作が許可されていないコンテキストで操作が試行されたか、削除されたか取り除かれたオブジェクトに対して要求が行われました。

`NotSupportedError`

指定されたオプションが無効なため、`MediaRecorder` を作成できませんでした。 `message` 属性は、もしあれば追加情報を提供するべきです。

`SecurityError`

`MediaStream` は収録を禁止するように設定されています。 これは、例えば、ユーザーが入力デバイスを使用するパーミッションを拒否したときに `getUserMedia()` を使用して取得されたソースの場合などです。

`InvalidModificationError`

収録中のストリームのトラック数が変更されました。メディアの収録中にトラックを追加したり削除したりすることはできません。

`UnknownError`

 セキュリティに関連しない、他に分類できないエラーが発生しました。
 収録が停止し、`MediaRecorder` の `state` は `inactive` となり、最後に残りの受信データで `dataavailable` イベントが `MediaRecorder` に送られ、最後に `stop` イベントが送出されました。

例
-

### 基本的なエラー処理の例

この関数は、与えられた `MediaStream` の `MediaRecorder` を作成して、データを配列にバッファし、エラーを監視するように構成して、返します。

```js
function recordStream(stream) {
  let recorder = null;
  let bufferList = [];

  try {
    recorder = new MediaRecorder(stream);
  } catch (err) {
    /\* レコーダーを作成しようとしたときの例外。 それを扱う \*/
  }

  recorder.ondataavailable = (event) => {
    bufferList.push(event.data);
  };

  recorder.onerror = (event) => {
    console.error(`Error: ${event.error}`);
  };

  recorder.start(100); /\* バッファあたり 100ms のタイムスライス \*/
  return recorder;
}
```

仕様
--

#### No specification found

No specification data found for `api.MediaRecorderErrorEvent.error`.
Check for problems with this page or contribute a missing `spec_url` to mdn/browser-compat-data. Also make sure the specification is included in w3c/browser-specs.

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* MediaStream 収録 API
* MediaStream 収録 API の使用


MediaRecorderErrorEvent() - Web API

MediaRecorderErrorEvent()
=========================

**非推奨:** この機能は非推奨になりました。まだ対応しているブラウザーがあるかもしれませんが、すでに関連するウェブ標準から削除されているか、削除の手続き中であるか、互換性のためだけに残されている可能性があります。使用を避け、できれば既存のコードは更新してください。このページの下部にある互換性一覧表を見て判断してください。この機能は突然動作しなくなる可能性があることに注意してください。

**非標準:** この機能は標準ではなく、標準化の予定もありません。公開されているウェブサイトには使用しないでください。ユーザーによっては使用できないことがあります。実装ごとに大きな差があることもあり、将来は振る舞いが変わるかもしれません。

**`MediaRecorderErrorEvent()`** コンストラクターは、新しい `MediaRecorderErrorEvent` オブジェクトを作成します。これは、MediaStream 収録 API によるメディアの記録中に発生したエラーを表します。

**メモ:** 一般に、これらを自分で作成することはありません。 メディアの記録中にエラーが発生すると、 `onerror` の実装に配信されます。

構文
--

```js
new MediaRecorderErrorEvent(type, options)
```

### 引数

`type`

 文字列で、イベントの名前です。
 大文字小文字を区別し、ブラウザーは常に `error` を設定します。

`options`

オブジェクトで、*`Event()` で定義されているプロパティに加えて*、以下のプロパティがあります。

`error`

発生したエラーを説明する `DOMException`。 このオブジェクトの `name` プロパティは、発生したエラーの名前を示すべきです。 他のフィールドは存在する場合と存在しない場合があります。

**メモ:** 一部のユーザーエージェントは、スタックダンプ、JavaScript ファイルの名前、エラーが発生した行番号、その他のデバッグツールなどの情報を提供するその他のプロパティを `error` オブジェクトに追加しますが、本番環境ではこの情報に頼るべきではありません。

### 返値

新しい `MediaRecorderErrorEvent` オブジェクトです。

仕様
--

#### No specification found

No specification data found for `api.MediaRecorderErrorEvent.MediaRecorderErrorEvent`.
Check for problems with this page or contribute a missing `spec_url` to mdn/browser-compat-data. Also make sure the specification is included in w3c/browser-specs.

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


ウェブプッシュ API 通知のベストプラクティス - Web API

ウェブプッシュ API 通知のベストプラクティス
========================

この記事では、ユーザーエンゲージメントのためにプッシュ通知を使用するウェブサイトやアプリケーションを開発する際に留意すべき最善の手法について有益な概要を提供します。

>
> 「うまくやれば、あるに越したことはないが、うまくやらないと実に迷惑だ。」 — ブラウザー開発者2人がプッシュ通知の倫理について議論しているときの会話。
>
>
>

ウェブプッシュ通知の概要
------------

ウェブプッシュ通知（通知、プッシュ、サービスワーカー APIの組み合わせを使用して作成される）は、製品開発者やマーケティング担当者が自分のサイトの注目を取得するために使用しているノイズの一部です。「ウェブプッシュ通知」でウェブを検索すると、プッシュ通知を使用して、ウェブサイトから離脱した人々が購入を完了したり、最新のニュースを送ったり、おすすめの商品へのリンクを受け取ったりできるように再エンゲージするべきだと考えるマーケティングの専門家の記事を見つけることができます。

### 暗黒面

その斬新さは、進取の気性に富むサイトが潜在顧客にリーチするための新しい未開拓の機会を提供します。顧客はメールの返信のためにタブを切り替えていませんか？期限つきの送料無料キャンペーンで顧客を取り戻しましょう！

しかし実に、これがプッシュ通知の最良の使用方法なのでしょうか？それとも、古くて使い古されたポップアップ広告の新しい反復なのでしょうか？

>
> 「ウェブプッシュはスパムフォルダー内に終わる心配がありません。広告ブロッカーにブロックされることもありません。ウェブサイトを閉じていてもデスクトップに正しく表示されます。モバイルでは、ブラウザーが実行されていなくても、アプリのプッシュ通知のように通知トレイに表示させます。" - 無名のマーケティングサイト
>
>
>

### プッシュ通知の正の利用方法

しかし、プッシュ通知には明るく有益な面もあります。例えば、あなたのチームで通常チャットプログラムを使用してコミュニケーションをとっているとします。プロジェクトマネージャーが承認に問題を見つけ、作業を進める前にあなたの意見が聞きたいとします。

あなたに注意を促すために色々やってみた後、彼らがあなたにメールを送ると、使っているウェブメールアプリが開いていなくても、メールアプリがプッシュ通知を生成すれば、ウェブのメールアプリが開いていなくても注意を引くことに成功します。

この文書では、ウェブプッシュ通知の倫理的使用について説明します。プッシュ通知は、イライラや煩わしさを解消してくれることもあれば、発生させてしまうこともあります。プッシュ通知を使用する際に賢明な提案（と判断）をできるかどうかは、開発者であるあなた次第です。

このプッシュ通知で何を期待しますか？
------------------

何事もそうですが、大きな力には大きな責任が伴います。すべてのプッシュ通知は有益で時刻に敏感なものであるべきであり、ユーザーには最初のプッシュ通知を送信する前に必ずその許可を求め、今後プッシュ通知を取得しないようにする簡単な方法を提供する必要があります。

プッシュ通知が必要かどうかを判断するには、基本的な質問に答える必要があります。

* リアルタイムでレスポンスを待っている人がいますか？上記の例では、プログラムマネージャーがあなたのレスポンスを待っているので、プッシュ通知が適切です。
* 最新情報の更新は必要でしょうか？私は様々なソーシャルメディアのニュースソースを集約するサービスワーカースクリプトを使用しています。興味深いストーリーがトレンドになったら、通知を取得したいです！
* タイムリーな新しいニュースはありますか？ここが少し難しいところです。新しいニュースサイトが「見て見て！」とばかりにプッシュ通知をリクエストすることがあります。すべてはユーザーが何を望んでいるかで決まりますが、行動を用いて意図を判断することができます。例えば、ユーザーが複数の記事を閲覧していたり、ページに数分間滞在している場合、更新情報を受け取ることに関心がある可能性があります。

プッシュ通知がすべて必要かどうかという問題に加えて、プッシュ通知には、さりげなく消えるものから持続的に対話を求めるものまで、さまざまな種類があります。

対話を必要とするものは、とても迷惑になる可能性があるため、控えめに使用するように注意してください。通知は邪魔なものではなく、支援するものであるべきです。

信頼の構築
-----

ある調査によると、プッシュ通知の60％がブロックされていることが示されています。リアルタイムでプッシュ通知を許可するには、信頼が必要です。ユーザーへの敬意を示す良いコンテンツを提供し、プッシュ通知を受け入れることに明確な値を示す、優れたデザインのウェブサイトを持つことで信頼を築くことができます。

ブラウザーによる対策
----------

過去にプッシュ通知の悪用があったため、ウェブブラウザー開発者はこの問題を改善するための戦略を実装し始めています。例えば、 Safari 12.1 では、ページがプッシュ通知の実行権限をリクエストする前に、ユーザーが何らかの方法でページを操作することが要求されるようになっています。これにより、少なくとも、ユーザーは繰り返しになりますが、ほとんど見ていないウェブページで、自発的にこの質問をされることを防ぐことができます。

Firefox の場合、 Firefox バグ 1524619 を参照してください。 Firefox 68 では、環境設定 `dom.webnotifications.requireuserinteraction` に隠され、既定では無効ですが実装していることがわかります。

関連情報
----

* 通知 API
* 通知 API の使用
* プッシュ API


XRReferenceSpace: reset イベント - Web API

XRReferenceSpace: reset イベント
============================

**安全なコンテキスト用:** この機能は一部またはすべての対応しているブラウザーにおいて、安全なコンテキスト (HTTPS) でのみ利用できます。

ネイティブの原点または実際の原点のいずれかで不連続が検出されると、**`reset`** イベントが `XRReferenceSpace` オブジェクトに送信され、参照空間を使用して方向付けられたオブジェクトの位置または方向がジャンプします。 これは、ユーザーが XR デバイスを較正または再較正する場合、またはデバイスがユーザーの追跡を失った後に原点を自動的に変更してから再び取得する場合によく見られます。

`XRBoundedReferenceSpace` オブジェクトの場合、`boundsGeometry` が変更されたときに `reset` イベントを発生させることもできます。

いずれの場合も、イベントは、新しい原点を利用する WebXR アニメーションフレームが実行される前に送信されます。

|  |  |
| --- | --- |
| バブリング | あり |
| キャンセル | 不可 |
| インターフェイス | `XRReferenceSpaceEvent` |
| イベントハンドラープロパティ | `onreset` (en-US) |

使用上の注意
------

`reset` イベントは、参照空間の原点を変更し、イベントの `transform` プロパティで示されるように移動および回転することにより、座標系がリセットまたは再構成されたことを示します。 イベントは、アニメーションフレームのコールバックが実行されて保留中のフレームがレンダリングされる前に送信され、これらのコールバックで更新された座標系が使用可能であることを確認します。

リセットが発生する理由はいくつかあります。 それらの間で最も一般的なものは次のとおりです。

* ユーザーが、顔の向きおよびハンドコントローラーがユーザーの実際の位置および顔の向きと同期するようにヘッドセット自体の再較正を要求するなどして、手動で座標系をリセットしました。 これは主に、`local` または `local-floor` の参照空間の問題です。
* `bounded-floor` 参照空間では、ユーザーが参照空間の境界を出て新しい参照空間に入る場合、座標系をリセットできます（ゲーム内のあるレベルから別のレベルに横断する場合など、各レベルは独自の座標系を持つ独自のマップです）。
* 追跡システムは一時的にユーザーを見失い、その後再びユーザーを取り戻しましたが、最後に知られた位置のすぐ近くを離れるのに十分な距離を移動した後になってからでした。 主に `unbounded` 参照空間の問題です。
* ユーザーは、`unbounded` 参照空間にいて、開始位置（参照空間の原点）から十分に離れているため、浮動小数点またはその他の形式の誤差またはドリフトが問題になります。 このため座標系は、ユーザーの現在の位置またはその近くの新しい原点でリセットされます。
* WebXR インフラストラクチャーまたはハードウェアドライバーは、デバイスが一時的に追跡を失い、ハードウェアとソフトウェアの位置と方向が同期していないことを検出しました。

**メモ:** 参照空間が以前の原点の追跡を取り戻すことができる場合、`reset` イベントは発生*しません*。 これは、原点が強制的に再配置されていないことを意味します。 このイベントは、追跡喪失から回復するために原点を再配置する必要がある場合にのみ発生します。

### 手動リセット

VR ヘッドセットを使用したことがある人なら、起動したときに正面を向いているのに、ヘッドセットは空や床を見ていると勘違いしたり、ハンドコントローラーをまっすぐ前に向けているのに、どこか上や右に向けていると勘違いしたりしたことがあるでしょう。このような場合、通常はどこかのボタンを押し続けると、世界がデバイスの現在の向きに再同期されます。 これは、ヘッドセットの向きに基づいた 1 つまたは複数の参照空間に `reset` イベントを送信することで機能します。

### 不連続性の処理

ビューアーの位置のジャンプは、`XRPose` (en-US) の Boolean プロパティ `emulatedPosition` (en-US) を監視することで処理できます。 ビューアーの位置のジャンプが、`emulatedPosition` が `true` から `false` に切り替わるのと同時に起こった場合、ビューアーは追跡を回復し、新しい位置は以前にエミュレートされた値からの補正を表します。 これは、サイトまたはアプリが、（XR デバイスが移動を導入するために使用するユーザーの物理的な移動ではなく）ビューアーの位置や向きを明示的に変更することによる空間内のモーションをシミュレートしない場合に一般的に望ましい振る舞いです。

ただし、そのような「テレポート」が使用されている場合は、追跡を回復した後に、追加の潜在的に不快なジャンプが発生する可能性があり、実際にはユーザーの位置をジャンプしないようにする必要があります。 これを許可する代わりに、`getOffsetReferenceSpace()` を呼び出す前に計算されたテレポーテーションオフセットに `emulatedPosition` を統合して、更新された実際の原点が前のフレームからビューアーの位置がジャンプした隔たりによって調整された新しい参照空間を作成できます。 このようにして、ユーザーの位置を 2 回ではなく 1 回だけ変更します。

### 不連続の大きさの効果

不連続が十分に小さく、デバイスが同じ追跡領域内で追跡を取り戻せる場合、`reset` イベントは発生しません。 また、ユーザーの近くで空間の安定性を維持するために、時間の経過とともにネイティブの原点に小さな調整を加えるため、unbounded 参照空間で発生することもありません。 大きな不連続のみがリセットをトリガーします。

例
-

`reset` イベントのハンドラーを追加するには、2 つの方法のいずれかを使用できます。 まず、次のように `addEventListener()` メソッドを使用できます。

```js
viewerRefSpace.addEventListener("reset", (event) => {
  /\* perform reset related tasks \*/
});
```

2 番目のオプションは、次のように `XRReferenceSpace` オブジェクトの `onreset` (en-US) イベントハンドラープロパティを設定することです。

```js
viewerRefSpace.onreset = (event) => {
  /\* perform reset related tasks \*/
};
```

仕様
--

| Specification |
| --- |
| WebXR Device API # eventdef-xrreferencespace-reset |
| WebXR Device API # dom-xrreferencespace-onreset |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


XRReferenceSpace.getOffsetReferenceSpace() - Web API

XRReferenceSpace.getOffsetReferenceSpace()
==========================================

**安全なコンテキスト用:** この機能は一部またはすべての対応しているブラウザーにおいて、安全なコンテキスト (HTTPS) でのみ利用できます。

`XRReferenceSpace` インターフェイスの **`getOffsetReferenceSpace()`** メソッドは、メソッドが呼び出されたオブジェクトと 3D 空間内の特定の点との間の位置の相対的な違いを表す新しい参照空間オブジェクトを返します。 `getOffsetReferenceSpace()` によって返されるオブジェクトは、`XRReferenceSpace` で呼び出された場合は `XRReferenceSpace` であり、`XRBoundedReferenceSpace` で呼び出された場合は `XRBoundedReferenceSpace` です。

つまり、3D 空間にオブジェクトがあり、それに対して別のオブジェクトを相対的に配置する必要がある場合は、`getOffsetReferenceSpace()` を呼び出して、*`getOffsetReferenceSpace()` を呼び出すオブジェクトの位置と向きを基準*にして、2 番目のオブジェクトに持たせる位置と向きを渡します。

次に、シーンを描画するときに、オフセット参照空間を使用して、オブジェクトを相互に相対的に配置するだけでなく、必要な変換を適用して、ビューアーの位置に基づいてオブジェクトを適切にレンダリングできます。 これは、非 XR 入力に基づく回転の実装の例で示されています。 この例では、この方法を使用して、ユーザーがマウスを使用して視野角をピッチおよびヨーできるようにする方法を示しています。

構文
--
```

offsetReferenceSpace = xrReferenceSpace.getOffsetReferenceSpace(originOffset);
```

### パラメーター

`originOffset`

新しい参照空間の原点へのオフセットを指定する `XRRigidTransform`。 これらの値は、現在の参照空間の位置と向きに追加され、その結果は、新しく作成された `XRReferenceSpace` の位置と向きとして使用されます。

### 返り値

メソッドが呼び出された参照空間と同じネイティブの原点を持つが、オブジェクトから `originOffset` で指定された点までの隔たりを示す原点オフセットを持つ参照空間を記述する新しい `XRReferenceSpace` オブジェクト。

このメソッドを呼び出すオブジェクトが `XRBoundedReferenceSpace` の場合、返されるオブジェクトも `XRBoundedReferenceSpace` です。 新しい参照空間の `boundsGeometry` には、元のオブジェクトの `boundsGeometry` が設定され、それぞれの点に `originOffset` の逆行列（inverse）が乗算されます。

例
-

以下は、`getOffsetReferenceSpace()` の使用方法を示すいくつかの例です。

### ビューアーのテレポートまたは位置の設定

シーンを最初に作成するときに、3D 世界内でのユーザーの位置を設定する必要がある場合があります。 `getOffsetReferenceSpace()` を使用してこれを行うことができます。

```js
xrSession.requestReferenceSpace("local").then((refSpace) => {
  xrReferenceSpace = refSpace;
  xrReferenceSpace = xrReferenceSpace.getOffsetReferenceSpace(
    new XRRigidTransform(startPosition, { x: 0, y: 0, z: 1.0, w: 1.0 }),
  );
  xrSession.requestAnimationFrame(drawFrame);
});
```

このコードでは、ローカル参照空間を取得し、`getOffsetReferenceSpace()` を使用して、原点を `startPosition` で指定された位置に調整し、向きが Z 軸に直に沿った新しい空間を作成します。 次に、`XRSession` (en-US) の `requestAnimationFrame()` (en-US) を使用して最初のアニメーションフレームを要求します。

### 非 XR 入力に基づく回転の実装

WebXR が直接サポートしている入力コントロールは、すべて専用の VR または AR の入力デバイスです。 マウス、キーボード、またはその他の入力デバイスを使用して 3D 空間内のオブジェクトを移動または変換したり、ユーザーが空間内を移動できるようにするには、入力を読み取って移動するためのコードを記述する必要があります。

これは、`getOffsetReferenceSpace()` のもう 1 つの良いユースケースです。 この例では、ユーザーがマウスを右クリックしながら動かすことで、視野角の変更により周囲を見回せるコードを示します。

まず、`mousemove` イベントのイベントハンドラーを追加します。 これは、マウスの右ボタンが押されている場合に回転を実行するコードを呼び出します。 `oncontextmenu` イベントで `preventDefault()` を呼び出すことにより、このイベントが無視されるように設定していることにも注意してください。 これにより、右クリックによってコンテキストメニューがブラウザーに表示されなくなります。

```js
canvas.oncontextmenu = (event) => {
  event.preventDefault();
};
canvas.addEventListener("mousemove", (event) => {
  if (event.buttons & 2) {
    rotateViewBy(event.movementX, event.movementY);
  }
});
```

次に、`rotateViewBy()` 関数は、`mousemove` イベントからのマウスデルタ値に基づいて、マウスルック（mouse look）方向のヨーとピッチを更新します。 ピッチを制限しているため、真上と真下を超えて見ることはできません。 これが呼び出されるたびに、新しいオフセットを使用して、`mousePitch` と `mouseYaw` の現在の値が更新されます。

```js
let mouseYaw = 0.0;
let mousePitch = 0.0;
const inverseOrientation = quat.create();
const MOUSE\_SPEED = 0.003;

function rotateViewBy(dx, dy) {
  mouseYaw += dx \* MOUSE\_SPEED;
  mousePitch += dy \* MOUSE\_SPEED;

  if (mousePitch < -Math.PI \* 0.5) {
    mousePitch = -Math.PI \* 0.5;
  } else if (mousePitch > Math.PI \* 0.5) {
    mousePitch = Math.PI \* 0.5;
  }
}
```

最後に、計算したヨーとピッチをビューアーの向きに実際に適用するコードが必要です。 この関数 `applyMouseMovement()` は、次のことを処理します。

```js
function applyMouseMovement(refSpace) {
  if (!mouseYaw && !mousePitch) {
    return refSpace;
  }

  quat.identity(inverseOrientation);
  quat.rotateX(inverseOrientation, inverseOrientation, -mousePitch);
  quat.rotateY(inverseOrientation, inverseOrientation, -mouseYaw);

  let newTransform = new XRRigidTransform(
    { x: 0, y: 0, z: 0 },
    {
      x: inverseOrientation[0],
      y: inverseOrientation[1],
      z: inverseOrientation[2],
      w: inverseOrientation[3],
    },
  );

  return refSpace.getOffsetReferenceSpace(newTransform);
}
```

この関数は、現在のピッチ値とヨー値からビューアーの向きを決めるために使用する逆向き行列を作成し、`new XRRigidTransform()` を呼び出すときにその行列を向きのソースとして使用します。 次に、新しい `XRRigidTransform` の参照空間がフェッチされ、呼び出し元に返されます。

この新しい参照空間は、ビューアーの位置は変更しませんが、蓄積されたマウス入力から生成されたピッチとヨーの値に基づいて向きを変更します。 `applyMouseMovement()` は、フレームを描画するときに、`getViewerPose()` (en-US) を使用してビューアーのポーズをフェッチする直前に呼び出す必要があり、レンダリングはこの参照空間で実行する必要があります。

これに似たコードが、移動、向き、モーションという WebXR チュートリアルの幅広い記事で使用されているのを見ることができます。 特に、WebXR セッションの開始というセクションを確認してください。

仕様
--

| Specification |
| --- |
| WebXR Device API # dom-xrreferencespace-getoffsetreferencespace |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


MessageEvent: MessageEvent() コンストラクター - Web API

MessageEvent: MessageEvent() コンストラクター
=====================================

**`MessageEvent()`** コンストラクターは新しい `MessageEvent` オブジェクトのインスタンスを作成します。

構文
--

```js
new MessageEvent(type)
new MessageEvent(type, options)
```

### 引数

`type`

 文字列で、このイベントの名前を示します。
 大文字と小文字を区別し、ブラウザーは常に `message` を設定します。

`options` 省略可

*`Event()` で定義されたプロパティに加え*、以下のプロパティを持つことができるオブジェクトです。

`data` 省略可

 MessageEvent に含めたいデータ。
 これは任意のデータ型であり、指定しない場合は既定で `null` になります。

`origin` 省略可

 文字列で、メッセージの発信元を表します。
 指定しなかった場合の既定値は空文字列 ("") です。

`lastEventId` 省略可

 文字列で、このイベントの一意な ID を表します。
 指定しなかった場合の既定値は空文字列 ("") です。

`source` 省略可

メッセージの送信元を表す `MessageEventSource` （`Window`、`MessagePort`、`ServiceWorker` の何れかのオブジェクト）を指定します。設定されていない場合の既定値は `null` です。

`ports` 省略可

`MessagePort` オブジェクトの配列で、メッセージが（チャンネルメッセージングや共有ワーカーにメッセージを送信する場合など、適切な場合に）送信されるチャンネルに関連するポートを表します。
 指定しない場合の既定値は空の配列 (`[]`) になります。

返値
--

新しい `MessageEvent` オブジェクトです。

例
-

```js
const myMessage = new MessageEvent("message", {
  data: "hello",
});
```

仕様書
---

| Specification |
| --- |
| HTML Standard # the-messageevent-interface:dom-event-constructor |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* `ExtendableMessageEvent` — このインターフェイスに似ていますが、もっと柔軟性が必要なインターフェイスで使用されます。


MessageEvent: source プロパティ - Web API

MessageEvent: source プロパティ
==========================

**`source`** は `MessageEvent` インターフェイスの読み取り専用プロパティで、メッセージ送信者を表す `MessageEventSource` （WindowProxy, `MessagePort`, `ServiceWorker` オブジェクトのいずれか）です。

値
-

メッセージ送信者を表す `MessageEventSource` （WindowProxy, `MessagePort`, `ServiceWorker` オブジェクトのいずれか）です。

例
-

```js
myWorker.onmessage = (e) => {
  result.textContent = e.data;
  console.log("Message received from worker");
  console.log(e.source);
};
```

仕様書
---

| Specification |
| --- |
| HTML Standard # dom-messageevent-source-dev |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* `ExtendableMessageEvent` — このインターフェイスに似ていますが、もっと柔軟性が必要なインターフェイスで使用されます。


MessageEvent: origin プロパティ - Web API

MessageEvent: origin プロパティ
==========================

**`origin`** は `MessageEvent` インターフェイスの読み取り専用プロパティで、メッセージ送信元のオリジンを表す文字列です。

値
-

オリジンを表す文字列です。

例
-

```js
myWorker.onmessage = (e) => {
  result.textContent = e.data;
  console.log("Message received from worker");
  console.log(e.origin);
};
```

仕様書
---

| Specification |
| --- |
| HTML Standard # dom-messageevent-origin-dev |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* `ExtendableMessageEvent` — このインターフェイスに似ていますが、もっと柔軟性が必要なインターフェイスで使用されます。


MessageEvent: data プロパティ - Web API

MessageEvent: data プロパティ
========================

**`data`** は `MessageEvent` インターフェイスのプロパティで、このメッセージの送信元が送信するデータを表します。

値
-

このメッセージの送信元が送信するデータです。このイベントの発信元が何であるかによって、あらゆるデータ型になる可能性があります。

例
-

```js
myWorker.onmessage = (e) => {
  result.textContent = e.data;
  console.log("Message received from worker");
};
```

仕様書
---

| Specification |
| --- |
| HTML Standard # dom-messageevent-data-dev |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* `ExtendableMessageEvent` — このインターフェイスに似ていますが、もっと柔軟性が必要なインターフェイスで使用されます。


MessageEvent: ports プロパティ - Web API

MessageEvent: ports プロパティ
=========================

**`ports`** は `MessageEvent` インターフェイスの読み取り専用プロパティで、メッセージが（チャネルメッセージングや共有ワーカーにメッセージを送信する場合など、適切な場合に）送信されるチャネルに関連するポートを表す `MessagePort` オブジェクトの配列です。

値
-

`MessagePort` オブジェクトの配列です。

例
-

```js
onconnect = (e) => {
  const port = e.ports[0];

  port.addEventListener("message", (e) => {
    const workerResult = `Result: ${e.data[0] \* e.data[1]}`;
    port.postMessage(workerResult);
  });

  port.start(); // addEventListener を使用した場合は必要。 onmessage セッターからは暗黙に呼び出されます。
};
```

仕様書
---

| Specification |
| --- |
| HTML Standard # dom-messageevent-ports-dev |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* `ExtendableMessageEvent` — このインターフェイスに似ていますが、もっと柔軟性が必要なインターフェイスで使用されます。


MessageEvent: lastEventId プロパティ - Web API

MessageEvent: lastEventId プロパティ
===============================

**`lastEventId`** は `MessageEvent` インターフェイスの読み取り専用プロパティで、このイベントの固有の ID を表す文字列です。

値
-

ID を表す文字列です。

例
-

```js
myWorker.onmessage = (e) => {
  result.textContent = e.data;
  console.log("Message received from worker");
  console.log(e.lastEventId);
};
```

仕様書
---

| Specification |
| --- |
| HTML Standard # dom-messageevent-lasteventid-dev |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* `ExtendableMessageEvent` — このインターフェイスに似ていますが、もっと柔軟性が必要なインターフェイスで使用されます。


ScreenOrientation.unlock() - Web API

ScreenOrientation.unlock()
==========================

`ScreenOrientation` インターフェイスの **`unlock()`** プロパティは、含んでいる文書の向きのデフォルトの向きへの固定を解除します。

構文
--

```js
unlock()
```

### 引数

なし

### 返値

なし (`undefined`)

仕様書
---

| Specification |
| --- |
| Screen Orientation # dom-screenorientation-unlock |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


ScreenOrientation.angle - Web API

ScreenOrientation.angle
=======================

`ScreenOrientation` インターフェイスの読み取り専用プロパティ **`angle`** は、文書の現在の向きの角度を返します。

値
-

`unsigned short` の整数です。

仕様書
---

| Specification |
| --- |
| Screen Orientation # dom-screenorientation-angle |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


ScreenOrientation.lock() - Web API

ScreenOrientation.lock()
========================

`ScreenOrientation` インターフェイスの **`lock()`** プロパティは、含んでいる文書の向きを指定された向きに固定します。

通常、向きの固定はモバイルデバイスで、かつブラウザーがフルスクリーン表示になっているときのみ有効です。向きの固定に対応している場合、以下で挙げる引数の値全てで動作しなければなりません。

構文
--

```js
lock(orientation)
```

### 引数

`orientation`

向きの固定のタイプです。以下のいずれかです。

`"any"`

`portrait-primary`、`portrait-secondary`、`landscape-primary`、`landscape-secondary` のいずれかです。

`"natural"`

OS が定義する画面の自然な向きで、`portrait-primary` または `landscape-primary` のいずれかです。

`"landscape"`

画面の幅が画面の高さより長い向きです。プラットフォームの仕様により、`landscape-primary` か、`landscape-secondary` か、またはその両方です。

`"portrait"`

画面の高さが画面の幅より長い向きです。プラットフォームの仕様により、`portrait-primary` か、`portrait-secondary` か、またはその両方です。

`"portrait-primary"`

1 番目のポートレートモードです。自然な向きがポートレートモード (画面の高さが画面の幅より長い) のときは、これは自然な向きと同じであり、角度は 0 度に対応します。自然な向きがランドスケープモードのときは、どっちのポートレートの向きが `portrait-primary` でどっちが `portrait-secondary` かはユーザーエージェントが決めることができ、どっちかが 90 度、もう一方が270 度に対応します。

`"portrait-secondary"`

2 番目のポートレートの向きです。自然な向きがポートレートモードのときは、これは角度 180 度に対応します。(すなわち、デバイスは自然な向きから上下逆さまになっています) 自然な向きがランドスケープモードのときは、これはユーザーエージェントが決めることができ、`portrait-primary` として選ばれなかったほうの向きです。

`"landscape-primary"`

1 番目のランドスケープモードです。自然な向きがランドスケープモード (画面の幅が画面の高さより長い) のときは、これは自然な向きと同じであり、角度は 0 度に対応します。自然な向きがポートレートモードのときは、どっちのランドスケープの向きを `landscape-primary` とし、角度を 90 度とするか 270 度とするかはユーザーエージェントが決めることができます。(`landscape-secondary` がもう一方の向きと角度になります)

`"landscape-secondary"`

2 番目のランドスケープモードです。自然な向きがランドスケープモードのときは、自然な向きから上下逆さまであり、角度は 180 度です。自然な向きがポートレートモードのときは、これはユーザーエージェントが決めることができ、`landscape-primary` として選ばれなかったほうです。

### 返値

固定に成功した後解決する `Promise` です。

### 例外

プロミスが以下の例外により拒否されることがあります。

`NotSupportedError` `DOMException`

ユーザーエージェントが画面の向きの固定に対応していません。

`SecurityError` `DOMException`

ユーザーエージェント定義の固定前の条件を満たしていません。例えば、ブラウザーがトップレベルのブラウジングコンテキストの `Document` がフルスクリーンになっていることを要求するかもしれません。文書でブラウジングコンテキストの画面の向きの固定のサンドボックス化フラグが立っている場合も、プロミスがこのエラーで拒否されることがあります。

`TypeError`

引数 `orientation` がありません。

例
-

この例は、画面の向きを現在の向きの反対に固定する方法を示します。なお、この例はモバイルデバイスおよび向きの変更に対応したその他のデバイスでのみ動作するでしょう。

```html
<div id="example\_container">
  <button id="fullscreen\_button">フルスクリーン</button>
  <button id="lock\_button">固定</button>
  <button id="unlock\_button">固定解除</button>
  <textarea id="log" rows="7" cols="85"></textarea>
</div>
```

```js
const log = document.getElementById("log");

// 固定ボタン: 画面の向きをもう一方の向き (90 度回転させた向き) に固定します
const rotate_btn = document.querySelector("#lock\_button");
rotate_btn.addEventListener("click", () => {
  log.textContent += `固定ボタンが押されました\n`;

  const oppositeOrientation = screen.orientation.type.startsWith("portrait")
    ? "landscape"
    : "portrait";
  screen.orientation
    .lock(oppositeOrientation)
    .then(() => {
      log.textContent = `${oppositeOrientation} に固定しました\n`;
    })
    .catch((error) => {
      log.textContent += `${error}\n`;
    });
});

// 固定解除ボタン: (固定されていれば) 画面の向きの固定を解除します
const unlock_btn = document.querySelector("#unlock\_button");
unlock_btn.addEventListener("click", () => {
  log.textContent += "固定解除ボタンが押されました\n";
  screen.orientation.unlock();
});

// フルスクリーンボタン: 例をフルスクリーンにします
const fullscreen_btn = document.querySelector("#fullscreen\_button");
fullscreen_btn.addEventListener("click", () => {
  log.textContent += "フルスクリーンボタンが押されました\n";
  const container = document.querySelector("#example\_container");
  container.requestFullscreen().catch((error) => {
    log.textContent += `${error}\n`;
  });
});
```

この例を試すには、まずフルスクリーンボタンを押してください。例がフルスクリーンになったら、固定ボタンを押すと向きを切り替えることができ、固定解除ボタンを押すと自然な向きに戻すことができます。

仕様書
---

| Specification |
| --- |
| Screen Orientation # dom-screenorientation-lock |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


ScreenOrientation.type - Web API

ScreenOrientation.type
======================

`ScreenOrientation` の読み取り専用プロパティ **`type`** は、文書の現在の向きのタイプを返します。`"portrait-primary"`、`"portrait-secondary"`、`"landscape-primary"`、`"landscape-secondary"` のいずれかです。

値
-

`String` です。

仕様書
---

| Specification |
| --- |
| Screen Orientation # dom-screenorientation-type |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


マルチタッチ操作 - Web API

マルチタッチ操作
========

タッチイベントのインターフェイスは、アプリケーション固有のシングルタッチやマルチタッチ操作に対応しています。しかし、タッチイベントは`マウスイベント`のような他の DOM 入力イベントとは大きく異なるため、プログラマーにとってインターフェイスを使用するのは少し難しいかもしれません。このガイドで説明するアプリケーションは、アプリケーション固有のジェスチャーを作成するために必要な基本である、単純なシングルおよびマルチタッチ操作にタッチイベントを使用する方法を示しています。

このアプリケーションのライブ版は、 GitHub で利用できます。ソースコードは GitHub で利用でき、プルリクエストやバグ報告は歓迎されます。

例
-

この例では、`touchstart`、`touchmove`、`touchcancel`、`touchend` のタッチイベントを、シングルタッチ、 2 回（同時）タッチ、 2 回以上同時タッチ、 1 本指スワイプ、 2 本指移動/ピンチ/スワイプのジェスチャーに使用する例を示します。

### タッチターゲットの定義

このアプリケーションでは、 `<div>` 要素を使って 4 つのタッチ領域を表現しています。

```html
<style>
 div {
 margin: 0em;
 padding: 2em;
 }
 #target1 {
 background: white;
 border: 1px solid black;
 }
 #target2 {
 background: white;
 border: 1px solid black;
 }
 #target3 {
 background: white;
 border: 1px solid black;
 }
 #target4 {
 background: white;
 border: 1px solid black;
 }
</style>
```

### グローバルステート

`tpCache` は、タッチポイントが発生したイベントの外部で処理するためにタッチポイントをキャッシュするために使用されます。

```js
// ログイベントフラグ
const logEvents = false;

// タッチポイントキャッシュ
const tpCache = [];
```

### イベントハンドラーの登録

イベントハンドラーは 4 種類のタッチイベントすべてに登録されています。 `touchend` と `touchcancel` のイベント種別は同じハンドラーを使用します。

```js
function set\_handlers(name) {
  // 指定された要素のイベントハンドラーをインストール
  const el = document.getElementById(name);
  el.ontouchstart = start_handler;
  el.ontouchmove = move_handler;
  // touchcancel と touchend に同じハンドラーを使用
  el.ontouchcancel = end_handler;
  el.ontouchend = end_handler;
}

function init() {
  set\_handlers("target1");
  set\_handlers("target2");
  set\_handlers("target3");
  set\_handlers("target4");
}
```

### 移動/ピンチ/ズームハンドラー

この関数は、とても基本的な2タッチ水平移動/ピンチ/ズーム処理に対応します。このコードにはエラー処理や垂直方向の移動は含まれていません。ピンチとズームの動きを検出するための *しきい値* は、アプリケーション固有（および端末依存）であることに注意してください。

```js
// これは非常に基本的な2タッチ操作用の移動/ピンチ/ズームハンドラーで、
// エラー処理は含まれておらず、水平方向の移動などを処理するだけです。
function handle\_pinch\_zoom(ev) {
  if (ev.targetTouches.length === 2 && ev.changedTouches.length === 2) {
    // 2 つのタッチが、 2 タッチを開始したのと同じタッチかどうかを確認
    const point1 = tpCache.findLastIndex(
      (tp) => tp.identifier === ev.targetTouches[0].identifier,
    );
    const point2 = tpCache.findLastIndex(
      (tp) => tp.identifier === ev.targetTouches[1].identifier,
    );

    if (point1 >= 0 && point2 >= 0) {
      // 開始座標と移動座標の差を計算
      const diff1 = Math.abs(
        tpCache[point1].clientX - ev.targetTouches[0].clientX,
      );
      const diff2 = Math.abs(
        tpCache[point2].clientX - ev.targetTouches[1].clientX,
      );

      // この閾値は、デバイスに依存し、アプリケーションにも依存する
      const PINCH\_THRESHOLD = ev.target.clientWidth / 10;
      if (diff1 >= PINCH\_THRESHOLD && diff2 >= PINCH\_THRESHOLD)
        ev.target.style.background = "green";
    } else {
      // empty tpCache
      tpCache = [];
    }
  }
}
```

### タッチ開始ハンドラー

`touchstart` イベントハンドラーは、 2 タッチジェスチャに対応するためにタッチ点をキャッシュします。また、 `preventDefault()` を呼び出して、ブラウザーがさらなるイベント処理（例えば、マウスイベントのエミュレーション）を適用しないようにします。

```js
function start\_handler(ev) {
  // ユーザーが同時にタッチした場合、ブラウザーは各タッチポイントに
  // 対して別々の touchstart イベントを発生させます。したがって、3 つの
  // 同時タッチがあった場合、最初の touchstart イベントの targetTouches の
  // 長さは 1、2 番目のイベントの長さは 2 となります。
  ev.preventDefault();
  // 2タッチピンチ/ズームを後で処理するためにタッチ点をキャッシュする
  if (ev.targetTouches.length === 2) {
    for (let i = 0; i < ev.targetTouches.length; i++) {
      tpCache.push(ev.targetTouches[i]);
    }
  }
  if (logEvents) log("touchStart", ev, true);
  update\_background(ev);
}
```

### タッチ移動ハンドラー

`touchmove` ハンドラーは、上記と同じ理由で `preventDefault()` を呼び出し、ピンチ/ズームハンドラーを呼び出します。

```js
function move\_handler(ev) {
  // メモ: ユーザーが複数の「同時」タッチをした場合、ほとんどのブラウザーは 1 つ
  // 以上の touchmove イベントを発生させ、複数を発生させるものもあります。
  // その結果、アプリケーションはいくつかのタッチ移動を「無視」したくなるかもしれません。
  //
  // この関数は、ターゲット要素の境界線を「破線」に設定し、ターゲットが移動
  // イベントを受け取ったことを視覚的に示します。
  //
  ev.preventDefault();
  if (logEvents) log("touchMove", ev, false);
  // タッチ移動イベントが開始されたときに色が点滅しすぎるのを防ぐため、
  // 2 つのタッチポイントがアクティブな場合は背景を更新しないようにします。
  if (!(ev.touches.length === 2 && ev.targetTouches.length === 2))
    update\_background(ev);

  // ターゲット要素の境界線を破線に設定し、要素が移動イベントを受け取ったことを
  // 視覚的に明確に示します。
  ev.target.style.border = "dashed";

  // 2 タッチの移動/ピンチ/ズームジェスチャーでは、このイベントをチェックする
  handle\_pinch\_zoom(ev);
}
```

### タッチ終了ハンドラー

`touchend` ハンドラーは、イベントターゲットの背景色を元の色に戻します。

```js
function end\_handler(ev) {
  ev.preventDefault();
  if (logEvents) log(ev.type, ev, false);
  if (ev.targetTouches.length === 0) {
    // Restore background and border to original values
    ev.target.style.background = "white";
    ev.target.style.border = "1px solid black";
  }
}
```

### アプリケーションの UI

このアプリケーションは、`<div>`要素をタッチ領域に使用し、ログを有効にするボタンとログをクリアするボタンを提供します。

```html
<div id="target1">Tap, Hold or Swipe me 1</div>
<div id="target2">Tap, Hold or Swipe me 2</div>
<div id="target3">Tap, Hold or Swipe me 3</div>
<div id="target4">Tap, Hold or Swipe me 4</div>

<!-- UI for logging/debugging -->
<button id="log" onclick="enableLog(event);">Start/Stop event logging</button>
<button id="clearlog" onclick="clearLog(event);">Clear the log</button>
<p></p>
<output></output>
```

### その他の機能

これらの昨日はアプリケーションをサポートしますが、イベントフローには直接関与しません。

#### 背景色の更新

タッチ領域の背景色は、タッチなしは`白`、 1 回タッチは`黄`、 2 回同時タッチは`ピンク`、 3 回以上同時タッチは`水色`に変化します。 2 本指の移動/ピンチ/ズームが検出されたときの背景色の変化についてはタッチ移動を参照してください。

```js
function update\_background(ev) {
  // Change background color based on the number simultaneous touches
  // in the event's targetTouches list:
  // yellow - one tap (or hold)
  // pink - two taps
  // lightblue - more than two taps
  switch (ev.targetTouches.length) {
    case 1:
      // Single tap`
      ev.target.style.background = "yellow";
      break;
    case 2:
      // Two simultaneous touches
      ev.target.style.background = "pink";
      break;
    default:
      // More than two simultaneous touches
      ev.target.style.background = "lightblue";
  }
}
```

#### イベントのログ出力

この関数は、アプリケーションウィンドウにイベントアクティビティを記録し、デバッグやイベントフローの学習の助けにするために使用されます。

```js
function enableLog(ev) {
  logEvents = !logEvents;
}

function log(name, ev, printTargetIds) {
  const o = document.getElementsByTagName("output")[0];
  let s =
    `${name}: touches = ${ev.touches.length} ; ` +
    `targetTouches = ${ev.targetTouches.length} ; ` +
    `changedTouches = ${ev.changedTouches.length}`;
  o.innerHTML += `${s}<br>`;

  if (printTargetIds) {
    s = "";
    for (let i = 0; i < ev.targetTouches.length; i++) {
      s += `... id = ${ev.targetTouches[i].identifier}<br>`;
    }
    o.innerHTML += s;
  }
}

function clearLog(event) {
  const o = document.getElementsByTagName("output")[0];
  o.innerHTML = "";
}
```

関連トピックとリソース
-----------

* ポインターイベント


タッチイベントの使用 - Web API

タッチイベントの使用
==========

今日、ほとんどのウェブコンテンツはキーボードとマウス入力用に設計されています。しかし、タッチ画面を備えた端末（特に携帯端末）が主流となっており、ウェブアプリケーションはタッチイベントを用いることでタッチベースの入力を直接処理するか、アプリケーションが入力用に*解釈されたマウスイベント*を使用することができます。マウスイベントを使用することの欠点は、ユーザーによる同時入力に対応していないことです。一方、タッチイベントは複数の同時入力（タッチ面上の異なる場所での入力も可能）に対応しているため、ユーザーによる使い勝手を向上させることができます。

タッチイベントインターフェイスは、2本指ジェスチャのようなアプリケーション特有のシングルおよびマルチタッチ操作に対応します。マルチタッチ操作は、指（またはスタイラス）が最初に接触面に触れたときに始まります。他の指が続けて接触面に触れ、オプションで接触面を移動することもあります。この操作は、指がタッチ面から除去されると終わります。この操作中、アプリケーションは開始、移動、終わりの各フェーズでタッチイベントを受信します。アプリケーションはタッチ入力に自分自身で意味づけを適用することができます。

インターフェイス
--------

タッチイベントは、 3 つのインターフェイス（`Touch`、`TouchEvent`、`TouchList`）と以下のイベント種別で構成されます。

* `touchstart` - タッチ面にタッチ点が配置されると発行されます。
* `touchmove` - タッチ点がタッチ面に沿って移動すると発行されます。
* `touchend` - タッチ点がタッチ面から除去されると発行されます。
* `touchcancel` - タッチ点が実装固有の方法で中断された場合に発行されます（例えば、作成されたタッチ点が多すぎる場合など）。

`Touch` インターフェイスは、タッチ感応機器上の単一のタッチ点を表します。この接触点は通常タッチ点 (*touch point*) またはタッチ (*touch*) と呼ばれます。タッチは通常、タッチスクリーン、ペン、トラックパッド上の指やスタイラスによって生成されます。タッチ点のプロパティには、固有の識別子、タッチ点の対象要素、ビューポート、ページ、画面とタッチ点の相対関連の位置の *X* および *Y* 座標があります。

`TouchList` インターフェイスはタッチ面の接触点のリストを表します。したがって、ユーザーが 1 本の指でタッチ面を操作した場合、リストには 1 つの項目が格納され、ユーザーが 3 本の指でタッチ面に触れた場合、リストの長さは 3 つになります。

`TouchEvent` インターフェイスは、タッチ感応面との接触の状態が変化したときに送信されるイベントを表します。状態の変化とは、タッチ面との接触を始めること、タッチ面との接触を維持したままタッチ点を移動すること、タッチ点を離すこと、タッチイベントを取り消すことです。このインターフェイスの属性には、いくつかの修飾キー（`shift` キーなど）の状態と、次のタッチリストが含まれています。

* `touches` - 現在画面上にあるすべてのタッチ点のリストです。
* `targetTouches` - ターゲット DOM 要素上のタッチ点のリストです。
* `changedTouches` - 関連するイベント種別に依存する項目を持つタッチ点のリストです。
    + `touchstart` イベントにおいては、現在のイベントでアクティブになったタッチ点のリストです。
    + `touchmove` イベントにおいては、前回のイベント以降に変更されたタッチ点のリストです。
    + `touchend` イベントにおいては、表面から除去されたタッチ点のリスト（つまり、表面に触れなくなった指に対応するタッチ点の集合）です。

これらのインターフェイスは、比較的低レベルの機能集合を定義していますが、多指スワイプ、回転、ピンチ、ズームなどのおなじみのマルチタッチジェスチャーを含む、多くの種類のタッチベースの操作に対応しています。

インターフェイスからジェスチャーへ
-----------------

アプリケーションは、ジェスチャーの意味づけを定義する際に異なる要素を考慮することがあります。例えば、タッチ点が始めた位置からタッチが終わった位置までの移動距離です。例えば、タッチを始めてから終わるまでの経過時間や、ダブルタッ プジェスチャーを作成する意図通りに 2 回連続してタップする間の経過時間などです。スワイプの方向性（例えば左から右、右から左など）も考慮すべき要因の 1 つです。

アプリケーションが使用するタッチリストは、アプリケーションのジェスチャーの意味づけに依存します。例えば、アプリケーションが 1 つの要素における単一のタッチ（タップ）に対応している場合、 `targetTouches` リストを `touchstart` イベントハンドラーで使用して、アプリケーション固有の方法でタッチ点を処理します。アプリケーションが任意の 2 つのタッチ点について 2 本指スワイプに対応している場合、 `changedTouches` リストを `touchmove` イベントハンドラーで使用して、 2 つのタッチ点が移動したかどうかを判断し、アプリケーション固有の方法でそのジェスチャーの意味づけを実装します。

ブラウザーは通常、アクティブなタッチ点が単一の場合のみ、エミュレートされたマウスイベントとクリックイベントを配信します。 2 つ以上のアクティブなタッチ点を含むマルチタッチ操作では、通常、タッチイベントのみが生成されます。エミュレートされたマウスイベントが送信されないようにするには、タッチイベントハンドラーで `preventDefault()` メソッドを使用してください。マウスイベントとタッチイベントの対話操作に関する詳細情報は、 TouchEvent と MouseEvent の両方への対応を参照してください。

基本的な手順
------

この節では、上記のインターフェイスを使用する基本的な使い方を格納します。より詳しい例はタッチイベントの概要を参照してください。

タッチイベントの種類ごとにイベントハンドラーを登録します。

```js
// タッチイベントのハンドラーを登録
someElement.addEventListener("touchstart", process_touchstart, false);
someElement.addEventListener("touchmove", process_touchmove, false);
someElement.addEventListener("touchcancel", process_touchcancel, false);
someElement.addEventListener("touchend", process_touchend, false);
```

アプリケーションのジェスチャー意味づけを実装するために、イベントハンドラーでイベントを処理します。

```js
// touchstart ハンドラー
function process\_touchstart(ev) {
  // イベントのデータを使用して、適切なジェスチャーハンドラーに呼び出す
  switch (ev.touches.length) {
    case 1:
      handle\_one\_touch(ev);
      break;
    case 2:
      handle\_two\_touches(ev);
      break;
    case 3:
      handle\_three\_touches(ev);
      break;
    default:
      gesture\_not\_supported(ev);
      break;
  }
}
```

タッチ点の属性にアクセスします。

```js
// touchstart ハンドラーの作成
someElement.addEventListener(
  "touchstart",
  (ev) => {
    // この要素に対して有効化されたタッチ点を反復処理し、
    // 各イベントの 'target' を処理する
    for (let i = 0; i < ev.targetTouches.length; i++) {
      process\_target(ev.targetTouches[i].target);
    }
  },
  false,
);
```

ブラウザーがエミュレートされたマウスイベントを処理しないようにします。

```js
// touchmove ハンドラー
function process\_touchmove(ev) {
  // preventDefault() を呼び出すよう設定
  ev.preventDefault();
}
```

ベストプラクティス
---------

タッチイベントを使用する際に考慮すべきベストプラクティスをいくつか紹介します。

* タッチハンドラーで行う作業を最小限に抑えましょう。
* 文書全体や文書ツリー内の上位ノードではなく）固有の対象要素にタッチ点ハンドラーを追加しましょう。
* `touchmove`、`touchend`、`touchcancel` イベントハンドラーは `touchstart` 内で追加しましょう。
* 対象とするタッチ要素やノードは、指が触れるのに十分な大きさが必要です。対象領域が小さすぎる場合、タッチすると隣接する他の要素のイベントが発生する可能性があります。
実装と展開状況
-------

タッチイベントのブラウザ互換性データによると、モバイルブラウザーのタッチイベント対応は相対的に広く、デスクトップブラウザーの対応は遅れていますが、追加実装は進行中です。

タッチ点のタッチ領域 - ユーザーとタッチ面の接触の領域 - に関するいくつかの新しい機能が標準化されつつあります。新しい機能には、タッチ点とタッチ面の接触面積を最も近く囲む楕円の半径 *X* と *Y* が含まれています。。また、タッチ点の回転角度（記述されている楕円を何度回転させ、接触領域に配置するか）も標準化され、タッチ点にかかる圧力も標準化されています。

ポインターイベントについて
-------------

新しい入力メカニズムが導入されると、キーイベント、マウスイベント、ペン/スタイラスイベント、タッチイベントなど、さまざまな入力イベントを処理するアプリケーションの複雑さが増します。この問題を解決するために、ポインターイベント標準が、マウス、ペン、タッチ画面などを含む機器のハードウェアにとらわれないポインター入力を処理するためのイベントと関連のインターフェイスを定義しています。つまり、抽象的なポインターとして、指やペン/スタイラスの接触点または、マウスを表すことができる統一された入力モデルを作成します。ポインターイベントの MDN 記事を参照してください。

ポインターはあらゆる入力機器からの入力を表すので、ポインターイベントモデルはアプリケーションの入力処理を単純化することができます。さらに、ポインターイベントの修理はマウスイベントの種類（例えば `pointerdown` や `pointerup`）ととても似ているので、ポインタイベントを処理するコードはマウスを処理するコードとよく似ます。

ブラウザーにおけるポインターイベントの実装状況は比較的高く、Chrome、Firefox、IE11、Edge は完全に実装しています。

例とデモ
----

以下のドキュメントでは、タッチイベントの使用方法について説明しており、例コードもあります。

* タッチイベントの概要
* Implement Custom Gestures
* Add touch screen support to your website (The easy way)

タッチイベントのデモ:

* Paint Program (by Rick Byers)
* Touch/pointer tests and demos (by Patrick H. Lauke)
コミュニティ
------

* Touch Events Community Group
* メーリングリスト
* W3C #touchevents IRC channel
関連トピックとリソース
-----------

* ポインターイベント標準


PromiseRejectionEvent.promise - Web API

PromiseRejectionEvent.promise
=============================

`PromiseRejectionEvent` インターフェイスの **`promise`** は読み取り専用プロパティで、拒否された JavaScript の `Promise` を表します。プロミスが拒否された理由は、イベントの `PromiseRejectionEvent.reason` プロパティを検査することでわかります。

### 値

拒否されて、処理されていない JavaScript の `Promise` です。

例
-

この例では処理されておらず、 `reason` の `code` 項目が "Module not ready" であるオブジェクトであるプロミスを待ち受けし、正しい実行に失敗するタスクを再実行する何もしないコールバックをセットアップします。

`event.preventDefault()` はプロミスが処理されたことを示すために呼ばれます。

```js
window.onunhandledrejection = (event) => {
  if (event.reason?.code === "Module not ready") {
    requestIdleCallback((deadline) => {
      loadModule(event.reason.moduleName).then(performStartup);
    });
    event.preventDefault();
  }
};
```

仕様書
---

| Specification |
| --- |
| HTML Standard # dom-promiserejectionevent-promise |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* プロミスの拒否イベント
* `Promise`
* `PromiseRejectionEvent`
* `rejectionhandled`
* `unhandledrejection`


PromiseRejectionEvent() - Web API

PromiseRejectionEvent()
=======================

**`PromiseRejectionEvent()`** コンストラクターは、新しい `PromiseRejectionEvent` オブジェクトを返します。これは、 JavaScript の `Promise` が拒否されたときに発生するイベントを表します。

プロミス拒否イベントにより、失敗したプロミスや、失敗を見逃したプロミスを検出して報告することが可能になります。また、エラーに対するグローバルハンドラーを書くことも容易になります。

`PromiseRejectionEvent` には 2 種類あります。 `unhandledrejection` は、プロミスが拒否されたが処理されなかった場合に JavaScript ランタイムによって発行されます。 `rejectionhandled` イベントは、プロミスが拒否されたが、拒否が拒否ハンドラーによって捕捉された場合に発行されるものです。

構文
--

```js
new PromiseRejectionEvent(type, options)
```

### 引数

`type`

 文字列で、イベントの名前を表します。
 大文字と小文字の区別があり、ブラウザーは `rejectionhandled` または `unhandledrejection` のどちらかに設定します。

`options`

オブジェクトで、 *`Event()` で定義されたプロパティに加え*、以下のプロパティを持つことができます。

`promise`

拒否された `Promise`。

`reason`

プロミスが拒否された理由を表す任意の値または `Object`。これは、エラーコード、エラー文字列、プロミスが拒否された状況を記述する詳細な情報を含むオブジェクトなど、何でもかまいません。

### 返値

引数で指定された通りに構成された、新しい `PromiseRejectionEvent` オブジェクトです。

例
-

この例では、文字列 "My house is on fire" を理由とするプロミス `myPromise` に対して新しい `unhandledrejection` イベントが作成されます。理由`は数字とすることもできますし、家の住所や火事の程度、緊急連絡先の電話番号など、詳細な情報を記載したオブジェクトでも構いません。

```js
let myRejectionEvent = new PromiseRejectionEvent("unhandledrejection", {
  promise: myPromise,
  reason: "My house is on fire",
});
```

仕様書
---

| Specification |
| --- |
| HTML Standard # unhandled-promise-rejections:dom-event-constructor |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* プロミスの使用
* `Promise`
* `PromiseRejectionEvent`


PromiseRejectionEvent.reason - Web API

PromiseRejectionEvent.reason
============================

`PromiseRejectionEvent` の **`reason`** は読み取り専用プロパティで、`Promise.reject()` に渡される理由を提供する任意の JavaScript 値、または `Object` です。理論的にはプロミスが拒否された理由についての情報を提供します。

### 値

プロミスが拒否された理由を理解するために使用することができる情報を提供する値またはオブジェクト。これは、エラーコードからテキスト、リンク、その他含めたいものを持つオブジェクトまで、何らかのものにすることができます。

例
-

```js
window.onunhandledrejection = (e) => {
  console.log(e.reason);
};
```

仕様書
---

| Specification |
| --- |
| HTML Standard # dom-promiserejectionevent-reason |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* `Promise`
* `PromiseRejectionEvent`
* `rejectionhandled`
* `unhandledrejection`


USBConnectionEvent() - Web API

USBConnectionEvent()
====================

Limited availability
--------------------

This feature is not Baseline because it does not work in some of the most widely-used browsers.

* Learn more
* See full compatibility
* Report feedback
**安全なコンテキスト用:** この機能は一部またはすべての対応しているブラウザーにおいて、安全なコンテキスト (HTTPS) でのみ利用できます。

**Experimental:** **これは実験的な機能です。**
本番で使用する前にブラウザー互換性一覧表をチェックしてください。

**`USBConnectionEvent()`** コンストラクターは、新しい `USBConnectionEvent` オブジェクトを生成します。このコンストラクターは通常使われず、このオブジェクトは USB デバイスの接続や切断に伴ってブラウザーが生成します。

構文
--

```js
new USBConnectionEvent(type, options)
```

### 引数

`type`

イベントの名前を表す文字列です。大文字と小文字は区別され、ブラウザーは `connect` または `disconnect` に設定します。

`options`

オブジェクトで、*`Event()` で定義されたプロパティに加えて* 以下のプロパティを持つことができます。

`device`

接続中または切断中の USB デバイスを表す `USBDevice` です。

### 返値

新しい `USBConnectionEvent` です。

仕様書
---

| Specification |
| --- |
| WebUSB API # dom-usbconnectionevent-usbconnectionevent |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


USBConnectionEvent.device - Web API

USBConnectionEvent.device
=========================

Limited availability
--------------------

This feature is not Baseline because it does not work in some of the most widely-used browsers.

* Learn more
* See full compatibility
* Report feedback
**安全なコンテキスト用:** この機能は一部またはすべての対応しているブラウザーにおいて、安全なコンテキスト (HTTPS) でのみ利用できます。

**Experimental:** **これは実験的な機能です。**
本番で使用する前にブラウザー互換性一覧表をチェックしてください。

`USBConnectionEvent` インターフェイスの読み取り専用プロパティ **`device`** は、接続中または切断中のデバイスを表す `USBDevice` オブジェクトを返します。

値
-

`USBDevice` オブジェクトです。

例
-

USB デバイスを接続すると、`connect` イベントが発火します。`event.device` を呼び出すと、接続された `USBDevice` が返ります。

```js
navigator.usb.addEventListener("connect", (event) => {
  console.log(event.device);
});
```

仕様書
---

| Specification |
| --- |
| WebUSB API # dom-usbconnectionevent-device |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


WindowClient: focus() メソッド - Web API

WindowClient: focus() メソッド
==========================

**`focus()`** は `WindowClient` インターフェイスのメソッドで、現在のクライアントにユーザー入力フォーカスを与え、既存の `WindowClient` に解決するプロミス (`Promise`) を返します。

構文
--

```js
focus()
```

### 引数

なし。

### 返値

既存の `WindowClient` に解決するプロミス (`Promise`)。

### 例外

`InvalidAccessError` `DOMException`

アプリのオリジンのウィンドウに一時的な有効化が行われていない場合、この例外でプロミスが拒否されます。

セキュリティ要件
--------

* アプリのオリジンの中で少なくとも 1 つのウィンドウで一時的な有効化が行われている必要があります。
例
-

```js
self.addEventListener("notificationclick", (event) => {
  console.log("On notification click: ", event.notification.tag);
  event.notification.close();

  // これは、クライアントが既に開いているかどうかを確認し、
  // 開いている場合にフォーカスを合わせます
  event.waitUntil(
    clients
      .matchAll({
        type: "window",
      })
      .then((clientList) => {
        for (const client of clientList) {
          if (client.url === "/" && "focus" in client) return client.focus();
        }
        if (clients.openWindow) return clients.openWindow("/");
      }),
  );
});
```

仕様書
---

| Specification |
| --- |
| Service Workers # client-focus |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


WindowClient: focused プロパティ - Web API

WindowClient: focused プロパティ
===========================

**`focused`** は `WindowClient` インターフェイスの読み取り専用プロパティで、現在のクライアントにフォーカスがあるかどうかを示す論理値です。

### 値

論理値です。

例
-

```js
self.addEventListener("notificationclick", (event) => {
  console.log("On notification click: ", event.notification.tag);
  event.notification.close();

  // これは、クライアントが既に開いているかどうかを確認し、
  // 開いている場合にフォーカスを合わせます
  event.waitUntil(
    clients
      .matchAll({
        type: "window",
      })
      .then((clientList) => {
        for (const client of clientList) {
          if (client.url === "/" && "focus" in client) {
            if (!client.focused) return client.focus();
          }
        }

        if (clients.openWindow) return clients.openWindow("/");
      }),
  );
});
```

仕様書
---

| Specification |
| --- |
| Service Workers # client-focused |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


WindowClient: visibilityState プロパティ - Web API

WindowClient: visibilityState プロパティ
===================================

**`visibilityState`** は `WindowClient` インターフェイスの読み取り専用プロパティで、現在のクライアントの可視性を示します。 この値は、`"hidden"`、`"visible"`、`"prerender"` のいずれかです。

値
-

文字列です（値については `Document.visibilityState` を参照）。

例
-

```js
event.waitUntil(
  clients
    .matchAll({
      type: "window",
    })
    .then((clientList) => {
      for (const client of clientList) {
        if (client.url === "/" && "focus" in client) {
          if (client.visibilityState === "hidden") return client.focus();
        }
      }
      if (clients.openWindow) {
        return clients.openWindow("/");
      }
    }),
);
```

仕様書
---

| Specification |
| --- |
| Service Workers # client-visibilitystate |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


WindowClient: navigate() メソッド - Web API

WindowClient: navigate() メソッド
=============================

**`navigate()`** は `WindowClient` インターフェイスのメソッドで、指定された URL を制御されたクライアントのページに読み込んでから、既存の `WindowClient` に解決されるプロミス (`Promise`) を返します。

構文
--

```js
navigate(url)
```

### 引数

`url`

移動先の場所。

### 返値

既存の `WindowClient` に解決されるプロミス (`Promise`)。

仕様書
---

| Specification |
| --- |
| Service Workers # client-navigate |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


PeriodicSyncManager: register() メソッド - Web API

PeriodicSyncManager: register() メソッド
====================================

**Experimental:** **これは実験的な機能です。**
本番で使用する前にブラウザー互換性一覧表をチェックしてください。

**`register()`** は `PeriodicSyncManager` インターフェイスのメソッドで、
 指定したタグとオプションでブラウザーに定期的な同期リクエストを登録します。登録が完了すると解決する `Promise` を返します。

構文
--

```js
register(tag, options)
```

### 引数

`tag`

固有の文字列の識別子です。

`options` 省略可

以下のオプションデータを含む `Object` です。

`minInterval`

定期的な同期を行う最小間隔時間をミリ秒単位で指定します。

### 返値

`undefined` で解決する `Promise` を返します。

### 例外

`InvalidStateError` `DOMException`

アクティブなサービスワーカー (`ServiceWorker`) が存在しない場合に返されます。

`NotAllowedError` `DOMException`

バックグラウンド定期同期のその権限が与えられていない場合に返されます。

`InvalidAccessError` `DOMException`

アクティブウィンドウがメインウィンドウでない場合（`auxiliary` や `top-level` の種類でない場合）に返されます。

例
-

以下の非同期関数は、閲覧コンテキストから最小 1 日間隔で定期バックグラウンド同期を登録するものです。

```js
async function registerPeriodicNewsCheck() {
  const registration = await navigator.serviceWorker.ready;
  try {
    await registration.periodicSync.register("fetch-news", {
      minInterval: 24 \* 60 \* 60 \* 1000,
    });
  } catch {
    console.log("Periodic Sync could not be registered!");
  }
}
```

仕様書
---

| Specification |
| --- |
| Web Periodic Background Synchronization # dom-periodicsyncmanager-register |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* Richer offline experiences with the Periodic Background Sync API
* A Periodic Background Sync demo app


PeriodicSyncManager: unregister() メソッド - Web API

PeriodicSyncManager: unregister() メソッド
======================================

**Experimental:** **これは実験的な機能です。**
本番で使用する前にブラウザー互換性一覧表をチェックしてください。

**`unregister()`** は `PeriodicSyncManager` インターフェイスのメソッドで、
 指定したタグに対応する定期同期のリクエストを登録解除し、登録解除が完了したときに解決する `Promise` を返します。

構文
--

```js
unregister(tag)
```

### 引数

tag

固有のバックグラウンド同期に関する固有の文字列の記述子です。

### 返値

解決された `Promise` です。

### 例外

なし。

例
-

次の例では、定期同期を削除して、バックグラウンドでの記事の同期を停止しています。

```js
navigator.serviceWorker.ready.then((registration) => {
  registration.periodicSync.unregister("get-latest-news");
});
```

仕様書
---

| Specification |
| --- |
| Web Periodic Background Synchronization # dom-periodicsyncmanager-unregister |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* Richer offline experiences with the Periodic Background Sync API
* A Periodic Background Sync demo app


PeriodicSyncManager: getTags() メソッド - Web API

PeriodicSyncManager: getTags() メソッド
===================================

**Experimental:** **これは実験的な機能です。**
本番で使用する前にブラウザー互換性一覧表をチェックしてください。

**`getTags()`** は `PeriodicSyncManager` インターフェイスのメソッドで、定期的な同期を行うために現在登録されているタグを表す `String` オブジェクトのリストで解決される `Promise` を返します。

構文
--

```js
getTags()
```

### 引数

なし。

### 返値

定期的な同期を行うために現在登録されているタグを表す `String` オブジェクトのリストで解決される `Promise` です。

### 例外

なし。

例
-

次の例では、`getTags()` メソッドを使用して、指定されたタグを持つ定期的な同期タスクが登録されているかどうかを調べています。

```js
navigator.serviceWorker.ready.then((registration) => {
  registration.periodicSync.getTags().then((tags) => {
    if (tags.includes("get-latest-news")) skipDownloadingLatestNewsOnPageLoad();
  });
});
```

`skipDownloadingLatestNewsOnPageLoad()` は開発者が定義した関数です。

仕様書
---

| Specification |
| --- |
| Web Periodic Background Synchronization # dom-periodicsyncmanager-gettags |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* Richer offline experiences with the Periodic Background Sync API
* A Periodic Background Sync demo app


BaseAudioContext.createAnalyser() - Web API

BaseAudioContext.createAnalyser()
=================================

`createAnalyser()` は `BaseAudioContext` インターフェイスのメソッドで、 `AnalyserNode` を作成します。これは音声の時間と周波数データを公開し、データの可視化を行います。

**メモ:** `AnalyserNode()` コンストラクターが `AnalyserNode` を生成するのに推奨される方法です。 AudioNode の作成を参照してください。

**メモ:** このノードの詳しい説明は、 `AnalyserNode` のページを参照してください。

構文
--

```js
createAnalyser();
```

### 引数

なし。

### 返値

`AnalyserNode` です。

例
-

次の例では、 AudioContext を使用して Analyser ノードを作成し、 requestAnimationFrame() を使用してタイムドメインのデータを繰り返し収集し、現在の音声入力の「オシロスコープ形式」の出力を描画する基本的な使用方法を示しています。より完全な応用例や情報については、 Voice-change-O-matic のデモをご覧ください（関連コードは、 app.js 128-205 行目をご覧ください）。

```js
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var analyser = audioCtx.createAnalyser();

  ...

analyser.fftSize = 2048;
var bufferLength = analyser.frequencyBinCount;
var dataArray = new Uint8Array(bufferLength);
analyser.getByteTimeDomainData(dataArray);

// 現在の音のオシロスコープのように描く

function draw() {

      drawVisual = requestAnimationFrame(draw);

      analyser.getByteTimeDomainData(dataArray);

      canvasCtx.fillStyle = 'rgb(200, 200, 200)';
      canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);

      canvasCtx.lineWidth = 2;
      canvasCtx.strokeStyle = 'rgb(0, 0, 0)';

      canvasCtx.beginPath();

      var sliceWidth = WIDTH \* 1.0 / bufferLength;
      var x = 0;

      for(var i = 0; i < bufferLength; i++) {

        var v = dataArray[i] / 128.0;
        var y = v \* HEIGHT/2;

        if(i === 0) {
          canvasCtx.moveTo(x, y);
        } else {
          canvasCtx.lineTo(x, y);
        }

        x += sliceWidth;
      }

      canvasCtx.lineTo(canvas.width, canvas.height/2);
      canvasCtx.stroke();
    };

    draw();
```

仕様書
---

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-createanalyser |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* ウェブオーディオ API の使用


BaseAudioContext.createChannelMerger() - Web API

BaseAudioContext.createChannelMerger()
======================================

`createChannelMerger()` は `BaseAudioContext` インターフェイスのメソッドで、 `ChannelMergerNode` を作成します。これは複数の音声ストリームのチャンネルを一つの音声ストリームに結合します。

**メモ:** `ChannelMergerNode()` (en-US) コンストラクターは `ChannelMergerNode` を作成するための推奨される方法です。 AudioNode の作成を参照してください。

構文
--

```js
createChannelMerger(numberOfInputs);
```

### 引数

numberOfInputs

出力ストリームに含まれる、入力オーディオストリームのチャンネル数です。この引数が指定されていない場合の既定値は 6 です。

### 返値

`ChannelMergerNode` です。

例
-

次の例では、ステレオトラック（例えば音楽）を分離し、左チャンネルと右チャンネルを別々に処理する方法を示しています。これを使うには、 `AudioNode.connect(AudioNode)` メソッドの第 2、第 3 引数を使って、接続元のチャンネルのインデックスと接続先のチャンネルのインデックスの両方を指定することが必要です。

```js
var ac = new AudioContext();
ac.decodeAudioData(someStereoBuffer, function (data) {
  var source = ac.createBufferSource();
  source.buffer = data;
  var splitter = ac.createChannelSplitter(2);
  source.connect(splitter);
  var merger = ac.createChannelMerger(2);

  // 左チャンネルのボリュームのみ小さくする
  var gainNode = ac.createGain();
  gainNode.gain.setValueAtTime(0.5, ac.currentTime);
  splitter.connect(gainNode, 0);

  // スプリッターをマージャーの 2 番目の入力に接続すると、チャンネルが効果的に交換され、
  // ステレオイメージが反転します。
  gainNode.connect(merger, 0, 1);
  splitter.connect(merger, 1, 0);

  var dest = ac.createMediaStreamDestination();

  // ChannelMergerNode を使っているので、ステレオの MediaStream が
  // できました。これでウェブオーディオグラフを WebRTC や MediaRecorder などに
  // パイプするのに使えます。
  merger.connect(dest);
});
```

仕様書
---

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-createchannelmerger |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* ウェブオーディオ API の使用


BaseAudioContext.createBufferSource() - Web API

BaseAudioContext.createBufferSource()
=====================================

`createBufferSource()` は `BaseAudioContext` インターフェイスのメソッドで、新しい `AudioBufferSourceNode` を作成するために使用します。これは `AudioBuffer` オブジェクトに含まれる音声データを再生するために使用されます。 `AudioBuffer` は `BaseAudioContext.createBuffer` で生成さされたり、音声トラックのデコードに成功したときに `BaseAudioContext.decodeAudioData` から返されたりします。

**メモ:** `AudioBufferSourceNode()` コンストラクターは、 `AudioBufferSourceNode` を作成するための推奨される方法です。 AudioNode の作成を参照してください。

構文
--

```js
createBufferSource();
```

### 引数

なし。

### 返値

`AudioBufferSourceNode` です。

例
-

この例では、 2 秒間のバッファーを作成してホワイトノイズで満たし、 `AudioBufferSourceNode` で再生しています。コメントでは、何が起こっているのかを簡単に説明しています。

**メモ:** コードをライブで実行したり、ソースを閲覧したりすることもできます。

```js
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var button = document.querySelector("button");
var pre = document.querySelector("pre");
var myScript = document.querySelector("script");

pre.innerHTML = myScript.innerHTML;

// ステレオ
var channels = 2;
// AudioContext のサンプリングレートで 2 秒間の
// 空のステレオバッファーを生成する
var frameCount = audioCtx.sampleRate \* 2.0;

var myArrayBuffer = audioCtx.createBuffer(
  channels,
  frameCount,
  audioCtx.sampleRate,
);

button.onclick = function () {
  // バッファにホワイトノイズを書き込む
  // -1.0 から 1.0 の間の乱数の値
  for (var channel = 0; channel < channels; channel++) {
    // 実際のデータの配列を得る
    var nowBuffering = myArrayBuffer.getChannelData(channel);
    for (var i = 0; i < frameCount; i++) {
      // Math.random() は [0; 1.0] である
      // 音声は [-1.0; 1.0] である必要がある
      nowBuffering[i] = Math.random() \* 2 - 1;
    }
  }

  // AudioBufferSourceNode を得る
  // これは AudioBuffer を再生するときに使う AudioNode である
  var source = audioCtx.createBufferSource();
  // AudioBufferSourceNode にバッファーを設定する
  source.buffer = myArrayBuffer;
  // AudioBufferSourceNode を出力先に接続すると
  // 音声が聞こえるようになる
  source.connect(audioCtx.destination);
  // 音源の再生を始める
  source.start();
};
```

仕様書
---

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-createbuffersource |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* ウェブオーディオ API の使用


BaseAudioContext.destination - Web API

BaseAudioContext.destination
============================

`destination` は `BaseAudioContext` のプロパティで、このコンテキストのすべての音声の最終的な出力先を表す `AudioDestinationNode` を返します。これは、端末のスピーカーのような、オーディオレンダリング機器と考えることができます。

値
-

`AudioDestinationNode` です。

例
-

**メモ:** 完全な実装の例は、 MDN GitHub リポジトリーの voice-change-o-matic などを参照してください。

```js
var AudioContext = window.AudioContext || window.webkitAudioContext;
var audioCtx = new AudioContext();
// 古い webkit/blink のブラウザーでは接頭辞が必要です

var oscillatorNode = audioCtx.createOscillator();
var gainNode = audioCtx.createGain();

oscillatorNode.connect(gainNode);
gainNode.connect(audioCtx.destination);
```

仕様書
---

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-destination |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* ウェブオーディオ API の使用


BaseAudioContext.createConvolver() - Web API

BaseAudioContext.createConvolver()
==================================

`createConvolver()` は `BaseAudioContext` インターフェイスのメソッドで、 `ConvolverNode` を生成します。これは一般に音声にリバーブ効果を適用するために使用されます。詳しくは畳み込み効果の仕様定義 を参照してください。

**メモ:** `ConvolverNode()` (en-US) コンストラクターは `ConvolverNode` を作成するための推奨される方法です。 AudioNode の作成を参照してください。

構文
--

```js
createConvolver();
```

### 引数

なし。

### 返値

`ConvolverNode` です。

例
-

次の例は、畳み込みノードを作成するための AudioContext の基本的な使用方法を示しています。大前提として、畳み込み効果を形成するアンビエンスとして使用するサウンドサンプル（*インパルスレスポンス*と呼ばれます）を含む AudioBuffer を作成し、それを畳み込みに適用します。下の例では、コンサートホールの観客の短いサンプルを使っているので、適用されるリバーブ効果はとても深く、エコーがかかっているようです。

応用例や情報については、 Voice-change-O-matic デモを参照してください（関連コードは app.js を参照してください）。

```js
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var convolver = audioCtx.createConvolver();

  ...

// XHR で畳み込みノードのための音声トラックを得る

var soundSource, concertHallBuffer;

ajaxRequest = new XMLHttpRequest();
ajaxRequest.open('GET', 'concert-crowd.ogg', true);
ajaxRequest.responseType = 'arraybuffer';

ajaxRequest.onload = function() {
  var audioData = ajaxRequest.response;
  audioCtx.decodeAudioData(audioData, function(buffer) {
      concertHallBuffer = buffer;
      soundSource = audioCtx.createBufferSource();
      soundSource.buffer = concertHallBuffer;
    }, function(e){"Error with decoding audio data" + e.err});
}

ajaxRequest.send();

  ...

convolver.buffer = concertHallBuffer;
```

仕様書
---

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-createconvolver |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* ウェブオーディオ API の使用


BaseAudioContext.sampleRate - Web API

BaseAudioContext.sampleRate
===========================

`sampleRate` は `BaseAudioContext` インターフェイスのプロパティで、この音声コンテキスト内のすべてのノードが使用するサンプリングレートを、1 秒あたりのサンプル数で表した浮動小数点数を返すものです。この制限は、サンプリングレートコンバーターに対応していないことを意味します。

値
-

音声コンテキストのサンプリングレートを、 1 秒当たりのサンプル数で示す浮動小数点数です。

例
-

**メモ:** 完全なウェブオーディオの実装の例は、GitHub の MDN リポジトリー にあるいずれかのウェブオーディオのデモを参照してください。
 ブラウザーのコンソールで `audioCtx.sampleRate` と入力してみてください。

```js
const audioCtx = new AudioContext();
// 古い webkit/blink のブラウザーでは接頭辞が必要です

// …

console.log(audioCtx.sampleRate);
```

仕様書
---

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-samplerate |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* ウェブオーディオ API の使用


BaseAudioContext.createScriptProcessor() - Web API

BaseAudioContext.createScriptProcessor()
========================================

**非推奨:** この機能は非推奨になりました。まだ対応しているブラウザーがあるかもしれませんが、すでに関連するウェブ標準から削除されているか、削除の手続き中であるか、互換性のためだけに残されている可能性があります。使用を避け、できれば既存のコードは更新してください。このページの下部にある互換性一覧表を見て判断してください。この機能は突然動作しなくなる可能性があることに注意してください。

`createScriptProcessor()` は `BaseAudioContext` インターフェイスのメソッドで、直接音声処理に用いられる `ScriptProcessorNode` を生成します。

**メモ:** この機能は AudioWorklet と `AudioWorkletNode` インターフェイスに置き換えられました。

構文
--

```js
createScriptProcessor(
  bufferSize,
  numberOfInputChannels,
  numberOfOutputChannels,
);
```

### 引数

`bufferSize`

サンプルフレーム単位でのバッファーサイズ。指定する場合、 bufferSize は 256, 512, 1024, 2048, 4096, 8192, 16384 の値のいずれかでなければなりません。これが渡されなかった場合、あるいは値が 0 の場合、実装は与えられた環境に最適なバッファーサイズを選択し、それはノードの寿命を通じて 2 の一定乗となります。

この値は、`audioprocess` イベントが配信される頻度と、各呼び出しで処理される必要があるサンプルフレームの数を制御します。 `bufferSize` の値を小さくすると、遅延は小さく（良く）なります。音声が中断したりグリッチを避けるためには、より高い値が必要です。作者はこのバッファーサイズを指定せず、遅延と音質のバランスをとるために、実装が適切なバッファーサイズを選択できるようにすることをお勧めします。

`numberOfInputChannels`

整数で、このノードの入力のチャンネル数を指定します。既定値は 2 です。

`numberOfOutputChannels`

整数で、このノードの出力のチャンネル数を指定します。既定値は 2 です。

**警告:** Webkit は現在（バージョン 31）、このメソッドを呼び出すときに有効な `bufferSize` を渡すことを要求しています。

**メモ:** numberOfInputChannels`と`numberOfOutputChannels` の両方が 0 にするのは無効です。

### 返値

`ScriptProcessorNode` です。

例
-

次の例は、 `AudioContext.decodeAudioData()` によって読み込んだトラックを、入力トラック（バッファー）のそれぞれの音声サンプルにホワイトノイズを加えて処理し `AudioDestinationNode` によって再生する `ScriptProcessorNode` の基本的な使用方法を示しています。各チャンネルと各サンプルフレームに対して、`scriptNode.onaudioprocess`関数は関連する `audioProcessingEvent` を受け取り、それを使って入力バッファの各チャンネルと各チャンネルの各サンプルを通してループし、少量のホワイトノイズを追加してからその結果を各ケースで出力サンプルとしてセットします。

**メモ:** 完全な動作する例については、 GitHub の script-processor-node リポジトリを参照してください（ソースコードも見てください）。

```js
var myScript = document.querySelector("script");
var myPre = document.querySelector("pre");
var playButton = document.querySelector("button");

// Create AudioContext and buffer source
var audioCtx = new AudioContext();
source = audioCtx.createBufferSource();

// Create a ScriptProcessorNode with a bufferSize of 4096 and a single input and output channel
var scriptNode = audioCtx.createScriptProcessor(4096, 1, 1);
console.log(scriptNode.bufferSize);

// load in an audio track via XHR and decodeAudioData

function getData() {
  request = new XMLHttpRequest();
  request.open("GET", "viper.ogg", true);
  request.responseType = "arraybuffer";
  request.onload = function () {
    var audioData = request.response;

    audioCtx.decodeAudioData(
      audioData,
      function (buffer) {
        myBuffer = buffer;
        source.buffer = myBuffer;
      },
      function (e) {
        "Error with decoding audio data" + e.err;
      },
    );
  };
  request.send();
}

// Give the node a function to process audio events
scriptNode.onaudioprocess = function (audioProcessingEvent) {
  // The input buffer is the song we loaded earlier
  var inputBuffer = audioProcessingEvent.inputBuffer;

  // The output buffer contains the samples that will be modified and played
  var outputBuffer = audioProcessingEvent.outputBuffer;

  // Loop through the output channels (in this case there is only one)
  for (var channel = 0; channel < outputBuffer.numberOfChannels; channel++) {
    var inputData = inputBuffer.getChannelData(channel);
    var outputData = outputBuffer.getChannelData(channel);

    // Loop through the 4096 samples
    for (var sample = 0; sample < inputBuffer.length; sample++) {
      // make output equal to the same as the input
      outputData[sample] = inputData[sample];

      // add noise to each output sample
      outputData[sample] += (Math.random() \* 2 - 1) \* 0.2;
    }
  }
};

getData();

// wire up play button
playButton.onclick = function () {
  source.connect(scriptNode);
  scriptNode.connect(audioCtx.destination);
  source.start();
};

// When the buffer source stops playing, disconnect everything
source.onended = function () {
  source.disconnect(scriptNode);
  scriptNode.disconnect(audioCtx.destination);
};
```

仕様書
---

2014 年 8 月 29 日のウェブオーディオ API 仕様書公開以降、この機能は非推奨となりました。標準化の目処が立たなくなりました。

これは AudioWorklet と `AudioWorkletNode` インターフェイスに置き換えられました。

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* ウェブオーディオ API の使用


BaseAudioContext.decodeAudioData() - Web API

BaseAudioContext.decodeAudioData()
==================================

`decodeAudioData()` は `BaseAudioContext` のメソッドで、 `ArrayBuffer` に書き込まれた音声ファイルデータを非同期にデコードするために使用されます。この場合、 `ArrayBuffer` は `XMLHttpRequest` と `FileReader` から読み込まれます。デコードされた `AudioBuffer` は `AudioContext` のサンプリングレートにリサンプリングされ、コールバックやプロミスに渡されます。

この方法は、オーディオトラックからウェブオーディオ API 用のオーディオソースを作成する際に推奨される方法です。この方法は、音声ファイルの断片的なデータではなく、完全なファイルデータに対してのみ動作します。

構文
--

```js
// 古いコールバックの構文:
decodeAudioData(arrayBuffer, successCallback);
decodeAudioData(arrayBuffer, successCallback, errorCallback);

// 新しいプロミスベースの構文:
decodeAudioData(arrayBuffer);
```

### 引数

*arrayBuffer*

デコードする音声データが入った ArrayBuffer です。通常は`XMLHttpRequest`, `fetch()`, `FileReader` から取得します。

*successCallback*

デコードが完了すると呼び出されるコールバック関数です。このコールバックの引数は 1 つで、 `AudioBuffer` であり *decodedData* （デコードされた PCM 音声データ）を表します。通常は、デコードされたデータを `AudioBufferSourceNode` に入れて、そこから再生したり、好きなように操作したりすることができます。

*errorCallback* 省略可

任意のエラーコールバックで、音声データのデコードでエラーが発生すると呼び出されます。

### 返値

なし、または *decodedData* で履行される `Promise` オブジェクトです。

例
-

ここでは最初に古いコールバックベースのシステムを、次に新しいプロミスベースの構文を取り上げます。

### 古いコールバックベースの構文

この例では、 `getData()` 関数は XHR を使用して音声トラックを読み込み、リクエストの `responseType` を `arraybuffer` に設定して、レスポンスとして配列バッファーを返すようにして、それを `audioData` 変数に格納しています。それからこのバッファーを `decodeAudioData()` 関数に渡します。成功したコールバックは、デコードに成功した PCM データを受け取り、 `AudioContext.createBufferSource()` で作成した `AudioBufferSourceNode` に入れ、ソースを `AudioContext.destination` に接続してループするように設定します。

ボタンは単に `getData()` を実行して、それぞれトラックの読み込みと再生、停止を行うだけです。ソースの `stop()` メソッドが呼ばれると、ソースは消滅します。

**メモ:** 例をライブで実行 （またはソースを閲覧することができます。）

```js
// 変数の定義

var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var source;

var pre = document.querySelector("pre");
var myScript = document.querySelector("script");
var play = document.querySelector(".play");
var stop = document.querySelector(".stop");

// 音声トラックの読み込みには XHR を使い、
// decodeAudioData でデコードし、バッファーに格納する
// そして、そのバッファーを source に設定する

function getData() {
  source = audioCtx.createBufferSource();
  var request = new XMLHttpRequest();

  request.open("GET", "viper.ogg", true);

  request.responseType = "arraybuffer";

  request.onload = function () {
    var audioData = request.response;

    audioCtx.decodeAudioData(
      audioData,
      function (buffer) {
        source.buffer = buffer;

        source.connect(audioCtx.destination);
        source.loop = true;
      },

      function (e) {
        console.log("Error with decoding audio data" + e.err);
      },
    );
  };

  request.send();
}

// 音声の停止と再生を行うボタン

play.onclick = function () {
  getData();
  source.start(0);
  play.setAttribute("disabled", "disabled");
};

stop.onclick = function () {
  source.stop(0);
  play.removeAttribute("disabled");
};

// pre要素にスクリプトを設定する

pre.innerHTML = myScript.innerHTML;
```

### 新しいプロミスベースの構文

```js
ctx.decodeAudioData(audioData).then(function (decodedData) {
  // デコードしたデータをここで使う
});
```

仕様書
---

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-decodeaudiodata |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* ウェブオーディオ API の使用


BaseAudioContext.createPanner() - Web API

BaseAudioContext.createPanner()
===============================

`createPanner()` は `BaseAudioContext` インターフェイスのメソッドで、新しい `PannerNode` を作成するために使用されます。これは、入力される音声ストリームを三次元空間で空間化するのに使われます。

パナーノードは AudioContext の `AudioListener` （`AudioContext.listener` 属性によって定義）に関連して空間化され、音声を聞く人の位置と向きを表現します。

**メモ:** `PannerNode()` (en-US) コンストラクターは `PannerNode` を作成するための推奨される方法です。 AudioNode の作成を参照してください。

構文
--

```js
createPanner();
```

### 引数

なし。

### 返値

`PannerNode` です。

例
-

次の例では、 `createPanner()` メソッドと `AudioListener` と `PannerNode` を使って音声の空間化を制御する例を見ることができます。一般に、音声リスナーとパンナー（音源）が最初に占める三次元空間の位置を定義し、アプリケーションが使用されるたびに、これらの一方または両方の位置を更新します。たとえば、ゲームの世界でキャラクターを動かしていて、キャラクターがステレオなどの音楽プレーヤーに近づいたり遠ざかったりすると、音声の配信がリアルに変化するようにしたい場合があります。この例では `moveRight()`, `moveLeft()` などの関数でこれを制御し、 `PositionPanner()` 関数でパンナーの位置に新しい値を設定しているのが分かると思います。

完全な実装を見るには、私たちの panner-node の例（ソースコードを表示）をチェックしてください。このデモでは、 2.5 次元の「金属の部屋」に移動し、ラジカセでトラックを再生し、ラジカセの周りを歩いて音の変化を見ることができます。このデモでは、ラジカセの周りを歩いて、音がどのように変化するかを見ることができます。

なお、機能検出の使用方法に注意してください。ブラウザーが位置の設定などを行うために、新しいプロパティ値（例えば `AudioListener.forwardX` (en-US)）に対応している場合はそれを利用し、古いメソッドには対応しているが新しいプロパティには対応していない場合には、古いメソッド（例えば `AudioListener.setOrientation()` (en-US)）を利用しています。

```js
// set up listener and panner position information
var WIDTH = window.innerWidth;
var HEIGHT = window.innerHeight;

var xPos = Math.floor(WIDTH / 2);
var yPos = Math.floor(HEIGHT / 2);
var zPos = 295;

// define other variables

var AudioContext = window.AudioContext || window.webkitAudioContext;
var audioCtx = new AudioContext();

var panner = audioCtx.createPanner();
panner.panningModel = "HRTF";
panner.distanceModel = "inverse";
panner.refDistance = 1;
panner.maxDistance = 10000;
panner.rolloffFactor = 1;
panner.coneInnerAngle = 360;
panner.coneOuterAngle = 0;
panner.coneOuterGain = 0;

if (panner.orientationX) {
  panner.orientationX.setValueAtTime(1, audioCtx.currentTime);
  panner.orientationY.setValueAtTime(0, audioCtx.currentTime);
  panner.orientationZ.setValueAtTime(0, audioCtx.currentTime);
} else {
  panner.setOrientation(1, 0, 0);
}

var listener = audioCtx.listener;

if (listener.forwardX) {
  listener.forwardX.setValueAtTime(0, audioCtx.currentTime);
  listener.forwardY.setValueAtTime(0, audioCtx.currentTime);
  listener.forwardZ.setValueAtTime(-1, audioCtx.currentTime);
  listener.upX.setValueAtTime(0, audioCtx.currentTime);
  listener.upY.setValueAtTime(1, audioCtx.currentTime);
  listener.upZ.setValueAtTime(0, audioCtx.currentTime);
} else {
  listener.setOrientation(0, 0, -1, 0, 1, 0);
}

var source;

var play = document.querySelector(".play");
var stop = document.querySelector(".stop");

var boomBox = document.querySelector(".boom-box");

var listenerData = document.querySelector(".listener-data");
var pannerData = document.querySelector(".panner-data");

leftBound = -xPos + 50;
rightBound = xPos - 50;

xIterator = WIDTH / 150;

// listener will always be in the same place for this demo

if (listener.positionX) {
  listener.positionX.setValueAtTime(xPos, audioCtx.currentTime);
  listener.positionY.setValueAtTime(yPos, audioCtx.currentTime);
  listener.positionZ.setValueAtTime(300, audioCtx.currentTime);
} else {
  listener.setPosition(xPos, yPos, 300);
}

listenerData.textContent = `Listener data: X ${xPos} Y ${yPos} Z 300`;

// panner will move as the boombox graphic moves around on the screen
function positionPanner() {
  if (panner.positionX) {
    panner.positionX.setValueAtTime(xPos, audioCtx.currentTime);
    panner.positionY.setValueAtTime(yPos, audioCtx.currentTime);
    panner.positionZ.setValueAtTime(zPos, audioCtx.currentTime);
  } else {
    panner.setPosition(xPos, yPos, zPos);
  }
  pannerData.textContent = `Panner data: X ${xPos} Y ${yPos} Z ${zPos}`;
}
```

**メモ:** リスナーやパンナーの位置値をどのように設定すれば、画面上のビジュアルに適したサウンドになるかという点では、少し面倒な計算が必要となりますが、すこしやれば慣れる類のものです。

仕様書
---

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-createpanner |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* ウェブオーディオ API の使用


BaseAudioContext.state - Web API

BaseAudioContext.state
======================

`state` は `BaseAudioContext` インターフェイスの読み取り専用プロパティで、現在の `AudioContext` の状態を返します。

値
-

文字列です。取りうる値は以下の通りです。

* `suspended`: 音声コンテキストは（`AudioContext.suspend()` によって）一時停止中
* `running`: 音声コンテキストは通常動作中
* `closed`: 音声コンテキストは（`AudioContext.close()` によって）閉じられた
例
-

### 状態の変化の扱い

次のスニペットは AudioContext states のデモ（ライブ実行で確認）の一部です。 `onstatechange` ハンドラーは、状態が変わるたびにコンソールにログを出力するために使われています。

```js
audioCtx.onstatechange = function () {
  console.log(audioCtx.state);
};
```

### iOS Safari における一時停止した再生の再開の状態

iOS の Safari では、ユーザーが（タブの切り替え、ブラウザの最小化、画面のオフなどで）ページを離れると、音声コンテキストの状態が "interrupted" に変わり、再開させる必要があります。例えば次のようにします。

```js
function play() {
  if (audioCtx.state === "interrupted") {
    audioCtx.resume().then(() => play());
    return;
  }
  // ... rest of the play() function
}
```

仕様書
---

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-state |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* ウェブオーディオ API の使用


BaseAudioContext.createStereoPanner() - Web API

BaseAudioContext.createStereoPanner()
=====================================

`createStereoPanner()` は `BaseAudioContext` インターフェイスのメソッドで、音源にステレオパンニングを適用する`StereoPannerNode`を生成します。
 入力された音声ストリームは、低コストのパンニングアルゴリズムで位置が決められます。

**メモ:** `StereoPannerNode()` (en-US) コンストラクターは `StereoPannerNode` を作成するための推奨される方法です。 AudioNode の作成を参照してください。

構文
--

```js
createStereoPanner();
```

### 引数

なし。

### 返値

`StereoPannerNode` を返します。

例
-

StereoPannerNode の例（ソースコードを参照）の HTML では、単純な `<audio>` 要素と、パン値を増減するスライダー `<input>` を用意しています。 JavaScript では、`MediaElementAudioSourceNode` と `StereoPannerNode` を作成し、`connect()` メソッドを用いて両者をつなげます。そして、`oninput` イベントハンドラーを使って `StereoPannerNode.pan` (en-US) パラメータの値を変更し、スライダーが動いたときにパン値の表示を更新しています。

音楽再生中にスライダーを左右に動かすと、出力の左右のスピーカーにそれぞれ音楽がパンされます。

```js
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var myAudio = document.querySelector("audio");

var panControl = document.querySelector(".panning-control");
var panValue = document.querySelector(".panning-value");

pre.innerHTML = myScript.innerHTML;

// MediaElementAudioSourceNode を生成し、
// そこに HTMLMediaElementを 入れる
var source = audioCtx.createMediaElementSource(myAudio);

// ステレオパンナーを生成する
var panNode = audioCtx.createStereoPanner();

// イベントハンドラー関数で、スライダーが動いたとき
// 左右のパンの値を左右する

panControl.oninput = function () {
  panNode.pan.setValueAtTime(panControl.value, audioCtx.currentTime);
  panValue.innerHTML = panControl.value;
};

// MediaElementAudioSourceNode を panNode に、 panNode を
// 出力先に接続し、音楽を再生してコントロールでパンを調整
// できるようにします。
source.connect(panNode);
panNode.connect(audioCtx.destination);
```

仕様書
---

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-createstereopanner |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* ウェブオーディオ API の使用


BaseAudioContext.createDelay() - Web API

BaseAudioContext.createDelay()
==============================

`createDelay()` は `BaseAudioContext` インターフェイスのメソッドで、 `DelayNode` を生成します。これは、入力される音声信号を一定時間遅らせるために使用します。

**メモ:** `DelayNode()` (en-US) コンストラクターは `DelayNode` を作成するための推奨される方法です。 AudioNode の作成を参照してください。

構文
--

```js
createDelay(maxDelayTime);
```

### 引数

`maxDelayTime` 省略可

音声信号を遅延させることができる最大時間（秒）を指定します。 180 秒未満である必要があり、指定しない場合は 1 秒が既定値となります。

### 返値

`DelayNode` です。既定の `DelayNode.delayTime` は 0 秒です。

例
-

3 つの異なるサンプルを一定のループで再生する簡単な例を作成しました。 create-delay をご覧ください（ソースコードを閲覧することもできます）。再生ボタンを押しただけでは、ループはすぐに始まります。スライダーを右にスライドさせてから再生ボタンを押すと、遅延が導入され、ループする音は短い時間だけ再生を開始しません。

```js
var AudioContext = window.AudioContext || window.webkitAudioContext;
var audioCtx = new AudioContext();

var synthDelay = audioCtx.createDelay(5.0);

  ...

var synthSource;

playSynth.onclick = function() {
  synthSource = audioCtx.createBufferSource();
  synthSource.buffer = buffers[2];
  synthSource.loop = true;
  synthSource.start();
  synthSource.connect(synthDelay);
  synthDelay.connect(destination);
  this.setAttribute('disabled', 'disabled');
}

stopSynth.onclick = function() {
  synthSource.disconnect(synthDelay);
  synthDelay.disconnect(destination);
  synthSource.stop();
  playSynth.removeAttribute('disabled');
}

...

var delay1;
rangeSynth.oninput = function() {
  delay1 = rangeSynth.value;
  synthDelay.delayTime.setValueAtTime(delay1, audioCtx.currentTime);
}
```

仕様書
---

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-createdelay |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* ウェブオーディオ API の使用


BaseAudioContext.listener - Web API

BaseAudioContext.listener
=========================

`listener` は `BaseAudioContext` インターフェイスのプロパティで、三次元音声空間処理を実装するために使う `AudioListener` オブジェクトを返します。

値
-

`AudioListener` オブジェクトです。

例
-

**メモ:** 完全な三次元音声空間処理の例は、 panner-node のデモを参照してください。

```js
const audioCtx = new AudioContext();
// 古い webkit/blink のブラウザーでは接頭辞が必要です

// …

const myListener = audioCtx.listener;
```

仕様書
---

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-listener |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* ウェブオーディオ API の使用


BaseAudioContext.createGain() - Web API

BaseAudioContext.createGain()
=============================

`createGain()` は `BaseAudioContext` インターフェイスのメソッドで、 `GainNode` を生成します。これは、音声グラフの全体的なゲイン（音量）を調整するのに使用します。

**メモ:** `GainNode()` コンストラクターは `GainNode` を作成するための推奨される方法です。 AudioNode の作成を参照してください。

構文
--

```js
createGain();
```

### 引数

なし。

### 返値

1 つ以上の音声ソースを入力とし、ノードの `GainNode` で指定されたレベルにゲイン（音量）が調整された音声を出力する `GainNode.gain` です。 a-rate 引数で指定された音量にゲイン調整された音声を出力します。

例
-

次の例は `AudioContext` を使って `GainNode` を作成し、Mute ボタンをクリックしたときに `gain` プロパティの値を変更して音声をミュートしたりミュート解除したりする基本的な使用方法を示しています。

以下のスニペットはそのままでは動作しません。完全な動作例については、 Voice-change-O-matic（ソースを閲覧）デモをご覧ください。

```html
<div>
  <button class="mute">Mute button</button>
</div>
```

```js
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var gainNode = audioCtx.createGain();
var mute = document.querySelector('.mute');
var source;

if (navigator.mediaDevices.getUserMedia) {
 navigator.mediaDevices.getUserMedia (
   // constraints - only audio needed for this app
   {
     audio: true
   },

   // Success callback
   function(stream) {
     source = audioCtx.createMediaStreamSource(stream);

   },

   // Error callback
   function(err) {
     console.log('The following gUM error occurred: ' + err);
   }
  );
} else {
   console.log('getUserMedia not supported on your browser!');
}

source.connect(gainNode);
gainNode.connect(audioCtx.destination);

  ...

mute.onclick = voiceMute;

function voiceMute() {
  if(mute.id == "") {
    // 0 means mute. If you still hear something, make sure you haven't
    // connected your source into the output in addition to using the GainNode.
    gainNode.gain.setValueAtTime(0, audioCtx.currentTime);
    mute.id = "activated";
    mute.textContent = "Unmute";
  } else {
    gainNode.gain.setValueAtTime(1, audioCtx.currentTime);
    mute.id = "";
    mute.textContent = "Mute";
  }
}
```

仕様書
---

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-creategain |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* ウェブオーディオ API の使用


BaseAudioContext.createWaveShaper() - Web API

BaseAudioContext.createWaveShaper()
===================================

`createWaveShaper()` は `BaseAudioContext` インターフェイスのメソッドで、 `WaveShaperNode` (en-US) を生成します。これは非線形な歪みを表現します。オーディオに歪み効果を適用するために使用されます。

**メモ:** `WaveShaperNode()` (en-US) コンストラクターは `WaveShaperNode` (en-US) を作成するための推奨される方法です。 AudioNode の作成を参照してください。

構文
--

```js
createWaveShaper();
```

### 引数

なし。

### 返値

`WaveShaperNode` (en-US) です。

例
-

以下の例では、 AudioContext を使用して波形整形ノードを作成する基本的な使い方を示しています。応用例や情報については、 Voice-change-O-matic のデモを参照してください（関連コードは app.js を参照してください）。

**メモ:** シグモイド関数は、その自然な特性から、歪みカーブとしてよく使われます。例えば、その S 字形状は、より滑らかなサウンドを作り出すのに役立ちます。 Stack Overflow に、以下のような歪み曲線のコードが掲載されていました。

```js
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var distortion = audioCtx.createWaveShaper();

  ...

function makeDistortionCurve(amount) {
  var k = typeof amount === 'number' ? amount : 50,
    n_samples = 44100,
    curve = new Float32Array(n_samples),
    deg = Math.PI / 180,
    i = 0,
    x;
  for ( ; i < n_samples; ++i ) {
    x = i \* 2 / n_samples - 1;
    curve[i] = ( 3 + k ) \* x \* 20 \* deg / ( Math.PI + k \* Math.abs(x) );
  }
  return curve;
};

  ...

distortion.curve = makeDistortionCurve(400);
distortion.oversample = '4x';
```

仕様書
---

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-createwaveshaper |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* ウェブオーディオ API の使用


BaseAudioContext.createPeriodicWave() - Web API

BaseAudioContext.createPeriodicWave()
=====================================

`createPeriodicWave()` は `AudioContext` インターフェイスのメソッドで、周期的な波形を定義するために使われる `PeriodicWave` を生成します。これは `OscillatorNode` の出力を決めるために使われます。

構文
--

```js
createPeriodicWave(real, imag);
createPeriodicWave(real, imag, constraints);
```

### 引数

`real`

余弦項（従来は A 項）の配列。

`imag`

正弦項（従来は B 項）の配列。

配列 `real` と `imag` は同じ長さでなければならず、そうでない場合はエラーが発生します。

`constraints` 省略可

正規化を無効にするかどうか（指定しない場合、正規化は既定で有効）を指定する辞書オブジェクトです。 1 つのプロパティを持ちます。

* `disableNormalization`: `true` に設定すると、周期的な波の正規化を無効にします。既定値は `false` です。

**メモ:** 正規化した場合、結果の波は最大絶対ピーク値が 1 になります。

### 返値

`PeriodicWave` です。

例
-

次の例は、 `createPeriodicWave()` を使用して単純な正弦波を含む `PeriodicWave` オブジェクトを作成する方法を示しています。

```js
var real = new Float32Array(2);
var imag = new Float32Array(2);
var ac = new AudioContext();
var osc = ac.createOscillator();

real[0] = 0;
imag[0] = 0;
real[1] = 1;
imag[1] = 0;

var wave = ac.createPeriodicWave(real, imag, { disableNormalization: true });

osc.setPeriodicWave(wave);

osc.connect(ac.destination);

osc.start();
osc.stop(2);
```

これは、基本音だけを含む音は、定義上、正弦波であるため、うまくいきます。

ここでは、 2 つの値を持つ `PeriodicWave` を作成します。最初の値は DC オフセットで、これはオシレーターを開始する値です。ここでは、曲線を [-1.0; 1.0] 範囲の中央で開始したいので、 0 が適しています。

2 つ目以降の値は正弦波と余弦波成分です。時間領域の値から周波数領域の値を得る、フーリエ変換の結果と考えることができます。ここでは、`createPeriodicWave()`で、周波数を指定すると、ブラウザーは逆フーリエ変換を行い、発振器の周波数の時間領域のバッファーを取得します。ここでは、基本音にフルボリューム（1.0）の 1 成分のみを設定しているので、正弦波が得られます。

フーリエ変換の係数は昇順で与えなければならず（すなわち、

(

a
+
b
i

)

e
i

,

(

c
+
d
i

)

e

2
i

,

(

f
+
g
i

)

e

3
i

 \left(a+bi\right)e^{i} , \left(c+di\right)e^{2i} ,
 \left(f+gi\right)e^{3i}

など）、正でも負でも構いません。このような係数を手動で得る簡単な方法は（ベストではありませんが）、グラフ計算機を使うことです。

仕様書
---

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-createperiodicwave |

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* ウェブオーディオ API の使用