当前位置:首页 » 《随便一记》 » 正文

使用videjs+vue2+elementui自定义播放器控件

7 人参与  2023年04月05日 11:59  分类 : 《随便一记》  评论

点击全文阅读


一、安装项目所需依赖

videojs依赖:

npm install --save-dev video.js

elementui依赖(这个图方便就不按需引入了):

npm i element-ui -S

二、main.js修改

增加以下几行:

import videojs from 'video.js'import elemenui from 'element-ui'import 'element-ui/lib/theme-chalk/index.css';Vue.prototype.$videoJs = videojs;Vue.use(elemenui)

三、准备html结构

1、准备两个组件

在components文件夹下创建两个组件videoComponent和videoPlayer——

videoComponent挂载到App组件上

videoPlayer挂载到videoComponent上

2、各组件中的html结构

先把两个组件最基本的结构搭好

videoComponent:

<template>  <div class="container">    <video-player :options="videoOptions" class="video-css"></video-player>  </div></template><script>  import videoPlayer from './videoPlayer.vue'  export default {    name: 'videoComponent',    components:{videoPlayer},    data(){      return{        videoOptions:{            //一些视频的配置项,见下面补充...                }      }     }</script><style scoped>  .container{    width: 100%;    height: 100vh;    display: flex;    justify-content: center;    align-items: center;  }  /* 视频宽高将由此样式调整 */  .video-css{    width: 800px;    height: auto;  }</style>

videoPlayer:

<template>  <div class="videoBox">    <video ref="videoPlayer" class="video-js"></video>  </div></template><script>    import 'video.js/dist/video-js.css';    export default {        name:'videoPlayer',    }</script><style scoped>    .videoBox{        box-sizing: border-box;        position: relative;        width: 800px;        height:500px;    }</style>

App:

<template>  <div id="app">    <video-component></video-component>  </div></template><script>  import videoComponent from './components/videoComponent.vue'  export default {    name: 'App',    components: {      videoComponent    }  }</script><style>  *{    padding: 0;    margin: 0;  }</style>

3、在videoPlayer组件里实例化播放器

<script>    import 'video.js/dist/video-js.css';    export default {        name:'videoPlayer',        //接收外部组件来的参数,可实现videoPlayer组件的复用        props:{            options: {                type: Object            }        },        data(){            return{                player:null            }        },        methods:{            //定义好一个实例化播放器的方法            createVideoPlayer(){                this.player = this.$videoJS(this.$refs.videoPlayer,this.options)            }        },        //在组件挂载时调用播放器实例化方法        mounted(){            this.createVideoPlayer()        },        //组件销毁前销毁播放器        beforeDestroy(){            if(this.player){                this.player.dispose()            }        }    }</script>

4、videoComponent传递视频参数

还有很多配置项,有需要自己查

<script>   import videoPlayer from './videoPlayer.vue'  export default {    name: 'videoComponent',    components:{videoPlayer},    data(){      return{        videoOptions:{          //controls配置项决定是否显示默认控件          controls:true,          //fluid配置项根据外层css样式大小,自动填充宽高          fluid:true,          //sources配置项配置视频播放源          sources:[            {              //播放源              src:require('@/assets/kp.mp4'),              //视频类型              type:"video/mp4"            }          ]                  }      }    },     }</script>

在assets文件夹下事先放好一个视频,在配置项sources里面改一下视频路径。

走到这一步,运行项目一般可以正常播放视频了:

 接下来就可以实现播放器自定义控件

5、自定义控件的html结构

确定视频可以正常播放后就可以着手自定义控件了,控件写在videoPlayer这个组件里。先上结构,这里图标用的都是elementui的icon,音量没找到合适的就凑合着用了其他图标:

<template>  <div class="videoBox">    <video         ref="videoPlayer"        class="video-js"    >    </video>    <!-- 自定义控件 -->    <div class="controlBar">        <div class="progressBar"  @mousedown="isDraging = true" @mouseup="isDraging = false">            <el-slider                    v-model="currentTimeVal"                :max="totalTimeVal"                :format-tooltip="timeFormat"                @change="progressUpdate"            >            </el-slider>        </div>        <div class="controlBtnBox">            <div class="left">                <i class="el-icon-video-play icon-size"></i>                <span>00:00:00 / 00:00:00</span>            </div>            <div class="right">                <i class="el-icon-d-arrow-left icon-size" @click="back(player.currentTime())"></i>                <i class="el-icon-d-arrow-right icon-size" @click="forward(player.currentTime())"></i>                <i class="el-icon-bell icon-size" @click="toShowVolumeBar"></i>                <div id="volumeBar">                    <el-slider                        v-model="volume"                        vertical                        height="100px">                    </el-slider>                </div>            </div>            <div class="rateBox">                <span @click="toShowOptions">{{ratedisplay}}</span>                <div class="rateOptions" v-show="isShowRateOptions">                    <span                         v-for="r,index in rateOptions"                         :key="index"                         @click="setPlayRate(r)"                    >                        {{r}}x                    </span>                </div>            </div>        </div>    </div>  </div></template>

结构效果图最终长这样,这里样式不多废话,跟完整代码放在文章最后面。

 6、准备数据

 结构搞定之后,在data里准备好后续需要用到的数据

        data(){            return{                player:null,                //当前播放速率                rate:1.0,                //播放速率                rateOptions:[2.0,1.75,1.5,1.0,0.75,0.5],                //显示速率选项                isShowRateOptions:false,                //音量                volume:30,                //是否暂停                isPaused:true,                //当前播放时间点和视频总时长                currentTime:'00:00:00',                totalTime:'00:00:00',                //进度条的当前值,必须为number类型                currentTimeVal:0,                //进度条最大值,必须为number类型                totalTimeVal:0,                //是否在拖到进度条                isDraging:false            }        },

rate和rateOptions用在哪上面的代码已经写了,isShowRateOptions后面用来隐藏倍速选项那个框的这里不重要先不去理。主要是volume,它绑定在el-slider上是该滑块的默认值,后面改音量会用到;currentTime和 totalTime用于动态显示当前视频播放的具体时间,以及视频的总时长; currentTimeVal和totalTimeVal是改变进度条、实现时长跳转的主要数据;isPaused用来决定自定义控件的图标是暂停还是播放。

7、播放暂停、进度条实时跟进,拉到进度条实现跳转

首先从简单的暂停和播放做起。在html结构中,我们应该根据isPaused来决定显示哪个图标,同时将前面显示时长的假数据换成data里的currentTime和totalTime:

<i :class="[isPaused ? 'el-icon-video-play' : 'el-icon-video-pause']" class=" icon-size" v-show="isPaused"></i><span>{{currentTime}} / {{totalTime}}</span>

然后给图标绑定上一个togglePlay的函数,来响应点击后实现播放或暂停:

            //控制视频的播放与暂停            togglePlay(){                this.isPaused = !this.isPaused                if(!this.isPaused){                    this.player.play()                }else{                    this.player.pause()                }            },

时间格式化:

//视频时长格式化timeFormat(time){    let hour = Math.floor(time / 3600),        minute = Math.floor((time % 3600) / 60),        second = Math.floor(time % 60);    hour = hour < 10 ? "0" + hour : hour;    minute = minute < 10 ? "0" + minute : minute;    second = second < 10 ? "0" + second : second;    return `${hour}:${minute}:${second}`;},

获取视频总时长:

//获取视频的总时长和进度条最大值getTotalTime(){    this.totalTime = this.timeFormat(this.player.duration())    this.totalTimeVal = Math.floor(this.player.duration())},

更新视频当前播放时间、进度条实时跟进:

进度条的实现原理其实就是video的timeUpdate事件可以监测到视频的播放进度,在这个事件中可以一直获取到视频当前的播放时间,然后将这个值赋给滑块绑定的currentTimeVal,这样就能在播放过程中跟着改变滑块的位置了。

//更新视频当前播放时间timeUpdate(){    //如果当前正在拉到进度条,先停止更新当前播放时间,直接return结束这个函数    //没有这一句会出现拉动进度条跳转失败的bug    if(this.isDraging) return    this.currentTime = this.timeFormat(this.player.currentTime())    this.currentTimeVal = this.player.currentTime()                  //当前时间更新到等于总时长时,要改变视频的播放状态按钮    if(this.currentTime === this.totalTime){        this.isPaused = true    }               },

拉到进度条跳转到指定位置播放:

这一功能的实现就是el-slider有一个change事件,在拖拽滑块松开鼠标后触发,这时只要在鼠标松开后,改变播放器的currentTime属性的值,

这里要稍微注意一下:因为我们在拉动进度条的时候,视频还处于播放状态,那么意味着上一步我们更新进度条时长的那个函数获取到的currentTime值也会改变el-slider的值,所以在上一步的函数中,我们需要监测进度条是否在拉动,如果是,我们应该停止执行那个函数。监听只需要在进度条外层的div上绑定一个mouseon和mousedown事件,鼠标按住时让isDragging等于false,然后在timeUpdate函数中通过isDragging来判断进度条是否处于拖拽的状态。

//进度条拉动时更新进度条值并从拉到的位置播放progressUpdate(val){    this.player.currentTime(val)     // 虽然mouseup已经可以改变isDraging的值,但下面这句不能少,不然视频播放结束再点击播放时,进度条不会回到最开始位置      this.isDraging = false           },

8、更新速率、改变音量

//改变速率setPlayRate(rate){    this.rate = rate;    this.player.playbackRate(rate);    this.isShowRateOptions = false;},//改变音量changeVolume(val){    this.volume = val    //由于h5规定volum的值在0-1之间,所以这里要对获取到的val做一个处理(滑块的val是从0-100)    this.player.volume(val / 100)},//快进forward(ct){    this.progressUpdate(ct + 10)},//后退back(ct){    this.progressUpdate(ct - 10)}

9、完整代码

VideoComponent:

<template>  <div class="container">    <video-player      :options="videoOptions"          class="video-css"    >    </video-player>  </div></template><script>   import videoPlayer from './videoPlayer.vue'  export default {    name: 'videoComponent',    components:{videoPlayer},    data(){      return{        videoOptions:{          //controls配置项决定是否显示默认控件,因为这里要做自定义的控件,就不显示了          controls:false,          //fluid配置项根据外层css样式大小,自动填充宽高          fluid:true,          //sources配置项配置视频播放源          sources:[            {              //播放源              src:require('@/assets/kp.mp4'),              //视频类型              type:"video/mp4"            }          ],                  }      }    },     }</script><style scoped>  .container{    width: 800px;    height: 100vh;    display: flex;    justify-content: center;    align-items: center;  }  /* 视频宽高由此样式调整 */  .video-css{    width: 800px;    height:auto;  }</style>

VideoPlayer:

<template>  <div class="videoBox">    <video         ref="videoPlayer"        class="video-js"        @canplay="getTotalTime"        @timeupdate="timeUpdate"    >    </video>    <!-- 自定义控件 -->    <div class="controlBar">        <div class="progressBar" @mousedown="isDraging = true" @mouseup="isDraging = false">            <el-slider                    v-model="currentTimeVal"                :max="totalTimeVal"                :format-tooltip="timeFormat"                @change="progressUpdate"            >            </el-slider>        </div>        <div class="controlBtnBox">            <div class="left">                <i                     :class="[isPaused ? 'el-icon-video-play' : 'el-icon-video-pause']"                     class=" icon-size"                     @click="togglePlay()"                >                </i>                <span>{{currentTime}}/{{totalTime}}</span>            </div>            <div class="right">                <i class="el-icon-d-arrow-left icon-size" @click="back(player.currentTime())></i>                <i class="el-icon-d-arrow-right icon-size" @click="forward(player.currentTime())></i>                <i class="el-icon-bell icon-size"  @click="toShowVolumeBar"></i>                <div id="volumeBar" v-show="isShowVolumeBar">                    <el-slider                        v-model="volume"                        vertical                        height="100px"                        @input="changeVolume"                    >                    </el-slider>                </div>            </div>            <div class="rateBox">                <span @click="toShowOptions">{{ratedisplay}}</span>                <div class="rateOptions" v-show="isShowRateOptions">                    <span                         v-for="r,index in rateOptions"                         :key="index"                         @click="setPlayRate(r)"                    >                        {{r}}x                    </span>                </div>            </div>        </div>    </div>  </div></template><script>    import 'video.js/dist/video-js.css';    export default {        name:'videoPlayer',        //接收来自父组件videoComponent的video的具体配置信息,这样可以实现对VideoPlayer组件的复用        props:{            options: {                type: Object            }        },        //用计算属性来实现当速率为1时,显示“倍速”        computed:{            ratedisplay(){                if(this.rate == 1){                    return '倍速'                }else{                    return this.rate + 'x'                }            }        },        data(){            return{                                player:null,                //当前播放速率                rate:1.0,                //播放速率                rateOptions:[2.0,1.75,1.5,1.0,0.75,0.5],                //显示速率选项和音量选项                isShowRateOptions:false,                isShowVolumeBar:false,                //音量                volume:30,                //是否暂停                isPaused:true,                //当前播放时间点和视频总时长                currentTime:'00:00:00',                totalTime:'00:00:00',                //进度条的当前值,必须为number类型                currentTimeVal:0,                //进度条最大值,必须为number类型                totalTimeVal:0,                //是否在拖到进度条                isDraging:false            }        },        methods:{            createVideoPlayer(){                this.player = this.$videoJS(this.$refs.videoPlayer,this.options)            },            //显示速率选项            toShowOptions(){                this.isShowRateOptions = !this.isShowRateOptions            },            toShowVolumeBar(){                this.isShowVolumeBar = !this.isShowVolumeBar            },            //视频时长格式化            timeFormat(time){                let hour = Math.floor(time / 3600),                    minute = Math.floor((time % 3600) / 60),                    second = Math.floor(time % 60);                hour = hour < 10 ? "0" + hour : hour;                minute = minute < 10 ? "0" + minute : minute;                second = second < 10 ? "0" + second : second;                return `${hour}:${minute}:${second}`;            },            //获取视频的总时长和进度条最大值            getTotalTime(){                this.totalTime = this.timeFormat(this.player.duration())                this.totalTimeVal = Math.floor(this.player.duration())            },            //改变速率            setPlayRate(rate){                this.rate = rate;                this.player.playbackRate(rate);                this.isShowRateOptions = false;            },            //控制视频的播放与暂停            togglePlay(){                console.log()                this.isPaused = !this.isPaused                if(!this.isPaused){                    this.player.play()                }else{                    this.player.pause()                }            },             //更新视频当前播放时间            timeUpdate(){                //如果当前正在拉到进度条,先停止更新当前播放时间,直接return结束这个函数                //没有这一句会出现拉动进度条跳转失败的bug                if(this.isDraging) return                this.currentTime = this.timeFormat(this.player.currentTime())                this.currentTimeVal = this.player.currentTime()                              //当前时间更新到等于总时长时,要改变视频的播放状态按钮                if(this.currentTime === this.totalTime){                                        this.isPaused = true                }                           },            //进度条拉动时更新进度条值并从拉到的位置播放            progressUpdate(val){                this.player.currentTime(val)                 // 虽然mouseup已经可以改变isDraging的值,但下面这句不能少,不然视频播放结束再点击播放时,进度条不会回到最开始位置                  this.isDraging = false                       },            //改变音量            changeVolume(val){                this.volume = val                //由于h5规定volum的值在0-1之间,所以这里要对获取到的val做一个处理(滑块的val是从0-100)                this.player.volume(val / 100)            },            //快进            forward(ct){                this.progressUpdate(ct + 10)            },            //后退            back(ct){                this.progressUpdate(ct - 10)            }                    },        mounted(){            this.createVideoPlayer()        },        beforeDestroy(){            if(this.player){                this.player.dispose()            }        }    }</script><style scoped>    .videoBox{        box-sizing: border-box;        position: relative;        width: 800px;        height:500px;        background-color: rgb(73, 156, 128);    }    .controlBar{        width: 90%;        height: 55px;        position:absolute;        bottom: 20px;        left: 5%;        background-color:#817f7f5a;        box-sizing: border-box;        color: rgb(233, 231, 231);    }    .progressBar{        box-sizing: border-box;        position: relative;        width: 100%;        padding: 10px;        height: 10%;        /* background-color: aliceblue; */    }    .controlBtnBox{        box-sizing: border-box;        width: 100%;        height:60%;        display: flex;        justify-content: space-between;        align-items: center;         }    /* 以下强行修改了el-slider样式 */    .progressBar /deep/ .el-slider__bar{        height: 3px;        background-color: #409EFF;        border-top-left-radius: 3px;        border-bottom-left-radius: 3px;        position: absolute;    }    .progressBar /deep/ .el-slider__button{        height: 8px;        width: 8px;    }    .progressBar /deep/ .el-slider__runway{        margin-top:1px;        margin-bottom: 1px;        height: 3px;    }    .progressBar /deep/.el-slider__button-wrapper{        width: 28px;        height: 33px;    }     .icon-size{       font-size: 25px;       cursor: pointer;    }    .left{        padding-left:10px ;        width: 50%;        display: flex;        align-items: center;    }    .left span{               margin-left: 20px;    }    .right{        width: 15%;        display: flex;        justify-content: space-around;       position: relative;    }    .right i{        display: block;    }    #volumeBar{        width: 30px;        height: 120px;        background-color: #817f7f5a;        position: absolute;        top:-150px;        right: 4px;        display: flex;        justify-content: center;        align-items: center;    }    .rateBox{        width: 15%;        cursor: pointer;    }    .rateOptions{        width: 80px;        height: 180px;        background-color: #817f7f5a;        position: absolute;        top:-185px;        right: 50px;        display: flex;        flex-wrap: wrap;        align-content: center;    }    .rateOptions span{        display: block;        width: 100%;        height: 30px;        text-align: center;        line-height: 30px;    }    .rateOptions span:hover{       background-color: #cec9c95a;       color: #409EFF;    }</style>

App:

<template>  <div id="app">    <video-component></video-component>  </div></template><script>  import videoComponent from './components/videoComponent.vue'  export default {    name: 'App',    components: {      videoComponent    }  }</script><style>  *{    padding: 0;    margin: 0;  }  #app{    display: flex;    justify-content: center;    width: 100%;  }</style>

结束,暂时还没发现什么bug。

但测试用的是的视频是放在本地的,没有涉及到资源加载,如果视频资源是走网络请求的话还得再改。。

参考链接:

以下是实现的时候参考的几篇文章

vue中自定义视频:

vue 中 自定义视频video_粥粥_的博客-CSDN博客

video.js使用教程:

Video.js使用教程一(详解)_lucky-peach的博客-CSDN博客


点击全文阅读


本文链接:http://zhangshiyu.com/post/58438.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1