手把手從零開始—封裝一個vue視頻播放器組件

現在,在網頁上播放視頻已經越來越流行,但是網上的資料魚龍混雜,很難找到自己想要的,今天小編就自己的親身開發體驗,手把手從零開始—封裝一個vue視頻播放器組件。

作為一個老道的前端搬磚師,怎麼可能會一心閉門造車呢?還是先去網上找找輪子吧

經過在網上不斷的查閱之後,我最終選擇了video.js這個輪子,作為我的播放器。好,現在輪子找好了,乍一看,天,好像有點丑。不着急,我再來把它美化美化(二次封裝)。

引入video.js

安裝

//安裝video.js插件
npm install video.js -S 

//如果需要播放rtmp直播流,需安裝一下插件
npm install videojs-flash -S

在組件中簡單使用插件

template
<template>
  <div>
    <div data-vjs-player>
      <video ref="videoNode" class="video-js vjs-default-skin">抱歉,您的瀏覽器不支持</video>
    </div>
</template>
script
import videojs from "video.js";
//播放器中文,不能使用.js文件
import videozhCN from "video.js/dist/lang/zh-CN.json";
//樣式文件注意要加上
import "video.js/dist/video-js.css"; 
//如果要播放RTMP要使用flash 需要先npm i videojs-flash
import "videojs-flash";
export default {
    data() {
        return {
          player: null,
        };
    },
    //初始化播放器
    mounted(){
        let options = {
            autoplay: true, //自動播放
            language: "zh-CN",
            controls: this.controls, //控制條
            preload: "auto", //自動加載
            errorDisplay: true, //錯誤展示
            // fluid: true, //跟隨外層容器變化大小,跟隨的是外層寬度
            width: "500px",
            height: "500px",
            // controlBar: false,  // 設為false不渲染控制條DOM元素,只設置controls為false雖然不展示,但是存在
            // textTrackDisplay: false,  // 不渲染字幕相關DOM
            userActions: {
              hotkeys: true //是否支持熱鍵
            },
            notSupportedMessage: "此視頻暫無法播放,請稍後再試",
            techOrder: ["h5","flash"],//定義Video.js技術首選的順序
            sources: [
              {
                src: '視頻或者直播地址',
                type: 'video/mp4',
                //type: 'rtmp/flv',
              }
            ]
        };
        this.player = videojs(
            this.$refs.videoNode,
            options,
            function onPlayerReady() {
              videojs.log(`Your player is ready!`);
            }
        );
        videojs.addLanguage("zh-CN", videozhCN);
    },
    beforeDestroy() {
        if (this.player) {
          this.player.dispose();
        }
    }
}

這樣一個簡單的視頻播放功能就實現了。這裡小編也給大家整理了一些video.js常用的配置項:

常用選項
  • autoplay:true/false 播放器準備好之後,是否自動播放 【默認false】
  • controls:true/false 是否擁有控制條 【默認true】,如果設為false ,那麼只能通過api進行控制了。也就是說界面上不會出現任何控制按鈕
  • height: 視頻容器的高度,字符串或數字 單位像素 比如: height:300 or height:『300px『
  • width: 視頻容器的寬度, 字符串或數字 單位像素
  • loop : true/false 視頻播放結束後,是否循環播放
  • muted : true/false 是否靜音
  • poster: 播放前顯示的視頻畫面,播放開始之後自動移除。通常傳入一個URL
  • preload:預加載 『auto『 自動、、』metadata『 元數據信息,比如視頻長度,尺寸等、『none『 不預加載任何數據,直到用戶開始播放才開始下載
Video.js特定的選項

除非另有說明,否則默認情況下每個選項undefined

aspectRatio

類型: string

將播放器置於流體模式,並在計算播放器的動態大小時使用該值。該值應表示比率 – 由冒號(例如”16:9″或”4:3″)分隔的兩個數字。

autoSetup

類型: boolean

阻止播放器為具有data-setup屬性的媒體元素運行autoSetup 。

注意:必須在與videojs.options.autoSetup = falsevideojs源加載生效的同一時刻全局設置。

fluid

類型: boolean

設置為true,Video.js播放器將具有流暢的大小。換句話說,它將擴展以適應其容器。

此外,如果元素具有”vjs-fluid”,則此選項自動設置為true。

inactivityTimeout

類型: number

Video.js表示用戶通過”vjs-user-active”和”vjs-user-inactive”類以及”useractive”事件與玩家進行交互。

在inactivityTimeout決定了不活動的許多毫秒聲明用戶閑置之前是必需的。值為0表示沒有inactivityTimeout,用戶永遠不會被視為非活動狀態。

language

鍵入:string,默認值:瀏覽器默認值或』en』

與播放器中的一種可用語言匹配的語言代碼。這為播放器設置了初始語言,但始終可以更改。

在Video.js中了解有關語言的更多信息。

languages

類型: Object

自定義播放器中可用的語言。此對象的鍵將是語言代碼,值將是具有英語鍵和翻譯值的對象。

在Video.js中了解有關語言的更多信息

注意:通常,不需要此選項,最好將自定義語言傳遞給videojs.addLanguage()所有玩家!

nativeControlsForTouch

類型: boolean

明確設置關聯技術選項的默認值。

notSupportedMessage

類型: string

允許覆蓋Video.js無法播放媒體源時顯示的默認消息。

playbackRates

類型: Array

嚴格大於0的數字數組,其中1表示常速(100%),0.5表示半速(50%),2表示雙速(200%)等。如果指定,Video.js顯示控件(類vjs-playback-rate)允許用戶從選擇數組中選擇播放速度。選項以從下到上的指定順序顯示。

例如:

videojs('my-player', {
  playbackRates: [0.5, 1, 1.5, 2]
});

sources

類型: Array

一組對象,它們反映了本機元素具有一系列子元素的能力。這應該是帶有src和type屬性的對象數組。例如:

videojs('my-player', {
  sources: [{
    src: '//path/to/video.mp4',
    type: 'video/mp4'
  }, {
    src: '//path/to/video.webm',
    type: 'video/webm'
  }]
});

使用元素將具有相同的效果:

<video ...>
  <source src="//path/to/video.mp4" type="video/mp4">
  <source src="//path/to/video.webm" type="video/webm">
</video>

techCanOverridePoster

類型: boolean

使技術人員有可能覆蓋玩家的海報並融入玩家的海報生命周期。當使用多個技術時,這可能很有用,每個技術都必須在播放新源時設置自己的海報。

techOrder

輸入:Array,默認值:[『html5』]

定義Video.js技術首選的順序。默認情況下,這意味着Html5首選技術。其他註冊的技術將在此技術之後按其註冊順序添加。

nativeVideoTracks

類型: boolean

可以設置為false禁用本機視頻軌道支持。最常用於videojs-contrib-hls。

nativeControlsForTouch

類型: boolean

只有技術支持Html5,此選項可以設置true為強制觸摸設備的本機控件。

美化video.js輪子

播放按鈕居中

按照上面簡單的使用方式使用之後,我們會發現視頻播放待播放頁面是這樣的:

播放按鈕默認在左上角,是作者認為會遮擋內容考慮的,不過這個是可以根據參數修改的,我們只需要給video標籤加一個class(vjs-big-play-centered)就可以了。

<video ref="videoNode" class="video-js vjs-default-skin vjs-big-play-centered"></video>

加完之後效果如下:

加載中狀態美化

video.js在播放視頻的時候,有一個默認的加載中,這裡我根據自己的想法提供了一種自定義加載中頁面的思路,如有錯,請大佬指正。

主要思路:

在播放器的上面懸浮覆蓋一層div,用於顯示自定義加載中的內容,再通過一個變量值控制這個div是否加載,通過監聽視頻的一個加載狀態更新變量的值,以達到自定義加載中頁面的目的。

主要代碼:

template
註:不是完整代碼,只是關鍵代碼

<div :style="{width:'100%',position:'relative',height:height}">
    <div data-vjs-player style="width:100%">
      <video ref="videoNode" class="video-js vjs-default-skin vjs-big-play-centered">抱歉,您的瀏覽器不支持</video>
    </div>
    <div
      v-if="loading"
      :style="{width:'100%',height:height,position:'absolute',left:'0px',top:'0px'}"
    >
      <img
        :style="{width:'100%',height:height}"
        src="//img.zcool.cn/community/0113b1576a43e90000018c1b87042d.gif"
      />
    </div>
</div>

script
註:不是完整代碼,只是關鍵代碼

data() {
    return {
      loading: false
    };
 },
 let options = {
    autoplay: false, //自動播放
    .....省略代碼
 };
this.player = videojs(
    this.$refs.videoNode,
    options,
    function onPlayerReady() {
      videojs.log(`Your player${self.index} is ready!`);
      _this.loading = true;
      _this.player.play();
      _this.player.one("playing", function() {
        // 監聽播放
        // console.log("播放器開始播放");
        _this.loading = false;
      });
    
    }
);

效果如下:

當然,內容你也可以自定義。

視頻打點

平時生活中,我們在看視頻的時候,經常會看到,有些視頻的進度條上面有一些小點,然後鼠標放上去會出現一些文字提示,那麼我們的web播放器上面能不能也添加這個功能呢?當然是可以的!

首先還是去找了一波輪子,最後找到了videojs-markers這個輪子來實現該功能。

安裝videojs-markers
npm i videojs-markers -S
引入videojs-markers
import "videojs-markers";
//引入樣式
import "videojs-markers/dist/videojs.markers.css";
使用videojs-markers
this.player.markers({
  markerStyle: {
    // 標記點樣式
    width: "0.7em",
    height: "0.7em",
    bottom: "-0.20em",
    "border-radius": "50%",
    "background-color": "orange",
    position: "absolute"
  },
  //鼠標移入標記點的提示
  markerTip: {
    display: true,//是否顯示
    /*
      用於動態構建標記提示文本的回調函數。
      只需返回一個字符串,參數標記是傳遞給插件的標記對象
     */
    text: function(marker) {
      return marker.text;
    }
  },
  markers: [
        {
          time: 20,
          text:'點位一'
        },
        {
          time: 40,
          text:'點位二'
        },
        {
          time: 130,
          text:'點位三'
        },
        {
          time: 200,
          text:'點位四'
        }
    ],
});

效果如下:

封裝組件

播放器基本功能實現了,那麼最後一步就是封裝組件了。封裝的思路很簡單,就是將一些變化的屬性通過props的方式從父組件中傳入。

常用需要封裝的屬性

註:此處只列舉了部分,可以示實際情況添加或者刪除

  • src : 視頻或者直播的地址
  • height :播放器的高度
  • controls :控制條是否需要顯示
  • markers :視頻打點的數據源
  • type :播放視頻的類型

根據這些屬性,我們來改造一下我們的組件

template

<template>
  <div :style="{width:'100%',position:'relative',height:height}">
    <div data-vjs-player style="width:100%">
      <video ref="videoNode" class="video-js vjs-default-skin vjs-big-play-centered">抱歉,您的瀏覽器不支持</video>
    </div>
    <div
      v-if="loading"
      :style="{width:'100%',height:height,position:'absolute',left:'0px',top:'0px'}"
    >
      <img
        :style="{width:'100%',height:height}"
        src="//img.zcool.cn/community/0113b1576a43e90000018c1b87042d.gif"
      />
    </div>
  </div>
</template>

script

data() {
    return {
      player: null,
      loading: false
    };
},


let options = {
        // autoplay: true, //自動播放
        language: "zh-CN",
        controls: this.controls, //控制條
        preload: "auto", //自動加載
        errorDisplay: true, //錯誤展示
        // fluid: true, //跟隨外層容器變化大小,跟隨的是外層寬度
        width: "100%",
        height: this.height,
        // controlBar: false,  // 設為false不渲染控制條DOM元素,只設置controls為false雖然不展示,但還是存在
        // textTrackDisplay: false,  // 不渲染字幕相關DOM
        userActions: {
          hotkeys: true //是否支持熱鍵
        },
        notSupportedMessage: "此視頻暫無法播放,請稍後再試",
        techOrder: ["flash"],
        sources: [
          {
            src: this.src,
            type: this.type
          }
        ]
      };
      let _this = this;
      this.player = videojs(
        this.$refs.videoNode,
        options,
        function onPlayerReady() {
          videojs.log(`Your player${self.index} is ready!`);
          _this.loading = true;
          _this.player.play();
          _this.player.one("playing", function() {
            // 監聽播放
            // console.log("播放器開始播放");
            _this.loading = false;
          });
        }
      );
      videojs.addLanguage("zh-CN", videozhCN);
      if (this.markers) {
        this.player.markers({
          markerStyle: {
            // 標記樣式
            width: "0.7em",
            height: "0.7em",
            bottom: "-0.20em",
            "border-radius": "50%",
            "background-color": "orange",
            position: "absolute"
          },
          markerTip: {
            display: true,
            /*
              用於動態構建標記提示文本的回調函數。
              只需返回一個字符串,參數標記是傳遞給插件的標記對象
             */
            text: function(marker) {
              return marker.text;
            }
          },
          markers: this.markers,
        });
      }

動態切換視頻封裝

在視頻播放的時候,我們經常會有視頻切換之類的需求,那麼這個怎麼封裝呢?很簡單,只需要在組件中監聽src的變化,如果src發生了變化,那麼就重新加載視頻,播放視頻,代碼如下:

watch: {
    src() {
      if (this.player) {
        let _this = this;
        this.loading = true;//重新顯示加載狀態
        let myPlayer = this.player;
        myPlayer.off("timeupdate");//清空時間
        myPlayer.reset();//重置播放器
        myPlayer.pause();//暫停播放
        myPlayer.src([//重新設置播放源
          {
            src: this.src,
            type: "rtmp/flv"
          }
        ]);
        myPlayer.load(this.src);//重新加載視頻
        myPlayer.play();//播放視頻
        myPlayer.one("playing", function() {
          // 加載完成,開始播放
          // console.log("播放器開始播放");
          _this.loading = false;//隱藏加載狀態
        });
      }
    }
},

這樣一個簡單的視頻播放器就封裝好了。

使用示例

組件封裝完成,免不了使用一下,代碼如下:

<template>
  <basic-container>
    <el-row class="video-test">
      <el-col :span="16" class="video-test-left">
        <videoPlayer :controls="true" height="600px" :src="url" type="video/mp4" :markers="markers"/>
      </el-col>
    </el-row>
  </basic-container>
</template>
<script>
import videoPlayer from "@/components/videoPlayer/videoPlayer";
export default {
  components: { videoPlayer },
  data() {
    return {
      url: "//127.0.0.1/test.mp4",
      markers: [
        {
          time: 20,
          text:'點位一'
        },
        {
          time: 40,
          text:'點位二點位二點位二點位二點位二點位二點位二點位二'
        },
        {
          time: 130,
          text:'點位三點位三點位三點位三點位三點位三'
        },
        {
          time: 200,
          text:'點位四點位四點位四點位四點位四點位四'
        }
      ]
    };
  }
};
</script>
<style lang="scss">
</style> 

參考:

videojs中文文檔詳解 //blog.csdn.net/a0405221/article/details/80923090

Tags: