コフス技術ブログ

Picture-in-Picture APIで画像表示

昨今のブラウザではPicture-in-Picture APIを使いPinP表示する事が簡単にできますが、現状対応しているコンテンツは動画のみです。

基本的にvideo要素に表示されている内容のみがPinP表示されるわけですが、canvas要素を用いcaptureStream()を行えば自由なコンテンツをPinP表示する事が可能になります。

たとえば画像をPinP表示する例を示します。

以下の様な特定の画像を表示しつつ、ボタンをクリックするとPinP表示をトグルできるHTMLとJSを用意します。

index.html
<img
id="pip-image-element"
src="https://source.unsplash.com/400x300?random"
style="width: 400px; height: 300px;"
>
<button id="pip-button-element">PinP Toggle</button>
index.ts
interface CanvasElement extends HTMLCanvasElement {
captureStream(frameRate?: number): MediaStream
}
class PinpImage {
pipImageElement: HTMLImageElement | null
pipButtonElement: HTMLButtonElement | null
image: HTMLImageElement
constructor() {
this.pipImageElement = document.querySelector('#pip-image-element')
this.pipButtonElement = document.querySelector('#pip-button-element')
this.image = new Image()
if (!this.pipImageElement) {
return
}
this.image.crossOrigin = 'Anonymous'
this.image.addEventListener('load', () => this.imageLoaded(), false)
this.image.src = this.pipImageElement.src
}
imageLoaded(): void {
const canvas = <CanvasElement>document.createElement('canvas')
canvas.height = this.image.height
canvas.width = this.image.width
// Draw something to canvas.
const ctx = canvas.getContext('2d')
if (!ctx) {
return
}
ctx.scale(1, 1)
ctx.drawImage(this.image, 0, 0)
const video = document.createElement('video')
video.muted = true
video.autoplay = true
video.srcObject = canvas.captureStream(60)
video.play()
this.pipButtonElement?.addEventListener('click', async () => {
try {
if (video !== document.pictureInPictureElement) {
await video.requestPictureInPicture()
} else {
await document.exitPictureInPicture()
}
} catch (error) {
// some code
} finally {
// some code
}
})
}
}
window.addEventListener('load', () => {
new PinpImage()
})

注目する箇所は以下です。

  1. 画像をdrawImage()canvas要素に表示
  2. それをcaptureStream()video要素のメディアソースとして指定
  3. ボタンクリックでvideo要素をPinP表示

canvasにて表現できるものであれば比較的容易にPinP表示もできる事が分かります。

PinPの様子

アイデア次第でPinPの世界も広がりそうです。

参考: https://www.w3.org/TR/picture-in-picture/
参考: https://googlechrome.github.io/samples/picture-in-picture/audio-playlist
参考: https://sbfl.net/blog/2021/04/30/javascript-html-picture-in-picture/