项目要求不用video播放的自带controls样式,要根据高保真来
可以点击整个屏幕实现暂停或播放操作
代码实现结果:
附上代码:
视频播放组件:
<template>
<div class="video-play-page">
<video
v-if="Object.keys(videoDetail).length"
ref="videoPlayer"
id="videoElement"
:controls="false"
autoplay
:muted="true"
width="100%"
height="100%"
webkit-playsinline="false"
playsinline="false"
x5-video-player-type="h5"
x5-video-player-fullscreen="true"
x5-video-orientation="portraint"
:src="videoDetail.resUrl"
:poster="videoDetail.coverUrl"
@loadeddata="setVideoPoster($event)"
@progress="videoProgress($event)"
@timeupdate="videoTimeUpdate()"
@ended="videoEnded($event)"
@contextmenu="contextmenu"
>
您的浏览器不支持video
</video>
<div class="controls-cover" @click="videoPlayOrPause">
<!-- 暂停图片 -->
<img v-if="!playing" class="video-pause" :src="pauseIcon" alt="" />
</div>
<!-- 时间 -->
<div class="time-line" v-if="Object.keys(videoDetail).length">
<span class="time" id="currentTime" ref="progressTimer">{{
videoCurrentTime
}}</span>
<ProgressLine
class="progress-line"
:presentT="presentT"
:totalT="totalT"
:persentLoad="persentLoad"
@changeCurrentTime="changeCurrentTime($event)"
@changeCurrentWord="changeCurrentWord($event)"
>
</ProgressLine>
<span class="time" id="durationTime" ref="durationTimer">{{
videoTotalTime
}}</span>
</div>
</div>
</template>
<script>
import { wxLogin } from '@/utils/wechat'
import { Toast } from 'vant'
import ProgressLine from '@/components/Video/VideoProgress'
import VideoCard from '@/components/ListCard/VideoCard'
export default {
name: 'VideoPlay',
components: {
ProgressLine,
VideoCard,
},
props: {},
data() {
return {
presentT: 0, // 进度条的当前值,必须为number
totalT: 0, // 进度条的最大值,必须为number
persentLoad: 0, // 视频加载进度
videoCurrentTime: '00:00', // 当前视频已播放时长
videoTotalTime: '00:00', // 视频总时长
playerVideo: {},
playing: true, // 是否播放中 进入页面默认正在播放
pauseIcon: require('@/assets/images/play.png'), // 暂停按钮
videoDetail: {
resUrl: '',
coverUrl: '',
}, // 视频详情
resId: 0,
}
},
computed: {},
watch: {},
created() {},
mounted() {
this.userInfo = this.$common.getSessionJSON('userInfo')
this.isIOS = this.$common.getSessionJSON('isIOS')
this.mediumId = this.$route.query.mediumId
this.getVideoPlayDetail()
},
methods: {
videoPlayOrPause() {
if (this.playing) {
this.$refs.videoPlayer.pause() // 会触发videoPlay()函数
} else {
this.$refs.videoPlayer.play() // 会触发videoPause()函数
}
this.playing = !this.playing
},
fullScreen() {
this.$refs.videoPlayer.webkitRequestFullScreen()
},
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
}
},
contextmenu(e) {
e.returnValue = false
},
setVideoPoster(e) {
var videoObj = this.$refs.videoPlayer
this.totalT = videoObj.duration
this.videoTotalTime = this.formatTime(this.totalT)
},
videoEnded(e) {
this.$emit('videoEnded', e)
},
// 子组件传入的时间修改
changeCurrentTime(data) {
this.$refs.videoPlayer.currentTime = data // 点击进度条设置视频当前播放点
},
changeCurrentWord(data) {
this.videoCurrentTime = this.formatTime(data) // 当前播放时间显示文字
},
// 获取视频加载进度
videoProgress(e) {
var bf = this.$refs.videoPlayer?.buffered || 0
var time = this.$refs.videoPlayer?.currentTime || 0
if (bf.length !== 0) {
var range = 0
while (
!(bf?.start(range) <= time && time <= bf.end(range)) &&
range < bf.length - 1
) {
range += 1
}
var loadEndPercentage =
(bf.end(range) / this.playerVideo.duration) * 100 // 结束加载的百分比
this.persentLoad = loadEndPercentage
}
},
// 视频自动播放时
videoTimeUpdate() {
this.presentT = this.$refs.videoPlayer?.currentTime // 获取当前播放时长
this.totalT = this.$refs.videoPlayer?.duration // 获取视频总时长
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
},
},
}
</script>
<style lang="less" scoped>
@import '~@/styles/mixins.less';
.video-play-page {
height: 100vh;
background-color: #000;
position: relative;
#videoElement {
position: absolute;
top: 0;
left: 0;
}
}
.controls-cover {
width: 100%;
height: 100vh;
position: absolute;
top: 0;
right: 0;
z-index: 100;
.video-pause {
width: 63px;
height: 63px;
position: absolute;
right: calc(50% - 31px);
top: calc(50% - 31px);
opacity: 0.6;
}
.video-info {
position: absolute;
bottom: 80px;
color: #fff;
padding: 0 15px;
.title {
font-size: 16px;
}
.desc {
margin-top: 15px;
font-size: 12px;
}
}
}
.time-line {
width: 100%;
display: flex;
align-items: center;
position: absolute;
bottom: 50px;
color: #fff;
z-index: 200;
padding: 0 7px;
.time {
font-size: 12px;
flex-shrink: 0;
display: inline-block;
width: 50px;
padding: 0 8px;
}
.progress-line {
width: calc(100% - 80px);
flex: 1;
}
}
</style>
进度条组件
<template>
<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" @touchstart="roundDrag"></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) {
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)
},
// touchstart 触摸开始,多点触控,后面的手指同样会触发
// touchmove 接触点改变,滑动时
// touchend 触摸结束,手指离开屏幕时
// touchcancel 触摸被取消,当系统停止跟踪触摸的时候触发
// 进度条圆点拖拽
roundDrag(event) {
event.preventDefault()
const offsetX = event.touches[0].clientX
this.dragClick = true
this.clickDown = true // 解决圆点拖拽进度条长度抖动
document.ontouchmove = e => {
// 给圆点添加移动事件
const X = e.touches[0].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.ontouchend = () => {
document.ontouchmove = null
document.ontouchend = null
this.clickDown = false
}
},
},
}
</script>
<style lang="less" scoped>
.line-background {
width: 100%;
height: 3px;
background-color: rgba(255, 255, 255, 0.3);
border-radius: 2px;
.time-line {
width: 100%;
height: 3px;
background-color: #565651;
border-radius: 2px;
.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: 3px;
background-color: #fff;
border-radius: 2px;
}
.round {
position: absolute;
top: 20%;
left: 0;
transform: translateY(-50%);
width: 10px;
height: 10px;
border-radius: 5px;
background: #fff;
// box-shadow: -2px 0px 2px 2px rgba(3, 0, 0, 0.30);
}
}
}
}
</style>