ui设计的界面总是极具个性化的,要去修改插件中的视频控件的样式和布局太困难了,那就自己参照video原生事件,重写一个吧。
(效果图预览)
一、video标签的属性(props)
html <video>标签 | 菜鸟教程
<video
ref="videoPlayer"
id="videoElement"
controls
autoplay
:muted="isMute"
width="800px"
height="600px"
>
您的浏览器不支持video
</video>
参数说明:(更多属性参照上述菜鸟教程中的video标签)
- controls:默认为true,即向用户展示视频控件(如播放、暂停按钮等)
- autoplay:如果出现该属性,则视频在就绪后马上播放。
- muted:是否静音,默认为true
- width:设置视频播放器的宽度
二、video视频控件的触发事件
video标签支持的多媒体事件(Media Events) | 菜鸟教程
<video
ref="videoPlayer"
id="videoElement"
controls
autoplay
:muted="isMute"
width="100%"
height="100%"
@loadeddata="setVideoPoster($event)"
@progress="videoProgress($event)"
@pause="videoPause($event)"
@play="videoPlay($event)"
@timeupdate="videoTimeUpdate()"
@ended="videoEnded()"
@contextmenu="contextmenu"
>
您的浏览器不支持video
</video>
<button @click="handlePlay">播放</button>
<button @click="handlePause">暂停</button>
<button @click="handleMute">切换静音</button>
<button @click="fullScreen">全屏</button>
data(){
return{
isMute: true // 默认静音
}
}
1、播放(onplay事件)
this.$refs.videoPlayer.play();
methods:{
// 视频要开始播放时
videoPlay(e){
// ...触发该函数后视频会开始播放,我们可以做一些想做的事情,比如改变自定义播放按钮的样式等
},
// 自定义播放按钮中,触发视频的播放事件
handelPlay(){
this.$refs.videoPlayer.play(); // 会触发videoPlay()函数
}
}
2、暂停(onpause事件)
this.$refs.videoPlayer.pause();
methods:{
// 视频要暂停播放时
videoPause(e){
// ...触发该函数后视频会暂停播放
},
// 自定义播放按钮中,触发视频的播放事件
handelPause(){
this.$refs.videoPlayer.pause(); // 会触发videoPause()函数
}
}
3、静音(muted属性)
(1)切换静音
// 手动切换静音(点击(非拖拽)静音时,用户选择的音量不变)
handleMute() {
this.isMute = !this.isMute;
},
(2)改变音量(volume属性)
this.$refs.videoPlayer.volume = a; (a为从 0~1的数字)
// 这里用element的进度条写音量大小调节条
<el-slider v-model="curVolume" :show-tooltip="false" @input="changeVolume"></el-slider>
data(){
return{
curVolume: 0, // 默认音量为0
}
},
methods:{
changeVolume(val){
this.curVolume = val;
// 由于h5规定volum的值在0-1之间,所以这里要对获取到的val做一个处理(滑块的val是从0-100)
this.$refs.videoPlayer.volume = val / 100;
// 音量为0的时候,video控件为静音
if ( val == 0 ) {
this.isMute = true;
} else {
this.isMute = false;
}
}
}
4、全屏
fullScreen() {
this.$refs.videoPlayer.webkitRequestFullScreen();
},
5、播放进度条
获取视频总时长(duration)
var videoObj = this.$refs.videoPlayer;
videoObj.addEventListener('canplay', () => {
this.totalT = videoObj.duration;
})
获取视频加载进度
HTML5视频 - 加载百分比?
HTML5视频 - 加载百分比?
// 获取视频加载进度
videoProgress(e){
var bf = this.$refs.videoPlayer.buffered;
var time = this.$refs.videoPlayer.currentTime;
if ( bf.length != 0 ){
var range = 0;
while( !( bf.start(range) <= time && time <= bf.end(range) ) ) {
range += 1;
}
var loadEndPercentage = ( bf.end(range) / this.playerVideo.duration ) * 100; // 结束加载的百分比
this.persentLoad = loadEndPercentage;
}
},
(1)父组件调用
<template>
<video
ref="videoPlayer"
@progress="videoProgress($event)"
@timeupdate="videoTimeUpdate()"
>
您的浏览器不支持video
</video>
// 视频播放、加载进度条
<ProgressLine
:presentT="presentT"
:totalT="totalT"
:persentLoad="persentLoad"
@changeCurrentTime="changeCurrentTime($event)"
@changeCurrentWord="changeCurrentWord($event)"
>
</ProgressLine>
// 播放时长、视频总时长
<p>
<span id="currentTime" ref="progressTimer">{{ videoCurrentTime }}</span>
<span style="color: #ffffff;opacity: 0.3;"> / </span>
<span id="durationTime" ref="durationTimer">{{ videoTotalTime }}</span>
</p>
</template>
import ProgressLine from './ProgressLine.vue';
export default {
name: 'videoPage',
***ponents: {
ProgressLine
},
data(){
return{
presentT: 0, // 进度条的当前值,必须为number
totalT: 0, // 进度条的最大值,必须为number
persentLoad: 0, // 视频加载进度
videoCurrentTime: '00:00', // 当前视频已播放时长
videoTotalTime: '00:00', // 视频总时长
}
},
methods:{
// 子组件传入的时间修改
changeCurrentTime(data) {
this.$refs.videoPlayer.currentTime = data; // 点击进度条设置视频当前播放点
},
changeCurrentWord(data) {
this.videoCurrentTime = this.formatTime(data); // 当前播放时间显示文字
},
// 获取视频加载进度
videoProgress(e){
var bf = this.playerVideo.buffered;
var time = this.playerVideo.currentTime;
if ( bf.length != 0 ){
var range = 0;
while( !( bf.start(range) <= time && time <= bf.end(range) ) ) {
range += 1;
}
var loadEndPercentage = ( bf.end(range) / this.playerVideo.duration ) * 100; // 结束加载的百分比
this.persentLoad = loadEndPercentage;
}
},
// 视频自动播放时
videoTimeUpdate(){
this.presentT = this.playerVideo.currentTime; // 获取当前播放时长
this.videoCurrentTime = this.formatTime(this.presentT); // 时间格式化
},
// 时间格式化
formatTime(t) {
var m = parseInt(t % 3600 / 60)
m = m < 10 ? '0' + m : m
var s = parseInt(t % 60)
s = s < 10 ? '0' + s : s
return m + ':' + s
},
}
}
(2)进度条组件(播放进度条 和 加载进度条)
// ProgressLine.vue 进度条组件
<template>
<div>
<div class="line-background">
<div class="time-line" @click="adjustProgress($event)">
<div class="progress-round" ref="progressRound">
<div class="loading" ref="persentLoad" style="width: 0;"></div> <!-- 加载进度条 -->
<div class="progress" ref="progress" @click="adjustProgress"></div>
<div class="round" ref="round" @mousedown="roundDrag"></div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'ProgressLine',
props: {
presentT: {},
totalT: {},
persentLoad: { default : 0 }
},
data() {
return {
// 进度条拖拽
dragClick: false,
// 鼠标/手指按下
clickDown: false,
}
},
created() {
},
watch: {
// 侦听当前播放时长设置进度条
presentT: {
handler(newValue, oldValue) {
// 未点击进度条
if (this.dragClick == false && this.clickDown == false) {
this.$refs.progress.style.width = newValue / this.totalT * 100 + '%'
if ((newValue / this.totalT * 100 - 1.23) < 0) {
this.$refs.round.style.left = 0 + '%'
} else {
this.$refs.round.style.left = (newValue / this.totalT * 100) - 1.23 + '%'
}
} else if (this.dragClick == true) {
this.dealWidth()
this.dragClick = false
}
}
},
persentLoad: {
handler(newValue, oldValue) {
this.$refs.persentLoad.style.width = ( newValue / 100 ) * 1300 + 'px';
}
}
},
methods: {
progressData(data) {
this.$emit('changeCurrentTime', data)
this.$emit('changeCurrentWord', data)
},
// 进度条位置和圆点定位处理
dealWidth() {
this.$refs.progress.style.width = this.progressWidth / this.$refs.progressRound.offsetWidth * 100 + '%'
if ((this.progressWidth / this.$refs.progressRound.offsetWidth * 100) - 1.23 < 0) { // 圆点定位
this.$refs.round.style.left = 0 + '%'
} else {
this.$refs.round.style.left = (this.progressWidth / this.$refs.progressRound.offsetWidth * 100) - 1.23 + '%'
}
},
// 进度条点击
adjustProgress(e) {
this.dragClick = true
e.preventDefault()
const { left, width } = this.$refs.progressRound.getBoundingClientRect() // 进度条到屏幕距离及进度条的宽度
this.progressWidth = e.clientX - left
if (this.progressWidth < 0) {//进度条边界值计算情况
this.progressWidth = 0
} else if (this.progressWidth >= width) {
this.progressWidth = width
} else {
this.progressWidth = e.clientX - left // e.clientX:鼠标点击的位置到屏幕最左侧的距离
}
this.dealWidth()
this.progressData((this.progressWidth / width) * this.totalT)
},
// 进度条圆点拖拽
roundDrag(event) {
event.preventDefault()
const offsetX = event.offsetX
this.dragClick = true
this.clickDown = true // 解决圆点拖拽进度条长度抖动
document.onmousemove = (e) => { // 给圆点添加移动事件
e.preventDefault()// 阻止进度条拖拽时屏幕原有的滑动功能
const X = e.clientX // 获取圆点离屏幕的距离
const { left, width } = this.$refs.progressRound.getBoundingClientRect()
const ml = X - left // 进度条长度:圆点离屏幕的距离减去进度条最左边离屏幕的距离
if (ml <= 0) { // 进度条长度最小和最大值的界定
this.progressWidth = 0
} else if (ml >= width) {
this.progressWidth = width
} else {
this.progressWidth = ml
}
this.progressData((this.progressWidth / width) * this.totalT) //视频播放时间
this.dealWidth()
}
// 抬起鼠标,结束移动事件
document.onmouseup = () => {
document.onmousemove = null
document.onmouseup = null
this.clickDown = false
}
},
}
}
</script>
<style lang="less" scoped>
.line-background {
width: 100%;
height: 10px;
background-color: rgba(255, 255, 255, 0.3);
.time-line {
width: 100%;
height: 10px;
background-color: #565651;
.progress-round {
cursor: pointer;
width: 100%;
position: relative;
display: flex;
.loading {
height: 10px;
background-color: rgba(255, 255, 255, 0.3);
}
.progress {
position: absolute;
top: 0;
left: 0;
width: 00%;
height: 10px;
background-color: #3d7eff;
}
.round {
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
width: 16px;
height: 16px;
border-radius: 16px;
background: #ffffff;
box-shadow: -2px 0px 2px 2px rgba(3, 0, 0, 0.30);
}
}
}
}
</style>
6、视频中禁用右键(可以禁止用户下载视频)
// 在视频中禁用右键(禁止用户下载)
contextmenu(e){
e.returnValue = false;
},
7、设置倍速播放
this.$refs.videoPlayer.playbackRate = rate; // rate 一般在[2.0,1.75,1.5,1.0,0.75,0.5]范围