一、安装项目所需依赖

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-sliderv-model="volume"verticalheight="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.isPausedif(!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结束这个函数//没有这一句会出现拉动进度条跳转失败的bugif(this.isDraging) returnthis.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-sliderv-model="volume"verticalheight="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.isPausedif(!this.isPaused){this.player.play()}else{this.player.pause()}},//更新视频当前播放时间timeUpdate(){//如果当前正在拉到进度条,先停止更新当前播放时间,直接return结束这个函数//没有这一句会出现拉动进度条跳转失败的bugif(this.isDraging) returnthis.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博客

使用videjs+vue2+elementui自定义播放器控件相关推荐

  1. Android自定义一个播放器控件

    介绍 最近要使用播放器做一个简单的视频播放功能,开始学习VideoView,在横竖屏切换的时候碰到了点麻烦,不过在查阅资料后总算是解决了.在写VideoView播放视频时候定义控制的代码全写在Actv ...

  2. 基于MediaPlayer的Android播放器控件

    Android自身的播放控件在界面定制上不是很方便,而且没有针对播放流进行加工处理的相关接口.于是自己写了一个基于MediaPlayer的播放器控件.该控件有以下特点: 支持开发者对播放界面进行任意的 ...

  3. VS2010/MFC对话框程序调用Windows Media Player播放器控件

    MFC对话框程序调用Windows Media Player播放器控件播放打开的avi格式的文件,具体步骤如下: 1.根据MFC向导提示,创建一个默认的对话框项目TestMediaPlayer. 2. ...

  4. PPT中WMP播放器控件的使用方法

    Windows Media Player是个十分好用的播放器,它支持多个格式视频.音频文件的播放,在PPT 2003中使用的频率非常的高. 本文以PPT 2003为例,讲解一下PPT 2003中WMP ...

  5. vlc集成c#_C# Winform开发程序调用VLC播放器控件播放视频.

    VLC是个好东西,支持的格式多,还无广告,关键还有调用它的播放控件不用安装. 开个文章记录下调用这个控件的流水账,以便以后需要的时候查阅 创建工程 首先新建一个Winform工程. 这里姑且叫做VLC ...

  6. HTML如何设置音频播放器控件的大小

    如下所示: <object height="100" width="100" data="../i/horse.mp3"> &l ...

  7. 微信小程序 - 页面背景音乐播放器控件(音乐盒图标旋转 360° 动画且可点击暂停与播放)

    前言 因为无法插入视频,您所看到的是效果图(实则控制音乐),运行起来 有背景音乐. 默认直接播放音乐,用户可点击暂停与继续播放, 除了基础功能,也做好了播放.暂停.出错等监听,由您根据业务进行扩展. ...

  8. Delphi 媒体播放器控件

    樊伟胜 转载于:https://www.cnblogs.com/fanweisheng/p/11378752.html

  9. html5中音频、视频标签、自定义播放器常用属性及方法、全屏操作、新增属性兼容问题

    多媒体标签: 音频标签audio: <audio src="音频文件的URL"></audio><!-- audio标签需要controls控件才可以 ...

最新文章

  1. 二叉树 2.0 -- 非递归遍历
  2. 在Aptana下安装Zen coding
  3. Tomcat配置虚拟路径,使上传文件与服务器及工程文件分离开
  4. sqlmap完成简单的sql注入
  5. spark on k8s配置日志存储路径:spark-defaults.conf
  6. 从“学徒”(Apprentice III)看领导力(9-17集)
  7. Qt使用socket通信时接收的汉字信息显示时乱码
  8. 米斯特白帽培训讲义(v2)漏洞篇 XSS
  9. 证书服务器web注册,无法通过 Web 注册请求证书 - Windows Server | Microsoft Docs
  10. 哈密顿图 哈密顿回路 哈密顿通路(Hamilton)
  11. 青年会会训的一些探究
  12. sql语句ding_在postgresql中结束掉正在执行的SQL语句操作
  13. Mac 终端所有命令失效
  14. 用友 U8 word模板修改
  15. 如何解决移动硬盘无法格式化?两招方法教会你
  16. java webservice测试_搭建Soap webservice api接口测试案例系统
  17. EntityFramework笔记
  18. 开关、按钮开关、自锁开关内部结构
  19. 股市java_Java获取股市交易日
  20. 陌生人社交产品怎么设计?

热门文章

  1. Postgresql监控插件pg_stat_statements的安装
  2. 【电子学会】2022年06月图形化四级 -- 成绩查询
  3. 迟到的情人节祝福 Sierpinski Valentine
  4. JavaScript - 移除数组中的空字符串元素
  5. Spring Cloud Gateway 整合Spring Security
  6. App 提交审核被拒提示:Guideline 2.1 - Performance - App Completeness 的原因及解决
  7. 2022渗透测试-推荐一款漏洞扫描工具-AWVS安装与使用
  8. pycharm中的manage repositories为空
  9. GOTS认证辅导,GOTS标准将认证标签分为几个等级
  10. Android-App-启动优化全记录,hashmap和concurrenthashmap的区别