メインコンテンツへスキップ

All Posts

News bits

Document Picture-in-Picture API

Document Picture-in-Picture API は、video 要素のみに適用できる Picture-in-Picture API と異なり、任意の要素を小窓で表示することが出来ます。

tsx
const button = document.querySelector("#open-button");

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

  const pipWindow = await documentPictureInPicture.requestWindow({
    // initialAspectRatio: 1,
    width: player.width,
    height: player.height,
  });

  pipWindow.document.body.append(player);
});

#Picture-in-Picture API

Picture-in-Picture API は、video 要素を小窓で表示するための API です。

tsx
const button = document.querySelector("#open-button");
const video = document.querySelector("#video");

button.addEventListener("click", () => {
  video.requestPictureInPicture();
});

#Picture-in-Picture API との比較

Document Picture-in-Picture API は、Picture-in-Picture API の拡張ではなく似たような異なる API です。

Document Picture-in-Picture APIPicture-in-Picture API
対象となる要素任意の要素video 要素
Picture-in-Picture を開くメソッドdocumentPictureInPicture.requestWindow()video.requestPictureInPicture()
実行にクリックなどユーザー行動が必須か必須必須
iframe 内での実行不可不可
同時に開ける数1 個。個数は Picture-in-Picture API と共有1 個。個数は Document Picture-in-Picture API と共有
Picture-in-Picture した要素の状態要素が消える。Picture-in-Picture を閉じても要素は戻らない。要素は消えず代替表示がされる。picture-in-picture 擬似クラスでスタイルを制御できる。

Document Picture-in-Picture API は、任意の要素ではなく新たな Document を Picture-in-Picture していると考えると理解しやすいと思います。

#Document Picture-in-Picture API では勝手に要素が復帰しない

Picture-in-Picture API の場合と異なり、Document Picture-in-Picture API では Picture-in-Picture された要素は、勝手には戻りません。

次のように Picture-in-Picture の Window の unload イベントをトリガーにして、その Window にある表示した要素を取得し戻すようなコードを書く必要があります。

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

  const pipWindow = await documentPictureInPicture.requestWindow({
    width: player.width,
    height: player.height,
  });

  pipWindow.document.body.append(player);

  pipWindow.addEventListener("unload", (event) => {
    const pipPlayer = event.target.querySelector("#player");
    playerContainer.append(pipPlayer);
  });
});

#Picture-in-Picture 内に CSS を適用する

実験的な機能として導入されたときは存在していた copyStyleSheets オプションは削除されています。そのため、手軽に CSS をコピーして持っていくような方法は存在しません。

https://github.com/WICG/document-picture-in-picture/issues/12

削除された copyStyleSheets オプションの挙動を真似したい場合。

tsx
// const pipWindow = await documentPictureInPicture.requestWindow({
//   width: player.width,
//   height: player.height,
// });

[...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);
  }
});

// pipWindow.document.body.append(player);

特に言及されていませんが、個人的には Picture-in-Picture 用のスタイルを別途用意してあげた方が、無駄なスタイルを大量に渡さなく済むため良いように思います。

#実装例

https://stackblitz.com/edit/js-mrokk8?file=index.js

Chrome 110

#古い Windows OS のサポートが終了

Chrome 110 で、Windows 7/8/8.1 などの OS のサポートが終了しました。 なお 2023 年 1 月に、Microsoft は Windows 7/8/8.1 のほとんどのバージョンのサポートを終了しています。

また最後のサポートバージョンである Chrome 109 へのセキュリティ修正も 2023 年 10 月までとなっています。

R.I.P.

#PIP 擬似クラスのサポート

PIP(Picture in Picture)とは、再生している動画を画面の隅に別ウィンドウで表示する機能です。 PIP を利用している video 要素に対してスタイルを適用できる:picture-in-picture疑似クラスが Chrome 110 でサポートされました。

:picture-in-picture:が 1 つだけであり、::after::first-letterのような擬似要素ではありません。 PIP で表示される別ウィンドウを表すものではなく、PIP を利用している video 要素を表す点に注意してください。

https://developer.chrome.com/blog/new-in-chrome-110/

Chrome Developers の記事では、PIP モードである video 要素のデフォルトの見た目を上書きするデモが紹介されています。

css
video:picture-in-picture {
  opacity: 0;
}
.video-container {
  background: #000;
  position: relative;
}
.video-container:has(video:picture-in-picture)::before {
  content: "Video is now playing in a Picture-in-Picture window";
  position: absolute;
  right: 36px;
  bottom: 36px;
  color: #ddd;
}

現状、PIP を利用する機会はあまりないかもしれませんが、Chrome 111 では PIP が任意の要素に対して利用できるdocumentPictureInPictureのサポートが予定されており、 今後は利用する機会が増えるかもしれません。

著者について

Hi there. I'm hrdtbs, a frontend expert and technical consultant. I started my career in the creative industry over 13 years ago, learning on the job as a 3DCG modeler and game engineer in the indie scene.

In 2015 I began working as a freelance web designer and engineer. I handled everything from design and development to operation and advertising, delivering comprehensive solutions for various clients.

In 2016 I joined Wemotion as CTO, where I built the engineering team from the ground up and led the development of core web and mobile applications for three years.

In 2019 I joined matsuri technologies as a Frontend Expert, and in 2020 I also began serving as a technical manager supporting streamers and content creators.

I'm so grateful to be working in this field, doing something that brings me so much joy. Thanks for stopping by.