Video.jsでスキップボタンを実装する

YouTubeでやってるやつをVideo.jsでもやりたい。要するにこういうやつ。

![Screenshot from 2023-03-19 21-48-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52237/d5cd8bb0-5297-f3e9-4bdf-4fdde084155a.png)

で、それっぽいものができた。

デモは[ここ(JSBin)](https://jsbin.com/bibacaj/edit)。このデモの元は公式の[これ(JSBin)](https://jsbin.com/vobacas/edit?html,css,js,output)になる。

# 解説
Video.jsの[Component](https://videojs.com/guides/components/)を使うことで、プレイヤー内に操作できる要素を追加できる。

```javascript
// Get the Component base class from Video.js
var Component = videojs.getComponent('Component');

// add skip button
var skipFwd = videojs.extend(Component, {
  constructor: function(player, options) {
    Component.apply(this, arguments)
  },
  createEl: function() {
    return videojs.createEl('div', {
      className: 'vjs-skip vjs-skip-fwd'
    })
  }
})
var skipBwd = videojs.extend(Component, {
  constructor: function(player, options) {
    Component.apply(this, arguments)
  },
  createEl: function() {
    return videojs.createEl('div', {
      className: 'vjs-skip vjs-skip-bwd'
    })
  }
})
// register skip button to videojs
videojs.registerComponent('skipFwd', skipFwd)
videojs.registerComponent('skipBwd', skipBwd)
```
上のコードはプレイヤーのComponentを取得して、追加要素`skipFwd`, `skipBwd`を定義し、Componentとして追加登録している。[createElのAPI referense](https://docs.videojs.com/component#createEl)を見るとこの定義内でイベントハンドラも定義できるようであるが、試したところイベントの要素をうまく渡す方法がわからなかったので、ここではdivの追加だけにとどめ、イベントハンドラは別のところで定義している。

```javascript
// Create a player.
var player = videojs('my-player');
// add chile node to player
player.addChild('skipFwd')
player.addChild('skipBwd')
```
実際にプレイヤーのインスタンスを作成して、子要素として先程登録したComponentである`skipFwd`, `skipBwd`を登録する。

```javascript
document.getElementsByClassName('vjs-skip-fwd')[0].addEventListener('click', function(e) {
  videojs('my-player').currentTime(videojs('my-player').currentTime() + 5)
  setRipple(e.target, e.layerX, e.layerY)
})
document.getElementsByClassName('vjs-skip-bwd')[0].addEventListener('click', function(e) {
  videojs('my-player').currentTime(videojs('my-player').currentTime() - 5)
  setRipple(e.target, e.layerX, e.layerY)
})
```
追加したdivを取得して、イベントハンドラを追加する。リップルエフェクトをつけるための関数をここから呼び出している。

```javascript
// add ripple effect
function setRipple(parentDom, x, y) {
  var rippledom = document.createElement('span')
  rippledom.style.setProperty('left', x-75+'px')
  rippledom.style.setProperty('top', y-75+'px')
  rippledom.setAttribute('class', 'vja-skip ripple')
  parentDom.appendChild(rippledom)
  setTimeout(() => {
    var rip = document.getElementByClassName('ripple')
    for (var i = 0; i < rip.length; i++) {
      rip[i].remove()
    }
  }, 400)
}
```
リップルエフェクトを追加するための関数。span要素で実装している。アニメーションはCSSで行っている(後述)。

```css
.video-js .vjs-skip-fwd {
  background: rgba(0, 0, 0, 0);
  display: block;
  color: white;
  position: absolute;
  top: 0px;
  right: 0px;
  width: 40%;
  height: calc(100% - 30px);
  overflow: hidden;
}
.video-js .vjs-skip-bwd {
  background: rgba(0, 0, 0, 0);
  display: block;
  color: white;
  position: absolute;
  top: 0px;
  left: 0px;
  width: 40%;
  height: calc(100% - 30px);
  overflow: hidden;
}
```
これがスキップボタンのCSS定義。プレイヤーの両端から幅40%で透明なdivをかぶせるようにしている。

```css
.vjs-skip .ripple {
  width: 150px;
  height: 150px;
  position: absolute;
  border-radius: 100%;
  pointer-events: none;
  transform: scale(0);
  opacity: 0;
  background: black;
  animation: rippleanm 0.4s ease-out;
}
@keyframes rippleanm {
  from {
    opacity: 0.5;
  }
  to {
    transform: scale(8);
    opacity: 0;
  }
}
```
これがリップルエフェクトのCSS。半透明の円が0.4秒で消えるようにしている。

```javascript
player.play()
```
最後に再生を開始。ただブラウザによっては自動で開始しない。

以上です。