vue自定义h5video视频播放器进度条组件,可拖拽、跳转、倍速、全屏
需求
一个进度条组件控制多个视频的播放、进度调整。视频可点击全屏观看,唯一的进度条是某个指定视频的视频信息。
图示
实现
html
//视频
<div
v-for="(item, index) in urls"
:key="index"
>
<video :ref="item.type" preload="auto" :src="item.url" />
<div>
// svg-icon是icon图标组件
<svg-icon v-if="!isFullScreen" :style="{ fill: '#dbdbdb', 'font-size': 20 + 'px' }" icon-class="quanping_o" @click="handleFullScreen(item)" />
<svg-icon v-else :style="{ fill: '#dbdbdb', 'font-size': 20 + 'px' }" icon-class="quxiaoquanping_o" @click="handleFullScreen(item)" />
</div>
</div>
//进度条
<div v-show="fileUrl" class="progress-wrap">
<div class="operate">
<div class="speed">
//倍速列表
<el-menu class="list" mode="horizontal">
<el-menu-item v-for="(speed, i) in speedList" :key="i" class="list-item" @click="handleChangeSpeed(speed)">{{ speed }}</el-menu-item>
</el-menu>
//显示倍速
{{ playSpeed }}x
</div>
<div>
<div class="play-wrap" @click="play">
<svg-icon v-if="!paused" icon-class="zanting" :style="{ 'font-size': 14 + 'px' }" />
<svg-icon v-else-if="paused" icon-class="bofang" :style="{ 'font-size': 14 + 'px' }" />
</div>
// 显示已播放时长和总时长
<div class="timer">{{ currentTime }} / {{ totalTime }}</div>
</div>
</div>
//进度条容器
<div ref="control" class="control" @click="adjustProgress($event)">
// 进度条本条
<div class="progress" :style="{ width: progressWidth }">
//滑块
<div class="slider_circle" @mousedown="handleSliderMouseDown" />
<div class="slider_circle_large" />
</div>
</div>
</div>
vue.js
全屏
handleFullScreen(item) {
this.isFullScreen = !this.isFullScreen //isFullScreen 定义为布尔值,控制样式用的,给视频容器高度设置为100vh,宽度100%实现全屏显示
this.fullVideoType = item.type //控制样式用的,不必要
},
点击进度条跳转
adjustProgress(e) {
e.preventDefault()
// 这里的this.controlRef = this.$refs.control,加载完视频后定义即可
const { left, width } = this.controlRef.getBoundingClientRect()
// left: 进度条容器control到最左侧的距离,width:容器的宽度
// e.clientX:鼠标点击的位置到最左侧的距离
const progressWidth = e.clientX - left
this.progressWidth = progressWidth + 'px'
this.updadteCurrentTime(progressWidth, width)
},
拖动滑块
在菜鸟教程上有以下几个参数的详细解说,这张图忘记哪里看的了,如有认领可评论我贴链接
handleSliderMouseDown(event) {
//如果不添加以下两处的阻止默认事件,会出现以下情况: 鼠标点击滑块向前拉动,移出进度条范围时,会自动选择文字等元素,出现禁用图标。松开鼠标,再次进入进度条,即使没有按住滑块,滑块也会跟随鼠标移动。这不是我们想要看到的效果。
event.preventDefault()
// 滑块点击坐标
const offsetX = event.offsetX
document.onmousemove = (e) => {
e.preventDefault()
// 滑动距离可视区域左侧的距离
const X = e.clientX
// 减去滑块偏移量
const cl = X - offsetX
const { left, width } = this.controlRef.getBoundingClientRect()
// 除去滑块按钮长度的进度条长度
const ml = cl - left
let progressWidth
if (ml <= 0) {
//进度条长度最小和最大值的界定
progressWidth = 0
} else if (ml >= width) {
progressWidth = width
} else {
progressWidth = ml
}
this.progressWidth = progressWidth + 'px'
// 更新当前时间
this.updadteCurrentTime(progressWidth, width)
}
//抬起鼠标,结束移动事件
document.onmouseup = () => {
document.onmousemove = null
document.onmouseup = null
}
},
倍速
// 倍速
handleChangeSpeed(item) {
this.playSpeed = item
},
播放暂停
play() {
//是否暂停
this.paused = !this.paused
// 若此刻状态是重新播放,那么点击播放时,进度条需从头开始前进
// 进度条控制的是中间视频,this.middleRef
if (this.middleRef.duration === this.middleRef.currentTime) {
this.middleRef.currentTime = 0
this.currentTime = this.formatSeconds(this.middleRef.currentTime)
this.progressWidth = 0 + 'px !important'
}
if (!this.paused) {
// 定时器实时刷新
this.timer = setInterval(this.updateVideoProgress, 50)
this.updateVideoProgress()
}
this.videoRefArr.forEach((v) => {
v.currentTime = this.middleRef.currentTime
if (this.paused) {
v.pause()
} else {
v.play()
// 按倍速播放
v.playbackRate = this.playSpeed
}
})
},
clearTimer() {
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
},
// 更新播放时进度条长度
updateVideoProgress() {
//按照已播放时间和总时长的比例更新进度条长度
this.progressWidth = (this.middleRef.currentTime / this.middleRef.duration) * 100 + '%'
this.currentTime = this.formatSeconds(this.middleRef.currentTime)
this.totalTime = this.formatSeconds(this.middleRef.duration)
// 放完、暂停这两种情况,遍历所有视频,使他们的状态一致,并清除定时器
if (this.middleRef.currentTime === this.middleRef.duration || this.paused) {
this.videoRefArr.forEach((v) => {
v.pause()
})
this.paused = true
this.clearTimer()
}
},
// 格式化时间
formatSeconds(value) {
const result = parseInt(value)
const h = Math.floor(result / 3600) < 10 ? '0' + Math.floor(result / 3600) : Math.floor(result / 3600)
const m = Math.floor((result / 60) % 60) < 10 ? '0' + Math.floor((result / 60) % 60) : Math.floor((result / 60) % 60)
const s = Math.floor(result % 60) < 10 ? '0' + Math.floor(result % 60) : Math.floor(result % 60)
//把视频时长格式化到毫秒
let ms
const msValue = value.toFixed(3).toString().split('.')[1]
if (Math.floor(msValue % 1000) < 10) {
ms = '00' + Math.floor(msValue % 1000)
} else if (Math.floor(msValue % 1000) > 10 && Math.floor(msValue % 1000) < 100) {
ms = '0' + Math.floor(msValue % 1000)
} else if (Math.floor(msValue % 1000) < 1000) {
ms = Math.floor(msValue % 1000)
}
let res = ''
res += `${h}:`
res += `${m}:`
res += `${s}.`
res += `${ms}`
return res
}
跳转、拖动后更新进度条长度是通过更新视频的currentTime
// 更新当前时间、帧号
updadteCurrentTime(progressWidth, width) {
this.currentTime = this.formatSeconds((progressWidth / width) * this.middleRef.duration)
this.totalTime = this.formatSeconds(this.middleRef.duration)
this.videoRefArr.forEach((v) => {
v.currentTime = (progressWidth / width) * this.middleRef.duration
})
},
总结:公司内部使用需要写这样一个组件。虽然比较不通用,但是比较灵活。html结构为了看的直观点,省略了相关样式。
有感兴趣的可以一起讨论。
原创不易,转载请贴出处
版权声明:本文为huangyinzhang原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。