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

François Beaufort
François Beaufort

Browser Support

  • Chrome: 116.
  • Edge: 116.
  • Firefox: not supported.
  • Safari: not supported.

Source

ドキュメント ピクチャー イン ピクチャー API を使用すると、任意の HTML コンテンツを入力できる最前面のウィンドウを開くことができます。これは、HTML の <video> 要素のみをピクチャー イン ピクチャー ウィンドウに配置できる既存の <video> 用のピクチャー イン ピクチャー 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> のピクチャー イン ピクチャー API を介して提供できる現在のエクスペリエンスは、スタイルと入力が制限されています。ドキュメント全体をピクチャー イン ピクチャーで表示できるため、ウェブサイトは canvas ハックに頼ることなく、複数の動画ストリームを 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() の Window メソッドを使用して、ピクチャー イン ピクチャー ウィンドウのサイズを変更します。どちらの方法でもユーザー操作が必要です。

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;
  }
}

特徴検出

ドキュメントのピクチャー イン ピクチャー API がサポートされているかどうかを確認するには、次のコードを使用します。

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

デモ

VideoJS プレーヤー

Document Picture-in-Picture API の VideoJS プレーヤーのデモで試すことができます。

ポモドーロ

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

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

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

提案や質問については、GitHub で Issue を作成してください。