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

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 ウィンドウに簡単に組み合わせることができます。また、メッセージの送信、他のユーザーのミュート、挙手などのカスタム コントロールも提供できます。

仕事効率化

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

インターフェース

プロパティ

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 として報告する。