VideoJS Components解釈・作成

##はじめに
前回、[「Web Video Player, VideoJSの紹介」](https://qiita.com/tokidoki11/items/a5b72b6cb00421392d5a)を投稿しました。
今回はVideoJS内部Componentという解釈と簡単の作成チュートリアルを記事したいと思います。

#####VideoJS Componentとは
VideoJSにある、全部のコントロールやUIなどは`Component`というからクラス継承されています。
デフォルトの設計で`Component`を継承して、さまさまのComponentを作成しました。

```
Player
├── MediaLoader (has no DOM element)
├── PosterImage
├── TextTrackDisplay
├── LoadingSpinner
├── BigPlayButton
├── LiveTracker (has no DOM element)
├─┬ ControlBar
│ ├── PlayToggle
│ ├── VolumePanel
│ ├── CurrentTimeDisplay (hidden by default)
│ ├── TimeDivider (hidden by default)
│ ├── DurationDisplay (hidden by default)
│ ├─┬ ProgressControl (hidden during live playback, except when liveui: true)
│ │ └─┬ SeekBar
│ │   ├── LoadProgressBar
│ │   ├── MouseTimeDisplay
│ │   └── PlayProgressBar
│ ├── LiveDisplay (hidden during VOD playback)
│ ├── SeekToLive (hidden during VOD playback)
│ ├── RemainingTimeDisplay
│ ├── CustomControlSpacer (has no UI)
│ ├── PlaybackRateMenuButton (hidden, unless playback tech supports rate changes)
│ ├── ChaptersButton (hidden, unless there are relevant tracks)
│ ├── DescriptionsButton (hidden, unless there are relevant tracks)
│ ├── SubtitlesButton (hidden, unless there are relevant tracks)
│ ├── CaptionsButton (hidden, unless there are relevant tracks)
│ ├── SubsCapsButton (hidden, unless there are relevant tracks)
│ ├── AudioTrackButton (hidden, unless there are relevant tracks)
│ ├── PictureInPictureToggle
│ └── FullscreenToggle
├── ErrorDisplay (hidden, until there is an error)
├── TextTrackSettings
└── ResizeManager (hidden)
```

##目標
VideoJS ControlBarにあるCustomButton Componentというボタンを付属して、
そのボタンを押すと外部関数を走るようにします。

**このようなのものです**

![example.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/914725/cacf8b51-b700-af20-1ea1-4c76ab242ecf.gif)



Github Repo: https://github.com/tokidoki11/video-js-component
このRepoではNuxtJSを使っていますが、NuxtJS知識は要りません。
本記事ではVideoJS Componentを中心にして、そのことだけを記事します。

##実践

####CustomButton作成
以下のコードご覧ください。解釈は後につけています


```ts
import videojs, { VideoJsPlayer, VideoJsPlayerOptions } from 'video.js'
const Button = videojs.getComponent('Button')


const iconSVG = `
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" fill="currentColor" viewBox="0 0 20 16">
<path d="M17.218,2.268L2.477,8.388C2.13,8.535,2.164,9.05,2.542,9.134L9.33,10.67l1.535,6.787c0.083,0.377,0.602,0.415,0.745,0.065l6.123-14.74C17.866,2.46,17.539,2.134,17.218,2.268 M3.92,8.641l11.772-4.89L9.535,9.909L3.92,8.641z M11.358,16.078l-1.268-5.613l6.157-6.157L11.358,16.078z"></path>
</svg>
`

interface CustomButtonOption extends VideoJsPlayerOptions{
  onClick: () => void
}

export default class CustomButton extends Button {
  constructor(
    player: VideoJsPlayer,
    options: CustomButtonOption
  ) {
    super(player, options)
    this.controlText("My Custom Button")
    this.el().innerHTML = iconSVG
  }

  handleClick(){
    (this.options_ as CustomButtonOption).onClick()
  }

  createEl(): HTMLButtonElement {
    return super.createEl('button', {
      className: 'vjs-custom'
    })
  }
}

```
https://github.com/tokidoki11/video-js-component/blob/master/video-components/customButton.ts



####解釈

１. Extending Button Component

今回は`Button`Componentをもとに自作`CustomButton` Componentを作成します。
`videojs.getComponent`にComponent名を指定したら、Class Templateを取得します。

```ts
const Button = videojs.getComponent('Button')
```
取得してからベースクラスにして、継承を行います。

```ts
export default class CustomButton extends Button
```

２. Optionを展開する

基本的には`Component`をextendする時には2つの引数が示さなければなりません。
VideoJSPlayerとVideoJSOption。VideoJSOptionを展開して、onClick関数をもらえるようにします。

```ts
interface CustomButtonOption extends VideoJsPlayerOptions{
  onClick: () => void
}
```
```ts
constructor(
    player: VideoJsPlayer,
    options: CustomButtonOption
  )
```

`handleClick`関数をオーバーライドして、optionにあるonClickを実行するように

```ts
handleClick(){
    (this.options_ as CustomButtonOption).onClick()
}
```

３. Styling

Elementを作成します、`createEl`関数にParent Classの`createEl`を実行して。
`<button class="vjs-custom"></button>`が作成されるように

```ts
  createEl(): HTMLButtonElement {
    return super.createEl('button', {
      className: 'vjs-custom'
    })
  }
```

SVGアイコンを用意します

```ts
const iconSVG = `
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" fill="currentColor" viewBox="0 0 20 16">
<path d="M17.218,2.268L2.477,8.388C2.13,8.535,2.164,9.05,2.542,9.134L9.33,10.67l1.535,6.787c0.083,0.377,0.602,0.415,0.745,0.065l6.123-14.74C17.866,2.46,17.539,2.134,17.218,2.268 M3.92,8.641l11.772-4.89L9.535,9.909L3.92,8.641z M11.358,16.078l-1.268-5.613l6.157-6.157L11.358,16.078z"></path>
</svg>`
```
文言とSVGアイコンをボタンにつけます

```ts
constructor(
    player: VideoJsPlayer,
    options: CustomButtonOption
  ) {
    super(player, options)
    this.controlText("My Custom Button") //これはボタンTitleです
    this.el().innerHTML = iconSVG //ボタンElementのなかにSVGを付属する
  }
```

####VideoJSにつけたあるCustomButtonを作成
以下のコードご覧ください。解釈は後につけています

```vue
<template>
  <div id="video-player-component">
    <h1>I'm counting: {{count}}</h1>
    <video
      ref="videoPlayer"
      class="video-js vjs-show-big-play-button-on-pause vjs-big-play-centered"
      muted
    />
  </div>
</template>

<script lang="ts">
import Vue from 'vue'
import videojs, { VideoJsPlayerOptions } from 'video.js'
import CustomButton from '@/video-components/customButton'

videojs.registerComponent('CustomButton', CustomButton)

const videoConfig = {
  autoplay: true,
  controls: true,
  html5: {
    vhs: {
      allowSeeksWithinUnsafeLiveWindow: true,
      enableLowInitialPlaylist: true,
      overrideNative: false,
      smoothQualityChange: true,
      withCredentials: false
    }
  },
  inactivityTimeout: 0,
  sources: [{
    src: "https://live.unified-streaming.com/scte35/scte35.isml/.m3u8",
    type: 'application/x-mpegURL'
  }],
  liveui: true,
  muted: true,
  preload: 'auto',
  width:720
} as VideoJsPlayerOptions

export default Vue.extend({
  data:()=>{
    return {
      count : 0
    }
  },
  mounted(){
    const videoPlayer = this.$refs["videoPlayer"]
    const player = videojs(videoPlayer, videoConfig, ()=>{
      player.controlBar.addChild("CustomButton", {
        onClick:()=>{
          this.count++
        }
      })
    })
  }
})
</script>
<style>
.vjs-custom{
  cursor: pointer;
  font-size: 1.8em!important;
  color:white;
}
</style>
```
https://github.com/tokidoki11/video-js-component/blob/master/components/VideoPlayer.vue

####解釈
１. Import CustomButton Class
CustomButtonをImportして

```ts
import CustomButton from '@/video-components/customButton'
```

その後videoJSにComponentを紹介します。
引数１: Component name (好みで）
引数１: Component class

```ts
videojs.registerComponent('CustomButton', CustomButton)
```

２. VideoJS Instanceを作成

```ts
 const videoPlayer = this.$refs["videoPlayer"]
 const player = videojs(videoPlayer, videoConfig, ()=>{
 ...
```

３. VideoJS PlayerのControlBarにCustomButtonを追加

`addChild`関数には3つの引数を受けます
引数１: Component Name
引数2: Option(今回は CustomButtonOption Interfaceに参考)
引数3: 順番（なければ後ろに）

この`onClick`では`data`にある`count`を加算してます。

```ts
player.controlBar.addChild("CustomButton", {
  onClick:()=>{
    this.count++
  }
})
```

##最後
このチュートリアルは終了になります。
色々VideoJS Componentがありまして、Extend方法も多少違っています。
Button Componentには`handleClick`がありますが、他にはそうでもないでしょう。

基本的には同じ流れになります。

**この記事が役に立つなるように**

##参考に
* https://docs.videojs.com/tutorial-components.html
* https://github.com/videojs/video.js/blob/main/src/js/clickable-component.js
* https://github.com/videojs/video.js/blob/main/src/js/button.js