Document Picture-in-Picture APIを使ってみた 【JavaScript】

# はじめに
開いてくださりありがとうございます！普段は大学で化学を専攻してます[𝕏 | @jima_jimax](https://x.com/jima_jimax)です。  
今回はJavaScriptのPicture in PictureできるWeb APIを使ってみます。
 *この記事を見ているということは少しでも興味があると言うことですよね?*  



# Document Picture-in-Picture APIとは？？
`Document Picture-in-Picture API`とはChromeでは2023年8月に116で追加された`video`要素以外にもPicture-in-Picture(PiP)出来るようにしたものです。  
PiPを使えば常にウィンドウを最前面表示にできるので**動画以外**にも使えるようになったのはめちゃめちゃ嬉しいですよね！！


このAPIを使っておしゃれな(?)Web時計と文字数カウントを作ってみたので是非使って下さい↓

https://jimajimax.github.io/circlock/

https://jimajimax.github.io/text-counter/




# 早速使ってみよう
フレームワークやライブラリは ~~分からないので~~ 一切使わない`Vanilla.js`でやります！

### 1. 利用可能か判別
以下を使って利用可能か判別することができます
```javascript
   if ("documentPictureInPicture" in window) {
       // document PiP APIが利用可能な場合の処理
   } else {
       // document PiP APIが利用不可な場合の処理
   }
```



### 2. PiP Windowを開く
```javascript
   pipButton.addEventListener("click", async () => {
     // PiP Windowに追加したい要素
     const player = document.querySelector("#");

     // PiP Windowを開く
     const pipWindow = await documentPictureInPicture.requestWindow(options);

     // 要素をPiP Windowに追加
     pipWindow.document.body.append(player);
   });
```
`async`、`await`です。`options`にはwidth, height, disallowReturnToOpener, preferInitialWindowPlacementを指定できます。
<br>
`width`, `height`,はウィンドウの初期サイズ、  

`disallowReturnToOpener`はtrueにするとウィンドウ上の戻るボタンを消せます。  

ウィンドウは通常2回目以降開く場合、前回閉じた位置で開かれますが`preferInitialWindowPlacement`をtrueにすることで毎回デフォルトの位置で開くことができます。

:::note
私が作ったページではウィンドウに要素を`"移動"`していますが、`"コピー"`することもできます 
後述しているブックマークレットでは`.cloneNode(true)`してます
:::



### 3.  PiP Windowを閉じる
要素は自動で親要素へと戻らないので親要素に戻す必要があります。
```javascript
   // 開く関数に記述
   pipContainer = player.parentNode;

   // 閉じる関数に記述
   const player = pipWindow.document.querySelector("#");
   pipContainer.append(player);
   pipWindow.close();
```



### 4. おまけ
以下の[ブックマークレット](https://google.com/search?q=ブックマークレット)を使えばdocument.documentElement要素をコピーしてどのページでもパワーでPiPすることができます。どうしてもPiPしたいページがある場合は使ってみて下さい

:::note warn
レイアウトが崩れたり動作しない場合があります
:::
```javascript:Bookmarklet(.cloneNode(true))
javascript:(()=>{if(!("documentPictureInPicture"in window)){alert("この環境はDocument Picture-in-Picture APIをサポートしていません");return;}const pipcheck=confirm("Picture-in-Picture表示にしますか？\n(ページレイアウトが崩れたり正常に動作しない可能性があります)");if(pipcheck){(async()=>{try{const player=document.documentElement.cloneNode(true);const pipWindow=await documentPictureInPicture.requestWindow({disallowReturnToOpener:true,preferInitialWindowPlacement:true,width:450,height:400});pipWindow.document.body.append(player);}catch(err){alert("PiPを開始出来ませんでした\n\n"+err.message);}})();}})();

```

**~~やはり暴力・・・・‼暴力は全てを解決する・・・・‼~~**



# 使用例
私が作ったページでは時計部分や文字を入れるフィールド部分をPiPできるので是非お試し下さい

https://jimajimax.github.io/circlock/

時計についてPiP以外に関することはGitHubの[[README]](https://github.com/jimajimax/circlock/blob/main/README.md
)をご覧下さい

https://jimajimax.github.io/text-counter/

:::note
動作は  
Windows: Chrome, Vivaldi  
Android: Chrome  
で確認を行ったためApple系デバイスやその他ブラウザは確認していません。
:::  

:::note
PiPウィンドウをフォーカス中に`Ctrl + 「+」or「-」`で拡大縮小できます
:::
[circlock](https://jimajimax.github.io/circlock/)
![circlock](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3953704/9f2026c9-5459-7375-ba94-2aabbbccd759.png)

cssをインライン記述にすることでPiP中でもスタイルを変更できます。
PiPウィンドウの秒表示の::selectionはpostMessageで子ウィンドウに色を送信して無理くり適用させてます。
<br>
また、dcument PiP APIが使用不可の環境の場合、`canvas`に描画させてからそれを`video`要素にすることで従来の方法のPiPを開始してます

<details><summary>canvas Picture-in-Picture</summary>

```javascript:PiP
  // canvas Picture-in-Picture
    const canvas = document.getElementById("canvas");
    const ctx = canvas.getContext("2d");
    const scaleFactor = 3;       // 3倍で描画させて解像度を3倍にする
    canvas.width = 300 * scaleFactor;
    canvas.height = 300 * scaleFactor;
    canvas.style.width = "300px";
    canvas.style.height = "300px";
    ctx.scale(scaleFactor, scaleFactor);

    const stream = canvas.captureStream(30);
    const video = document.createElement("video");
    video.srcObject = stream;

    pipButton.addEventListener("change", async () => {
        await video.play();
        if (document.pictureInPictureElement) {
            await document.exitPictureInPicture();
        } else {
            await video.requestPictureInPicture();
        }
    });
```
</details>
<br>

[text-counter](https://jimajimax.github.io/text-counter/)
![text-counter.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3953704/d2d62105-ee8c-62a3-8094-d2e4a19403bc.png)



<br><br>
またGoogle MeetやSpotifyも活用しているようです
[Google Meet](https://meet.google.com)
![googlemeet.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3953704/64fc77dd-d251-840d-d21b-ba8dfa979649.png)
(Google Meetではタブが切り替わった際にPiPを自動開始できるようです この実装方法が分かる方は教えて下さい....)
<br>
[Spotify](https://spotify.com)
![spotify.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3953704/8530cccc-afa3-91ae-fabe-4eeb6147ffee.png)
<br>
PiP Windowにマウスホバー
![spotifyhover.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3953704/8c8bc236-6e91-256b-f0a9-608967e88469.png)

Premiumじゃないとできない(?)またアプリ版でもPiPできます
<br>
他の作業中にもすぐ確認できるのは良いですね 
 またメインウィンドウをクリックしてもPiPを使うとウィンドウが後ろに隠れないところが**very good!**:thumbsup:

私は講義の残り時間を計算するページを作ってそれをPiPで表示させることで便利に使ってます。講義は集中してますよ、えぇもちろん



# まとめ
※このAPIは今後仕様が変更する可能性があるのでお気を付け下さい

以上Document Picture-in-Picture APIの紹介でした！
今後この機能を使ったサイトが増えてくと嬉しいですね。またこの機能を使ってサイトを作ったら是非教えて下さい！  


## 参考
- 「動画」だけでなく、すべての要素でピクチャー イン ピクチャーで使用可能
https://developer.chrome.com/docs/web-platform/document-picture-in-picture?hl=ja
- オリジントライアルになったDocument Picture-in-Picture APIを試してみよう
https://zenn.dev/cybozu_frontend/articles/picture-in-picture-api
- ChromeのDocument Picture-in-Picture APIを試してみた
https://zenn.dev/voiceapplab/articles/2b90180c38af9d
- 記事をPicture-in-Pictureで読めるようにしてみた
https://blog.stin.ink/articles/article-in-picture-in-picture



## おわりに
初めて記事をしかもMarkdown形式で書いたので拙い部分があると思いますがここまで読んで頂きありがとうございます！  
記事を共有してくださると大変喜びます！
<br><br>
それではあなたも良いPiPライフを！:grinning: :wave: