「動画」だけでなく、すべての要素でピクチャー イン ピクチャーで使用可能

François Beaufort
François Beaufort

対応ブラウザ

  • Chrome: 116。
  • Edge: 116。
  • Firefox: サポートされていません。
  • Safari: サポートされていません。

ソース

Document Picture-in-Picture API を使用すると、任意の HTML コンテンツを配置できる常に最前面のウィンドウを開くことができます。これは、HTML の <video> 要素のみをピクチャー イン ピクチャー ウィンドウに配置できるようにする既存の <video> 用 Picture-in-Picture API を拡張します。

Document Picture-in-Picture API のピクチャー イン ピクチャー ウィンドウは、window.open() で開く空の同じオリジンのウィンドウに似ていますが、いくつかの違いがあります。

  • ピクチャー イン ピクチャー ウィンドウは、他のウィンドウの上にフローティング表示されます。
  • ピクチャー イン ピクチャー ウィンドウが開始ウィンドウより長くなることはありません。
  • ピクチャー イン ピクチャー ウィンドウを操作できない。
  • ピクチャー イン ピクチャー ウィンドウの位置は、ウェブサイトでは設定できません。
Sintel の予告編動画を再生しているピクチャー イン ピクチャー ウィンドウ。
Document Picture-in-Picture API で作成されたピクチャー イン ピクチャー ウィンドウ(デモ)。

現在のステータス

ステップ ステータス
1. 説明を作成する 完了
2. 仕様の初期ドラフトを作成する 作成中
3. フィードバックを収集してデザインを反復する 作成中
4. オリジン トライアル 完了
5. リリース 完了(パソコン)

ユースケース

カスタム動画プレーヤー

ウェブサイトでは、既存の <video> 用の Picture-in-Picture API を使用してピクチャー イン ピクチャー動画エクスペリエンスを提供できますが、機能は非常に限られています。既存のピクチャー イン ピクチャー ウィンドウは入力が少なく、スタイル設定機能に制限があります。完全なドキュメント イン ピクチャーで、ウェブサイトでカスタム コントロールと入力機能(字幕、再生リスト、タイムスクラバー、動画に高評価と低評価を付けるなど)を提供して、ユーザーのピクチャー イン ピクチャー動画のエクスペリエンスを向上させることができます。

ビデオ会議

ビデオ会議中に、さまざまな理由(別のタブを会議に表示する、マルチタスクを行うなど)でブラウザのタブを閉じながら、会議を表示したいというニーズはよくあります。これは、ピクチャー イン ピクチャーの典型的なユースケースです。繰り返しになりますが、ビデオ会議ウェブサイトが <video> の Picture-in-Picture API を介して提供できる現在のエクスペリエンスは、スタイルと入力が制限されています。ウェブサイトがドキュメント全体をピクチャー イン ピクチャーで表示できる場合、キャンバス ハックに頼ることなく、複数の動画ストリームを 1 つの PiP ウィンドウに簡単に結合できます。また、メッセージの送信、他のユーザーのミュート、挙手などのカスタム コントロールも提供できます。

仕事効率化

調査によると、ユーザーはウェブ上で生産性を高めるための方法をさらに必要としています。ピクチャー イン ピクチャーでドキュメントを作成すると、ウェブアプリでより多くのことを柔軟に処理できるようになります。テキスト編集、メモ作成、タスクリスト、メッセージング、チャット、デザイン&開発ツールなど、ウェブアプリのコンテンツにいつでもアクセスできます。

インターフェース

プロパティ

documentPictureInPicture.window
現在のピクチャー イン ピクチャー ウィンドウを返します(存在する場合)。それ以外の場合は、null を返します。

メソッド

documentPictureInPicture.requestWindow(options)

ピクチャー イン ピクチャー ウィンドウが開かれたときに解決される Promise を返します。ユーザー操作なしで呼び出されると、Promise は拒否されます。options 辞書には、次のメンバーを任意で含めることができます。

width
ピクチャー イン ピクチャー ウィンドウの初期幅を設定します。
height
ピクチャー イン ピクチャー ウィンドウの初期の高さを設定します。
disallowReturnToOpener
true の場合、ピクチャー イン ピクチャー ウィンドウの [タブに戻る] ボタンを非表示にします。デフォルトは false です。
preferInitialWindowPlacement
ピクチャー イン ピクチャー ウィンドウをデフォルトの位置とサイズ(true の場合)で開きます。デフォルトは false です。

イベント

documentPictureInPicture.onenter
ピクチャー イン ピクチャー ウィンドウが開いたときに documentPictureInPicture で発生します。

次の HTML は、カスタム動画プレーヤーとボタン要素を設定して、ピクチャーインピクチャー ウィンドウで動画プレーヤーを開きます。

<div id="playerContainer">
  <div id="player">
    <video id="video"></video>
  </div>
</div>
<button id="pipButton">Open Picture-in-Picture window</button>

ピクチャー イン ピクチャー ウィンドウを開く

次の JavaScript は、ユーザーがボタンをクリックして空白のピクチャー イン ピクチャー ウィンドウを開くと、documentPictureInPicture.requestWindow() を呼び出します。返される Promise は、ピクチャー イン ピクチャー ウィンドウの JavaScript オブジェクトで解決されます。append() を使用して、動画プレーヤーがそのウィンドウに移動します。

pipButton.addEventListener('click', async () => {
  const player = document.querySelector("#player");

  // Open a Picture-in-Picture window.
  const pipWindow = await documentPictureInPicture.requestWindow();

  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);
});

ピクチャー イン ピクチャー ウィンドウのサイズを設定する

ピクチャー イン ピクチャー ウィンドウのサイズを設定するには、documentPictureInPicture.requestWindow()width オプションと height オプションを、目的のピクチャー イン ピクチャー ウィンドウのサイズに設定します。オプション値がユーザー フレンドリーなウィンドウ サイズに収まらないほど大きすぎるか小さすぎる場合は、Chrome によって値がクランプされることがあります。

pipButton.addEventListener("click", async () => {
  const player = document.querySelector("#player");

  // Open a Picture-in-Picture window whose size is
  // the same as the player's.
  const pipWindow = await documentPictureInPicture.requestWindow({
    width: player.clientWidth,
    height: player.clientHeight,
  });

  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);
});

ピクチャー イン ピクチャー ウィンドウの [タブに戻る] ボタンを非表示にする

ユーザーが開いたタブに戻ることができるピクチャー イン ピクチャー ウィンドウのボタンを非表示にするには、documentPictureInPicture.requestWindow()disallowReturnToOpener オプションを true に設定します。

pipButton.addEventListener("click", async () => {
  // Open a Picture-in-Picture window which hides the "back to tab" button.
  const pipWindow = await documentPictureInPicture.requestWindow({
    disallowReturnToOpener: true,
  });
});

ピクチャー イン ピクチャー ウィンドウをデフォルトの位置とサイズで開く

以前のピクチャー イン ピクチャー ウィンドウの位置やサイズを再利用しないようにするには、documentPictureInPicture.requestWindow()preferInitialWindowPlacement オプションを true に設定します。

pipButton.addEventListener("click", async () => {
  // Open a Picture-in-Picture window in its default position / size.
  const pipWindow = await documentPictureInPicture.requestWindow({
    preferInitialWindowPlacement: true,
  });
});

ピクチャー イン ピクチャー ウィンドウにスタイルシートをコピーする

元のウィンドウからすべての CSS スタイルシートをコピーするには、ドキュメントに明示的にリンクまたは埋め込まれている styleSheets をループして、ピクチャー イン ピクチャー ウィンドウに追加します。これは 1 回限りのコピーです。

pipButton.addEventListener("click", async () => {
  const player = document.querySelector("#player");

  // Open a Picture-in-Picture window.
  const pipWindow = await documentPictureInPicture.requestWindow();

  // Copy style sheets over from the initial document
  // so that the player looks the same.
  [...document.styleSheets].forEach((styleSheet) => {
    try {
      const cssRules = [...styleSheet.cssRules].map((rule) => rule.cssText).join('');
      const style = document.createElement('style');

      style.textContent = cssRules;
      pipWindow.document.head.appendChild(style);
    } catch (e) {
      const link = document.createElement('link');

      link.rel = 'stylesheet';
      link.type = styleSheet.type;
      link.media = styleSheet.media;
      link.href = styleSheet.href;
      pipWindow.document.head.appendChild(link);
    }
  });

  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);
});

ピクチャー イン ピクチャー ウィンドウが閉じられたときの処理

ウィンドウ "pagehide" イベントをリッスンして、ピクチャー イン ピクチャー ウィンドウがいつ閉じられたか(ウェブサイトによって開始されたか、ユーザーが手動で閉じたため)を確認します。イベント ハンドラは、次に示すように、要素をピクチャー イン ピクチャー ウィンドウから復元するのに適しています。

pipButton.addEventListener("click", async () => {
  const player = document.querySelector("#player");

  // Open a Picture-in-Picture window.
  const pipWindow = await documentPictureInPicture.requestWindow();

  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);

  // Move the player back when the Picture-in-Picture window closes.
  pipWindow.addEventListener("pagehide", (event) => {
    const playerContainer = document.querySelector("#playerContainer");
    const pipPlayer = event.target.querySelector("#player");
    playerContainer.append(pipPlayer);
  });
});

close() メソッドを使用して、ピクチャー イン ピクチャー ウィンドウをプログラムで閉じます。

// Close the Picture-in-Picture window programmatically. 
// The "pagehide" event will fire normally.
pipWindow.close();

ウェブサイトがピクチャー イン ピクチャーになったときに音声を再生する

documentPictureInPicture"enter" イベントをリッスンして、ピクチャー イン ピクチャー ウィンドウが開かれたことを確認します。このイベントには、ピクチャー イン ピクチャー ウィンドウにアクセスするための window オブジェクトが含まれています。

documentPictureInPicture.addEventListener("enter", (event) => {
  const pipWindow = event.window;
});

ピクチャー イン ピクチャー ウィンドウ内の要素にアクセスする

ピクチャー イン ピクチャー ウィンドウの要素にアクセスするには、documentPictureInPicture.requestWindow() によって返されたオブジェクトから、または以下に示すように documentPictureInPicture.window を使用します。

const pipWindow = documentPictureInPicture.window;
if (pipWindow) {
  // Mute video playing in the Picture-in-Picture window.
  const pipVideo = pipWindow.document.querySelector("#video");
  pipVideo.muted = true;
}

ピクチャー イン ピクチャー ウィンドウのイベントを処理する

ボタンやコントロールを作成し、JavaScript で通常どおりに "click" などのユーザー入力イベントに応答します。

// Add a "mute" button to the Picture-in-Picture window.
const pipMuteButton = pipWindow.document.createElement("button");
pipMuteButton.textContent = "Mute";
pipMuteButton.addEventListener("click", () => { 
  const pipVideo = pipWindow.document.querySelector("#video");
  pipVideo.muted = true;
});
pipWindow.document.body.append(pipMuteButton);

ピクチャー イン ピクチャー ウィンドウのサイズを変更する

resizeBy() ウィンドウ メソッドと resizeTo() ウィンドウ メソッドを使用して、ピクチャー イン ピクチャー ウィンドウのサイズを変更します。どちらの方法でも、ユーザー操作が必要です。

const resizeButton = pipWindow.document.createElement('button');
resizeButton.textContent = 'Resize';
resizeButton.addEventListener('click', () => {
  // Expand the Picture-in-Picture window's width by 20px and height by 30px.
  pipWindow.resizeBy(20, 30);
});
pipWindow.document.body.append(resizeButton);

開いたウィンドウにフォーカスする

focus() ウィンドウ メソッドを使用して、ピクチャー イン ピクチャー ウィンドウからオープナー ウィンドウにフォーカスします。この方法では、ユーザーの操作が必要です。

const returnToTabButton = pipWindow.document.createElement("button");
returnToTabButton.textContent = "Return to opener tab";
returnToTabButton.addEventListener("click", () => {
  window.focus();
});
pipWindow.document.body.append(returnToTabButton);

CSS ピクチャー イン ピクチャー ディスプレイ モード

CSS picture-in-picture 表示モードを使用すると、ウェブアプリ(の一部)がピクチャー イン ピクチャー モードで表示されている場合にのみ適用される特定の CSS ルールを記述します。

@media all and (display-mode: picture-in-picture) {
  body {
    margin: 0;
  }
  h1 {
    font-size: 0.8em;
  }
}

特徴検出

Document Picture-in-Picture API がサポートされているかどうかを確認するには、次のコマンドを使用します。

if ('documentPictureInPicture' in window) {
  // The Document Picture-in-Picture API is supported.
}

デモ

VideoJS プレーヤー

Document Picture-in-Picture API の VideoJS プレーヤーのデモで試すことができます。ソースコードをご確認ください。

Pomodoro

ポモドーロ ウェブアプリの Tomodoro も、利用可能な場合は Document Picture-in-Picture API を利用しています。GitHub の pull リクエストをご覧ください。

Tomodoro: ポモドーロ ウェブアプリ。
Tomodoro のピクチャー イン ピクチャー ウィンドウ。

フィードバックをお寄せください

提案や質問を GitHub で Issue として報告する。