iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 27
1
Modern Web

前端影片與直播筆記系列 第 27

Day27 Video.js + React 播放器整合

今天的內容會用 React + Video.js 去做出自己的 React 播放器元件。

前端在使用 React 開發網站時,有時會碰到第三方套件沒有 React 版本,而需要自己去整理、調整程式碼。

幸運的是:Video.js 文件有與 React 整合的例子 ( 雖然就只有這一頁 )。

開發環境:

使用 create-react-app 建立環境。
建好環境後刪除 src 資料夾不必要的檔案 ( 只留 App.js、index.css、index.js )

安裝、引入 video.js

因為某些原因,這邊的 Video.js 不使用 NPM 和 Webpack,而是改用 CDN 方式引入,實際大家在開發用 NPM 安裝 Video.js 即可。

在 public 資料夾的 index.html 引入 video.js 檔案

<!-- css -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/video.js/7.2.4/video-js.css">

<!-- js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/video.js/7.2.4/video.min.js">
</script>

建立 ITManPlayer.js

接著設計元件,在 src 資料夾新增一個 ITManPlayer.js,ITManPlayer就會是我們之後的React & Videojs 播放器元件。

將App.js改成這樣

// App.js
import React, { Component } from 'react';
import ITManPlayer from './ITManPlayer';
class App extends Component {
  render() {
    return (
      <div className="wrap">
        <ITManPlayer/>
      </div>
    );
  }
}

export default App;

然後到 index.css 加入

/* index.css */
.wrap {
  max-width: 800px;
  margin-left: auto;
  margin-right: auto;
}

接著替 ITManPlayer.js 新增內容。

// ITManPlayer.js
import React, { Component } from "react";

class ITManPlayer extends Component {
  constructor() {
    super();
    this.player = React.createRef();
  }
  
  render() {
    return (
      <video
        className="video-js vjs-big-play-centered"
        data-setup="{}"
        ref={this.player}
      />
    );
  }
  
  componentDidMount() {
    const videoNode = this.player.current;
    this.video = window.videojs(videoNode,{
        sources: [{
            src: 'http://www.html5videoplayer.net/videos/toystory.mp4'
        }],
        controls: true,
        fluid:true
    });
  }
  
  componentWillUnmount() {
      if (this.video) {
          this.video.dispose();
      }
  }
}

export default ITManPlayer;

說明:

建立 videojs 物件

按照昨日說明,要建立 videojs 物件實體,通常使用videojs(),而videojs()第一個參數是 nodeID (為了取的對應的DOM),我們該怎麼在 React 取得對應的 nodeID 呢?

使用 ref 屬性

這裡替 render() 內 video 標籤添加 ref 屬姓 ,ref 屬姓是用來獲取 DOM 節點的,媒體播放是少數 React 覺得可以用 ref 來獲取 DOM 並做對應操作的事。
記得在 constructor(){} 內也添加 this.player = React.createRef();

componentDidMount

按照 Videojs 文件內容,接著在 componentDidMount( ) 這個生命週期裡面,我們可以用 this.refs.player 來獲取剛剛用 ref 綁定的DOM。

因為我們在全域用 cdn 引入了 video.js,可以用 window.videojs 來綁定 video 到對應的DOM結點上。

我們可以直接用

window.videojs(this.refs.player)

去綁定 DOM 和新建 video.js 物件了 這裡第一個參數傳入this.refs.player,第二個參數傳入設定播放器設置的物件。

componentWillUnmount

按照 Videojs 文件內容,在 componentWillUnmount( ) 這個生命週期裡面,我們替其加上.dispose()這個方法,確保我們的 videojs 物件可以在卸除元件時確實被銷毀。

componentWillUnmount() {
      if (this.video) {
          this.video.dispose();
      }
  }

結果

此時網頁應該會呈現如下,至此已完成播放器元件的雛型。

定義接口

接著就是定義<ITManPlayer/>可傳入屬性與參數,接口部分與團隊討論好即可,以下內容僅供參考:

播放器 config

我希望其他前端在使用<ITManPlayer/>時,可以像操作 HTML5 播放器寫法一樣,直接在元件標籤上傳入對應的播放器設定,那該怎麼做呢?

例如:

<ITManPlayer
    src="http://www.html5videoplayer.net/videos/toystory.mp4"
    muted={true}
/>

播放器建立好時要能讀取到這個影片源,並且預設靜音。

這個部分比較簡單,在 ITManPlayer.js 內定義好 props 屬性與預設值,最終把這些參數加入 Video.js 的播放器設置 ( 看是要加進 data-setupvideojs()第二個參數都可以 ),嚴謹一點還可以定義 defaultProps 和 propsType 。

事件監聽

我希望其他前端在使用<ITManPlayer/>時,可以透過事件監聽(當某事件發生,按當時需求觸發自定義的回呼函式。

而監聽事件名與觸發的回呼函式,理當是從父元件往<ITManPlayer/>傳入,昨天有提到,Video.js 的 事件監聽寫法會是這樣

videojs物件.on("事件名",之後執行的回呼函式)

所以我們只要在 ITManPlayer.js 內,把上層傳入的參數或值,最終在componentDidMount()壓成一個一個的.on()去註冊監聽就好了。

我們可以假定上層元件開一個事件監聽屬性,其他前端工程師使用時會傳入一個物件:

{
    事件名稱: 之後會觸發的回呼函式
}

在 ITManPlayer.js 內 props 取得後,會壓成一個一個的

.on(事件名稱,之後會觸發的回呼函式)

例如 App.js 內的<ITManPlayer>元件這樣寫

<ITManPlayer 
  src="http://www.html5videoplayer.net/videos/toystory.mp4"
  eventON={{
    pause: () => {
      console.log("暫停");
    },
    play: () => {
      console.log("播放");
    }
  }}
/>

最終在 componentDidMount() 變成這樣

this.video.on("pause", () => {
  console.log("暫停");
});

this.video.on("play", () => {
  console.log("播放");
});

我們只要在componentDidMount()內用Object.keysforEach 去搭配即可完成此一需求。

  componentDidMount() {
    let { eventON } = this.props;
    const videoNode = this.player.current;
    this.video = window.videojs(videoNode);

    // 監聽事件
    if(eventON){
      Object.keys(eventON).forEach(item => {
        this.video.on(item, () => {
        eventON[item]()
        });
      });
    }

  }

在父元件操控播放器

如果今天在 app.js 新增播放按鈕與暫停按鈕,父元件要如何操縱<ITManPlayer/>播放器元件呢?

我們需要想辦法暴露<ITManPlayer/>內的 videojs 物件,讓上層元件也可以去操作它。

這裡一樣利用 ref,把 App.js改成這樣

import React, { Component } from "react";
import ITManPlayer from "./ITManPlayer";
class App extends Component {
  videoPlay = () =>{
    this.ITManPlayer.video.play();
  }
  videoPause= () =>{
    this.ITManPlayer.video.pause();
  }
  render() {
    return (
      <div className="wrap">
        <ITManPlayer
          ref={player => (this.ITManPlayer = player)}
          src="http://www.html5videoplayer.net/videos/toystory.mp4"
          eventON={{
            pause: () => {
              console.log("暫停");
            },
            play: () => {
              console.log("播放");
            }
          }}
        />
        <button onClick={this.videoPlay}>播放</button>
        <button onClick={this.videoPause}>暫停</button>
      </div>
    );
  }
}

export default App;

這麼一來,當點擊播放按鈕時,播放按鈕觸發videoPlay方法,videoPlay透過 ref 取得的this.ITManPlayer.video,直接以.play()方法操作<ITManPlayer/>播放影片。
 
 
 
 
今日內容可見我 github 備份
 

參考

Video官方文件 React


上一篇
Day26 Video.js 播放器
下一篇
Day28 Video.js 浮水印套件
系列文
前端影片與直播筆記30

尚未有邦友留言

立即登入留言