【JavaScript】動画以外の要素でもピクチャーインピクチャーできるらしい！

## はじめに
みなさんは、ピクチャーインピクチャーをご存じですか？

動画やビデオ通話系のサービスやアプリではよくみますが、他の用途ではあまりみませんよね！
それもそのはずで、今までのピクチャーインピクチャーは、`<video>` 要素でのみ使うことができました。

ですが、Chrome 111 以降では、`documentPictureInPicture`を使うことで、`<video>` 要素以外の任意の要素をピクチャーインピクチャーさせることができるようになります。

今までできなかった `<audio>` や `<img>`のような要素をピクチャーインピクチャーさせることができるようになるので、CDジャケットを表示させながら音楽を再生させたりでき、webサイト上でできることが広がります。

そのためこの記事では、そんな `documentPictureInPicture`を使ったピクチャーインピクチャーについて紹介します。


## ピクチャーインピクチャー（PiP）とは？
![image1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390720/53807829-2038-f9ad-a442-b2f69a5184ad.png)
現在ピクチャーインピクチャーは、モバイルデバイス等で他のアプリを使用しながら、小さなプレーヤーで動画を視聴できる機能です。

私は、youtubeを見ながら、X(Twitter)を見たり、Qiita記事を読んだり、
Prime Videoを見ながら、俳優・女優について調べたりしています。

動画以外にも、ビデオ通話やZoom会議等においても使われていることがあります。


## ピクチャーインピクチャーの実装

:::note warn
今回使う、任意の要素をピクチャーインピクチャーは、以下のフラグを有効化してから確認してください。
`chrome://flags/#document-picture-in-picture-api`
:::


### documentPictureInPicture
まずは、今回実装に使う　`documentPictureInPicture`　について解説していきます。

**○プロパティ**
+ `documentPictureInPicture.window`
    + ピクチャーインピクチャーのウィンドウにアクセスするためのプロパティです。
    + ウィンドウがなければ `null`を返します。
　

**○メソット**
+ `documentPictureInPicture.requestWindow(options)`
    + ピクチャーインピクチャーのウィンドウを表示する時に使うメソットです。
    + optionsには以下の要素を渡すことができます
        + `initialAspectRatio`：ウィンドウの縦横比
        + `whidth`：ウィンドウの幅
        + `height`：ウィンドウの高さ
        + `copyStyleSheets`： trueを渡すことで、１度だけスタイルを渡すことができる
　

**○イベント**
+ `documentPictureInPicture.onenter`
    + 　ピクチャーインピクチャーのウィンドウが表示された時のイベント



### 実装方法
では早速、任意の要素をピクチャーインピクチャーさせていきましょう。

#### 01. HTMLを記載する

```sample.html
<button id="openButton">ピクチャーインピクチャー表示する</button>
<div id="container">
  <div id="player">
    <!--ピクチャーインピクチャーのウィンドウに入れたい要素を記述する-->
  </div>
</div>
```
1. *`id="openButton"`は、クリックイベントを取得するために使います。*
1. *`id="container"`は、ピクチャーインピクチャーのウィンドウを閉じた時に要素を戻すために使います。*
1. *`id="player"`は、ピクチャーインピクチャーのウィンドウに入れる要素です。*

#### 02. CSSを記載する
```sample.css
/*ピクチャーインピクチャーのウィンドウに表示させる要素のスタイルを　記載します。*/
#player {
    background-color: red;
    height: 100px;
    width: 100px;
}
```
*※ スタイルが渡っていることがわかるようなにスタイルを調整*

#### 03. ピクチャーインピクチャーのウインドウを表示させる

```sample.js
// 1. #openButtonを取得
const OpenButton = window.document.getElementById('openButton')

// 1. クリックイベントを使う
OpenButton.addEventListener('click', async () => {
  const player = document.querySelector("#player");

  // 2. ピクチャーインピクチャーのウインドウの定義
  const pipWindow = await documentPictureInPicture.requestWindow({
    width: player.clientWidth,
    height: player.clientHeight,
    copyStyleSheets: true,
  });

  // 3. ウインドウに`#player`を追加
  pipWindow.document.body.append(player);
});
```
1. *`#openButton`を取得して、クリックされた時のイベントを使います。*
1. *ピクチャーインピクチャーのウインドウを定義します*
1. *ウインドウに`#player`を追加します。*


#### 04. ピクチャーインピクチャーのウインドウを閉じた時にもとに戻す
```sample.js
const OpenButton = window.document.getElementById('openButton')

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

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

  pipWindow.document.body.append(player);

  // 以下追加
  pipWindow.addEventListener("unload", (event) => {
    const playerContainer = document.querySelector("#container");
    const pipPlayer = event.target.querySelector("#player");
    playerContainer.append(pipPlayer);
  });
});
```
*ピクチャーインピクチャーのウインドウが`unload`された時に、`#player`要素を`#container`要素に追加する処理を記載する*


#### 05. 完成
このようにHTML・CSS・JavaScriptを記載することで、ピクチャーインピクチャーに任意の要素を記入することができます。

<p class="codepen" data-height="300" data-default-tab="js,result" data-slug-hash="vYzGYwL" data-editable="true" data-user="sp_degu" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/sp_degu/pen/vYzGYwL">
  picture in picture</a> by でぐぅー | Qiita (<a href="https://codepen.io/sp_degu">@sp_degu</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## サンプル
どんな要素でも入れることができそうだったので、モンスターボールでも、表示させようかなということで、サンプルを作成しました。

:::note warn
任意の要素をピクチャーインピクチャーに表示させるには、以下のフラグを有効化してから確認してください。
`chrome://flags/#document-picture-in-picture-api`
:::

<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="oNPgroz" data-editable="true" data-user="sp_degu" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/sp_degu/pen/oNPgroz">
  picture in picture</a> by でぐぅー | Qiita (<a href="https://codepen.io/sp_degu">@sp_degu</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## まとめ
この記事では、、`<video>`以外の**任意の要素**をピクチャーインピクチャーさせることができるようになる方法を紹介しました。

この方法は、まだ、Chrome 111 以降でのみの検証のため、実際に業務等で使えるものではありません。

ですが、このやり方が使えるようになった暁には、ピクチャーインピクチャーをwebサービスやアプリなどがちょこちょこ出てくるでしょう。
楽しみですね...

動画、音楽、ビデオ通話以外の実用的な使い方がまだいまいちわかっていないので、
いいアイディアを思いついたよという方は、ぜひコメントください。

---

最後まで読んでくださってありがとうございます！

普段はデザインやフロントエンドを中心にQiitaに記事を投稿しているので、ぜひQiitaのフォローとX(Twitter)のフォローをお願いします。

https://twitter.com/degudegu2510