手把手從零開始---封裝一個vue視頻播放器組件
現(xiàn)在,在網(wǎng)頁上播放視頻已經(jīng)越來越流行,但是網(wǎng)上的資料魚龍混雜,很難找到自己想要的,今天小編就自己的親身開發(fā)體驗,手把手從零開始—封裝一個vue視頻播放器組件。
作為一個老道的前端搬磚師,怎么可能會一心閉門造車呢?還是先去網(wǎng)上找找輪子吧
經(jīng)過在網(wǎng)上不斷的查閱之后,我最終選擇了video.js這個輪子,作為我的播放器。好,現(xiàn)在輪子找好了,乍一看,天,好像有點丑。不著急,我再來把它美化美化(二次封裝)。
 引入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,  // 設(shè)為false不渲染控制條DOM元素,只設(shè)置controls為false雖然不展示,但是存在
             // textTrackDisplay: false,  // 不渲染字幕相關(guān)DOM
             userActions: {
               hotkeys: true //是否支持熱鍵
             },
             notSupportedMessage: "此視頻暫無法播放,請稍后再試",
             techOrder: ["h5","flash"],//定義Video.js技術(shù)首選的順序
             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();
         }
     }
 }
這樣一個簡單的視頻播放功能就實現(xiàn)了。這里小編也給大家整理了一些video.js常用的配置項:
 常用選項
    autoplay:true/false 播放器準備好之后,是否自動播放 【默認false】
     controls:true/false 是否擁有控制條 【默認true】,如果設(shè)為false ,那么只能通過api進行控制了。也就是說界面上不會出現(xiàn)任何控制按鈕
     height: 視頻容器的高度,字符串或數(shù)字 單位像素 比如: height:300 or height:‘300px‘
     width: 視頻容器的寬度, 字符串或數(shù)字 單位像素
     loop : true/false 視頻播放結(jié)束后,是否循環(huán)播放
     muted : true/false 是否靜音
     poster: 播放前顯示的視頻畫面,播放開始之后自動移除。通常傳入一個URL
     preload:預(yù)加載 ‘a(chǎn)uto‘ 自動、、’metadata‘ 元數(shù)據(jù)信息,比如視頻長度,尺寸等、‘none‘ 不預(yù)加載任何數(shù)據(jù),直到用戶開始播放才開始下載
Video.js特定的選項
除非另有說明,否則默認情況下每個選項undefined
 aspectRatio
類型: string
將播放器置于流體模式,并在計算播放器的動態(tài)大小時使用該值。該值應(yīng)表示比率 - 由冒號(例如"16:9"或"4:3")分隔的兩個數(shù)字。
 autoSetup
類型: boolean
阻止播放器為具有data-setup屬性的媒體元素運行autoSetup 。
注意:必須在與videojs.options.autoSetup = falsevideojs源加載生效的同一時刻全局設(shè)置。
 fluid
類型: boolean
設(shè)置為true,Video.js播放器將具有流暢的大小。換句話說,它將擴展以適應(yīng)其容器。
此外,如果元素具有"vjs-fluid",則此選項自動設(shè)置為true。
 inactivityTimeout
類型: number
Video.js表示用戶通過"vjs-user-active"和"vjs-user-inactive"類以及"useractive"事件與玩家進行交互。
在inactivityTimeout決定了不活動的許多毫秒聲明用戶閑置之前是必需的。值為0表示沒有inactivityTimeout,用戶永遠不會被視為非活動狀態(tài)。
 language
鍵入:string,默認值:瀏覽器默認值或’en’
與播放器中的一種可用語言匹配的語言代碼。這為播放器設(shè)置了初始語言,但始終可以更改。
在Video.js中了解有關(guān)語言的更多信息。
 languages
類型: Object
自定義播放器中可用的語言。此對象的鍵將是語言代碼,值將是具有英語鍵和翻譯值的對象。
在Video.js中了解有關(guān)語言的更多信息
注意:通常,不需要此選項,最好將自定義語言傳遞給videojs.addLanguage()所有玩家!
 nativeControlsForTouch
類型: boolean
明確設(shè)置關(guān)聯(lián)技術(shù)選項的默認值。
 notSupportedMessage
類型: string
允許覆蓋Video.js無法播放媒體源時顯示的默認消息。
 playbackRates
類型: Array
嚴格大于0的數(shù)字數(shù)組,其中1表示常速(100%),0.5表示半速(50%),2表示雙速(200%)等。如果指定,Video.js顯示控件(類vjs-playback-rate)允許用戶從選擇數(shù)組中選擇播放速度。選項以從下到上的指定順序顯示。
例如:
videojs('my-player', {
   playbackRates: [0.5, 1, 1.5, 2]
 });
sources
類型: Array
一組對象,它們反映了本機元素具有一系列子元素的能力。這應(yīng)該是帶有src和type屬性的對象數(shù)組。例如:
videojs('my-player', {
   sources: [{
     src: '//path/to/video.mp4',
     type: 'video/mp4'
   }, {
     src: '//path/to/video.webm',
     type: 'video/webm'
   }]
 });
使用元素將具有相同的效果:
<video ...>
   <source src="http://path/to/video.mp4" type="video/mp4">
   <source src="http://path/to/video.webm" type="video/webm">
 </video>
techCanOverridePoster
類型: boolean
使技術(shù)人員有可能覆蓋玩家的海報并融入玩家的海報生命周期。當使用多個技術(shù)時,這可能很有用,每個技術(shù)都必須在播放新源時設(shè)置自己的海報。
 techOrder
輸入:Array,默認值:[‘html5’]
定義Video.js技術(shù)首選的順序。默認情況下,這意味著Html5首選技術(shù)。其他注冊的技術(shù)將在此技術(shù)之后按其注冊順序添加。
 nativeVideoTracks
類型: boolean
可以設(shè)置為false禁用本機視頻軌道支持。最常用于videojs-contrib-hls。
 nativeControlsForTouch
類型: boolean
只有技術(shù)支持Html5,此選項可以設(shè)置true為強制觸摸設(shè)備的本機控件。
 美化video.js輪子
 播放按鈕居中
按照上面簡單的使用方式使用之后,我們會發(fā)現(xiàn)視頻播放待播放頁面是這樣的:

播放按鈕默認在左上角,是作者認為會遮擋內(nèi)容考慮的,不過這個是可以根據(jù)參數(shù)修改的,我們只需要給video標簽加一個class(vjs-big-play-centered)就可以了。
<video ref="videoNode" class="video-js vjs-default-skin vjs-big-play-centered"></video>
加完之后效果如下:

加載中狀態(tài)美化
video.js在播放視頻的時候,有一個默認的加載中,這里我根據(jù)自己的想法提供了一種自定義加載中頁面的思路,如有錯,請大佬指正。
 主要思路:
在播放器的上面懸浮覆蓋一層div,用于顯示自定義加載中的內(nèi)容,再通過一個變量值控制這個div是否加載,通過監(jiān)聽視頻的一個加載狀態(tài)更新變量的值,以達到自定義加載中頁面的目的。
 主要代碼:
template
 注:不是完整代碼,只是關(guān)鍵代碼
<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="https://img.zcool.cn/community/0113b1576a43e90000018c1b87042d.gif"
       />
     </div>
 </div>
script
 注:不是完整代碼,只是關(guān)鍵代碼
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() {
         // 監(jiān)聽播放
         // console.log("播放器開始播放");
         _this.loading = false;
       });
     
     }
 );
效果如下:

當然,內(nèi)容你也可以自定義。
 視頻打點
平時生活中,我們在看視頻的時候,經(jīng)常會看到,有些視頻的進度條上面有一些小點,然后鼠標放上去會出現(xiàn)一些文字提示,那么我們的web播放器上面能不能也添加這個功能呢?當然是可以的!
    首先還是去找了一波輪子,最后找到了videojs-markers這個輪子來實現(xiàn)該功能。
安裝videojs-markers
npm i videojs-markers -S
1
引入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,//是否顯示
     /*
       用于動態(tài)構(gòu)建標記提示文本的回調(diào)函數(shù)。
       只需返回一個字符串,參數(shù)標記是傳遞給插件的標記對象
      */
     text: function(marker) {
       return marker.text;
     }
   },
   markers: [
         {
           time: 20,
           text:'點位一'
         },
         {
           time: 40,
           text:'點位二'
         },
         {
           time: 130,
           text:'點位三'
         },
         {
           time: 200,
           text:'點位四'
         }
     ],
 });
效果如下:


封裝組件
播放器基本功能實現(xiàn)了,那么最后一步就是封裝組件了。封裝的思路很簡單,就是將一些變化的屬性通過props的方式從父組件中傳入。
 常用需要封裝的屬性
注:此處只列舉了部分,可以示實際情況添加或者刪除
    src : 視頻或者直播的地址
     height :播放器的高度
     controls :控制條是否需要顯示
     markers :視頻打點的數(shù)據(jù)源
     type :播放視頻的類型
根據(jù)這些屬性,我們來改造一下我們的組件
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="https://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,  // 設(shè)為false不渲染控制條DOM元素,只設(shè)置controls為false雖然不展示,但還是存在
         // textTrackDisplay: false,  // 不渲染字幕相關(guān)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() {
             // 監(jiān)聽播放
             // 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,
             /*
               用于動態(tài)構(gòu)建標記提示文本的回調(diào)函數(shù)。
               只需返回一個字符串,參數(shù)標記是傳遞給插件的標記對象
              */
             text: function(marker) {
               return marker.text;
             }
           },
           markers: this.markers,
         });
       }
動態(tài)切換視頻封裝
在視頻播放的時候,我們經(jīng)常會有視頻切換之類的需求,那么這個怎么封裝呢?很簡單,只需要在組件中監(jiān)聽src的變化,如果src發(fā)生了變化,那么就重新加載視頻,播放視頻,代碼如下:
watch: {
     src() {
       if (this.player) {
         let _this = this;
         this.loading = true;//重新顯示加載狀態(tài)
         let myPlayer = this.player;
         myPlayer.off("timeupdate");//清空時間
         myPlayer.reset();//重置播放器
         myPlayer.pause();//暫停播放
         myPlayer.src([//重新設(shè)置播放源
           {
             src: this.src,
             type: "rtmp/flv"
           }
         ]);
         myPlayer.load(this.src);//重新加載視頻
         myPlayer.play();//播放視頻
         myPlayer.one("playing", function() {
           // 加載完成,開始播放
           // console.log("播放器開始播放");
           _this.loading = false;//隱藏加載狀態(tài)
         });
       }
     }
 },
這樣一個簡單的視頻播放器就封裝好了。
 使用示例
組件封裝完成,免不了使用一下,代碼如下:
<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: "http://127.0.0.1/test.mp4",
       markers: [
         {
           time: 20,
           text:'點位一'
         },
         {
           time: 40,
           text:'點位二點位二點位二點位二點位二點位二點位二點位二'
         },
         {
           time: 130,
           text:'點位三點位三點位三點位三點位三點位三'
         },
         {
           time: 200,
           text:'點位四點位四點位四點位四點位四點位四'
         }
       ]
     };
   }
 };
 </script>
 <style lang="scss">
 </style>
  
歡迎關(guān)注微信公眾號:猴哥說前端


                   
 個人中心
 退出


 
  分類導(dǎo)航