需求
- 播放暫停視頻
- 不允許快進(jìn),可以后退
- 視頻后退不會(huì)影響最高觀看時(shí)長(zhǎng),例如看了10分鐘,后退5分鐘,觀看時(shí)長(zhǎng)依然是600秒
- 監(jiān)聽退出記錄觀看時(shí)間,下次進(jìn)來(lái)接著看
- 視頻看完積分
- 自定義視頻是否有倍速
uniapp原生組件video 沒有倍速
<template>
<view>
<!-- id:唯一標(biāo)識(shí),@timeupdate進(jìn)度條變化的事件,@ended進(jìn)度條到最后的事件,initial-time指定視頻初始播放位置, -->
<video id="myVideo" style="width: 100%;" @timeupdate="timeUpdate" @ended="ended" :initial-time="initialTime"
:src="course.videos" :poster="course.img">
</video>
</view>
</template>
<script>
export default {
data() {
return {
initialTime: 0, //初始播放位置
duration: 0, // 視頻時(shí)長(zhǎng)
videoContext: '', // 用來(lái)存儲(chǔ)video對(duì)象
watchTime: 0, // 實(shí)際觀看時(shí)間
videoRealTime: 0, // 實(shí)時(shí)播放進(jìn)度
course: {
videos: "https://img.cdn.aliyun.dcloud.net.cn/guide/uniapp/%E7%AC%AC1%E8%AE%B2%EF%BC%88uni-app%E4%BA%A7%E5%93%81%E4%BB%8B%E7%BB%8D%EF%BC%89-%20DCloud%E5%AE%98%E6%96%B9%E8%A7%86%E9%A2%91%E6%95%99%E7%A8%8B@20181126-lite.m4v",
img: 'https://cdn.uviewui.com/uview/swiper/swiper1.png'
}
};
},
onLoad(options) {
uni.setNavigationBarTitle({
title: options.id
})
// 調(diào)用接口取到該用戶上次播放的位置(秒)
this.watchTime = 67
this.initialTime = 67
},
onReady() {
// 視頻唯一ID
this.videoContext = uni.createVideoContext('myVideo')
},
methods: {
// 監(jiān)聽進(jìn)度條變化:禁止拖動(dòng) e.detail = {currentTime, duration} 。觸發(fā)頻率 250ms 一次
timeUpdate(e) {
//視頻時(shí)長(zhǎng)
this.duration = parseInt(e.detail.duration)
// 記錄用戶當(dāng)前視頻進(jìn)度
var jumpTime = parseInt(e.detail.currentTime)
// 判斷用戶當(dāng)前視頻進(jìn)度比實(shí)際觀看時(shí)間差別,這里只判斷了用戶快進(jìn)
if (jumpTime - this.watchTime > 1) {
// 差別過(guò)大,調(diào)用seek方法跳轉(zhuǎn)到實(shí)際觀看時(shí)間
this.videoContext.seek(this.watchTime)
} else {
this.videoRealTime = parseInt(e.detail.currentTime)
if (this.videoRealTime > this.watchTime) {
this.watchTime = this.videoRealTime
}
}
},
ended() {
// 用戶把進(jìn)度條拉到最后,但是實(shí)際觀看時(shí)間不夠,跳轉(zhuǎn)回去會(huì)自動(dòng)暫停
if (this.watchTime < this.duration) {
this.videoContext.play()
} else {
console.log('看完了')
}
},
// 監(jiān)聽返回:監(jiān)聽不了ios的左滑返回,目前的采用的解決方案是在onLoad設(shè)置禁用左滑
// onLoad(option) {
// // 單頁(yè)禁止測(cè)滑返回
// // #ifdef APP-PLUS
// let currentWebview = this.$mp.page.$getAppWebview() //獲取當(dāng)前頁(yè)面的webview對(duì)象
// currentWebview.setStyle({
// popGesture: 'none'
// })
// // #endif
// }
// 監(jiān)聽返回,記錄視頻的觀看時(shí)長(zhǎng)
onBackPress(e) {
//backbutton 是點(diǎn)擊物理按鍵返回,navigateBack是uniapp中的返回(比如左上角的返回箭頭)
console.log('返回', e, this.watchTime, this.duration);
},
},
}
</script>
使用插件 x-video 視頻播放
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-675384.html
修改組件 實(shí)現(xiàn)不能往前拉的功能
<template>
<div class="video-wrap" :style="{ width: `${width}`, height: `${height}` }">
<!-- video player -->
<view @click="handleControls">
<video class="video-player" :id="videoId" :style="{ width: `${width}`, height: `${height}` }"
webkit-playsinline="true" playsinline="true" x-webkit-airplay="allow" x5-video-player-type="h5-page"
x5-video-orientation="portrait" :src="src" :initial-time="initialTime" :controls="isFullScreen"
:show-center-play-btn="false" :autoplay="autoplay" :muted="isMute" :poster="poster" @play="videoPlay"
@pause="videoPause" @ended="videoEnded" @timeupdate="videoTimeUp" @loadedmetadata="videoLoaded"
@seeked="videoSeeked" @seeking="videoSeeking" @waiting="videoWaiting" @error="videoError"
@fullscreenchange="onFullScreen"></video>
</view>
<view class="abs-center">
<!-- 中心播放按鈕 -->
<image src="../../static/play-btn.png" mode="" class="play-btn" v-if="!isVideoPlay && !showLoading"
@click="videoPlayCenter"></image>
<!-- 加載中 -->
<div class="video-loading" v-if="showLoading">
<image src="../../static/loading.png" mode="" class="loading-btn"></image>
</div>
</view>
<!-- 控制條 -->
<view :class="['controls-bar', controls ? 'show' : 'hide']">
<!-- 播放 -->
<view class="play-box" @click="videoPlayClick">
<image src="../../static/pause.png" mode="" class="play-icon" v-if="isVideoPlay"></image>
<image src="../../static/play.png" mode="" class="play-icon" v-else></image>
</view>
<!-- 聲音 -->
<view class="mute-box" @click="videoMuteClick">
<image src="../../static/sound.png" mode="" class="mute-icon" v-if="!isMute"></image>
<image src="../../static/mute.png" mode="" class="mute-icon" v-else></image>
</view>
<!-- 進(jìn)度 -->
<view class="progress">
<view class="currtime">{{ currentTimeStr }}</view>
<view class="slider-container">
<slider @change="sliderChange" @changing="sliderChanging" :step="step" :value="sliderValue"
backgroundColor="#9f9587" activeColor="#d6d2cc" block-color="#FFFFFF" block-size="12" />
</view>
<view class="druationTime">{{ druationTimeStr }}</view>
</view>
<!-- 倍速 -->
<view class="play-rate" @click="videoPlayRate" v-if="showRate">{{ playbackRate }}x</view>
<!-- 全屏 -->
<view class="play-full" @click="videoFull">
<image src="../../static/fullscreen.png" mode="" class="play-icon" @click="videoFull"></image>
</view>
<!-- 倍速菜單 -->
<ul class="play-rate-menu" :style="{ height: height }" v-if="showRateMenu">
<li v-for="item in playbackRates" :key="item" :class="[{ activeRate: playbackRate === item }, 'play-rate-item']"
@click="changePlayRate(item)">
{{ item }}x
</li>
</ul>
</view>
</div>
</template>
<script>
export default {
name: 'XVideo',
props: {
// 視頻地址
videoId: {
type: String,
default: 'myVideo'
},
// 視頻地址
src: {
type: String
},
// 自動(dòng)播放
autoplay: {
type: Boolean,
default: true
},
// 封面
poster: {
type: String
},
// 步長(zhǎng),表示占比,取值必須大于0且為整數(shù)
step: {
type: Number,
default: 1
},
// 初始播放進(jìn)度,表示占比
progress: {
type: Number
},
// 視頻寬度
width: {
type: String,
default: '100%'
},
// 視頻高度
height: {
type: String,
default: '484rpx'
},
// 播放錯(cuò)誤提示
errorTip: {
type: String,
default: '播放錯(cuò)誤'
},
// 是否展示倍速
showRate: {
type: Boolean,
default: true
},
// 播放速率
playbackRates: {
type: Array,
default: () => [0.5, 0.8, 1, 1.25, 1.5, 2]
}
},
data() {
return {
controls: false, //顯示播放控件
isVideoPlay: false, // 是否正在播放
isMute: false, // 是否靜音
isVideoEnd: false, // 是否播放結(jié)束
showPoster: true, // 是否顯示視屏封面
showLoading: false, // 加載中
durationTime: 0, //總播放時(shí)間 時(shí)間戳
currentTime: 0, //當(dāng)前播放時(shí)間 時(shí)間戳
watchTime: 0, //實(shí)際最高播放時(shí)間 時(shí)間戳
druationTimeStr: '00:00', //總播放時(shí)間 字符串 計(jì)算后
currentTimeStr: '00:00', //當(dāng)前播放時(shí)間 字符串 計(jì)算后
sliderValue: 0, //進(jìn)度條的值 百分比
isSeeked: true, //防止進(jìn)度條拖拽失效
playbackRate: 1, // 初始播放速率
showRateMenu: false, //顯示播放速率
initialTime: 0, //初始播放時(shí)間
isFullScreen: false, //是否全屏
windowWidth: 0 //屏幕寬度
}
},
mounted() {
this.videoPlayer = uni.createVideoContext(this.videoId, this)
// #ifdef H5
// 處理微信不能自動(dòng)播放
if (this.autoplay) {
document.addEventListener(
'WeixinJSBridgeReady',
() => {
this.videoPlayer.play()
this.isVideoPlay = true
},
false
)
}
// #endif
},
onHide() {
clearTimeout(this.timer)
},
methods: {
// 自動(dòng)隱藏控制條
hideControls() {
this.timer = setTimeout(() => {
this.controls = false
}, 5000)
},
// 點(diǎn)擊顯示/隱藏控制條
handleControls() {
this.controls = !this.controls
},
// 根據(jù)秒獲取時(shí)間
formatSeconds(second) {
second = Math.round(second)
var hh = parseInt(second / 3600)
var mm = parseInt((second - hh * 3600) / 60)
if (mm < 10) mm = '0' + mm
var ss = parseInt((second - hh * 3600) % 60)
if (ss < 10) ss = '0' + ss
if (hh < 10) hh = hh == 0 ? '' : `0${hh}:`
var length = hh + mm + ':' + ss
if (second > 0) {
return length
} else {
return '00:00'
}
},
// 緩沖
videoWaiting(e) {
// 沒有緩沖結(jié)束事件,所以在不播放的情況觸發(fā)loading
if (!this.isVideoPlay) this.showLoading = true
},
// 視頻信息加載完成
videoLoaded(e) {
// console.log(e.detail.duration, this.progress)
this.durationTime = e.detail.duration
this.druationTimeStr = this.formatSeconds(this.durationTime)
this.initialTime = this.progress
this.watchTime = this.progress
this.currentTime = this.progress
this.sliderValue = this.progress * 100 / this.durationTime
this.videoPlayer.seek(this.initialTime)
this.currentTimeStr = this.formatSeconds(this.initialTime)
this.controls = true
this.showLoading = false
this.$emit('loadeddata', this.durationTime, this.videoPlayer)
},
// 播放進(jìn)度更新,觸發(fā)頻率 250ms 一次
// videoTimeUp(e) {
// console.log(this.initialTime,this.currentTime,this.watchTime)
// // 記錄用戶當(dāng)前視頻進(jìn)度
// var jumpTime = parseInt(e.detail.currentTime)
// // 判斷用戶當(dāng)前視頻進(jìn)度比實(shí)際觀看時(shí)間差別,這里只判斷了用戶快進(jìn)
// if (jumpTime - this.currentTime > 1) {
// // 差別過(guò)大,調(diào)用seek方法跳轉(zhuǎn)到實(shí)際觀看時(shí)間
// this.videoPlayer.seek(this.currentTime)
// this.currentTimeStr = this.formatSeconds(this.currentTime)
// } else if (jumpTime - this.currentTime < -1) {
// let sliderValue = Math.round((e.detail.currentTime / this.durationTime) * 100)
// if (this.isSeeked) {
// //判斷拖拽完成后才觸發(fā)更新,避免拖拽失效
// if (sliderValue % this.step === 0)
// // 比例值能被步進(jìn)值整除
// this.sliderValue = sliderValue
// }
// this.currentTimeStr = this.formatSeconds(e.detail.currentTime)
// this.$emit('timeupdate', e)
// } else {
// let sliderValue = Math.round((e.detail.currentTime / this.durationTime) * 100)
// if (this.isSeeked) {
// //判斷拖拽完成后才觸發(fā)更新,避免拖拽失效
// if (sliderValue % this.step === 0)
// // 比例值能被步進(jìn)值整除
// this.sliderValue = sliderValue
// }
// this.currentTimeStr = this.formatSeconds(e.detail.currentTime)
// this.currentTime = e.detail.currentTime
// this.watchTime = e.detail.currentTime
// this.$emit('timeupdate', e)
// }
// },
videoTimeUp(e) {
// console.log(this.initialTime, this.currentTime, this.watchTime)
// 記錄用戶當(dāng)前視頻進(jìn)度
var jumpTime = e.detail.currentTime
// 判斷用戶當(dāng)前視頻進(jìn)度比實(shí)際觀看時(shí)間差別,這里只判斷了用戶快進(jìn)
if (jumpTime - this.watchTime > 1) {
// 差別過(guò)大,調(diào)用seek方法跳轉(zhuǎn)到實(shí)際觀看時(shí)間
this.videoPlayer.seek(this.watchTime)
// this.currentTimeStr = this.formatSeconds(this.currentTime)
} else {
this.currentTime = e.detail.currentTime
let sliderValue = Math.round((e.detail.currentTime / this.durationTime) * 100)
if (this.isSeeked) {
//判斷拖拽完成后才觸發(fā)更新,避免拖拽失效
if (sliderValue % this.step === 0)
// 比例值能被步進(jìn)值整除
this.sliderValue = sliderValue
}
this.currentTimeStr = this.formatSeconds(e.detail.currentTime)
if (this.currentTime > this.watchTime) {
this.watchTime = this.currentTime
}
this.$emit('timeupdate', this.watchTime)
}
},
//正在拖動(dòng)slider
sliderChanging(e) {
console.log(2, e.detail.value)
this.isSeeked = false // 拖拽過(guò)程中,不允許更新進(jìn)度條
this.showLoading = true
this.videoPlayer.pause()
this.$emit('seeking')
},
// 拖動(dòng)slider完成后
sliderChange(e) {
console.log(1, e.detail.value, this.durationTime, (e.detail.value / 100) * this.durationTime)
this.sliderValue = e.detail.value
let currentTime = (this.sliderValue / 100) * this.durationTime
this.showLoading = false
this.isSeeked = true // 完成拖動(dòng)后允許更新滾動(dòng)條
this.videoPlayer.seek(currentTime)
if (this.sliderValue < 100) {
this.videoPlayer.play()
} else {
this.videoPlayer.pause()
this.videoEnded()
}
this.hideControls()
this.$emit('seeked', this.sliderValue)
},
// 點(diǎn)擊中心播放
videoPlayCenter() {
this.videoPlayer.play()
this.$emit('play')
},
// 點(diǎn)擊左下角播放/暫停,會(huì)觸發(fā)原始播放/暫停事件,分開寫,防止重復(fù)觸發(fā)
videoPlayClick() {
if (this.isVideoPlay) {
this.videoPlayer.pause()
} else {
this.videoPlayer.play()
this.$emit('play')
}
},
// 原始播放
videoPlay() {
if (this.pauseTimer) {
clearTimeout(this.pauseTimer)
}
this.isVideoPlay = true
this.isVideoEnd = false
this.showLoading = false
this.hideControls()
},
// 原始暫停
videoPause() {
// 處理播放結(jié)束和拖動(dòng)會(huì)先觸發(fā)暫停的問題
this.pauseTimer = setTimeout(() => {
if (this.isVideoEnd) return
if (!this.isSeeked) return
this.isVideoPlay = false
this.$emit('pause')
}, 100)
},
// 靜音
videoMuteClick() {
this.isMute = !this.isMute
},
// 播放結(jié)束
videoEnded() {
// 重置狀態(tài)
this.isVideoPlay = false
this.showPoster = true
this.isVideoEnd = true
this.$emit('ended')
},
// 播放錯(cuò)誤
videoError(e) {
// uni.showToast({
// title: this.errorTip,
// icon: 'none'
// })
this.$emit('error')
},
// 顯示倍速
videoPlayRate() {
this.showRateMenu = true
},
// 點(diǎn)擊倍速
changePlayRate(rate) {
this.playbackRate = rate
this.videoPlayer.playbackRate(rate)
this.showRateMenu = false
this.hideControls()
},
// 創(chuàng)建倍速按鈕
createPlayRateDOM() {
const playRateDom = document.createElement('div')
playRateDom.className = 'full-play-rate'
playRateDom.innerText = `${this.playbackRate}x`
playRateDom.onclick = () => {
const playRateMenuDom = document.querySelector('.full-play-rate-menu')
playRateMenuDom.style.display = 'block'
}
return playRateDom
},
// 創(chuàng)建倍速菜單
createPlayRateMenuDom() {
const playRateMenuDom = document.createElement('ul')
playRateMenuDom.className = `play-rate-menu full-play-rate-menu`
playRateMenuDom.style.height = this.windowWidth + 'px'
playRateMenuDom.style.display = 'none'
let liStr = ''
this.playbackRates.forEach((item) => {
liStr += `
<li class="${this.playbackRate === item ? 'activeRate' : ''} play-rate-item full-play-rate-item">
${item}x
</li>
`
})
playRateMenuDom.innerHTML = liStr
return playRateMenuDom
},
// 處理全屏倍速功能
videoFullRate() {
this.windowWidth = uni.getSystemInfoSync().windowWidth
const fullScreen = document.querySelector('.uni-video-fullscreen')
// 添加倍速菜單
const playRateMenuDom = document.querySelector('.full-play-rate-menu')
const videoContainer = document.querySelector('.uni-video-container')
if (!playRateMenuDom) {
videoContainer.appendChild(this.createPlayRateMenuDom())
const lis = document.querySelectorAll('.full-play-rate-item')
lis.forEach((item) => {
item.style.lineHeight = this.windowWidth / this.playbackRates.length + 'px'
item.onclick = () => {
let rate = +item.innerText.slice(0, -1)
this.playbackRate = rate
this.videoPlayer.playbackRate(rate)
lis.forEach((li) => {
li.classList.remove('activeRate')
let rate = +li.innerText.slice(0, -1)
if (this.playbackRate === rate) {
li.classList.add('activeRate')
}
})
const playRateMenuDom = document.querySelector('.full-play-rate-menu')
playRateMenuDom.style.display = 'none'
const playRateDom = document.querySelector('.full-play-rate')
playRateDom.innerText = `${this.playbackRate}x`
}
})
}
// 添加倍速按鈕
const playRateDom = document.querySelector('.full-play-rate')
if (!playRateDom) {
fullScreen.parentNode.insertBefore(this.createPlayRateDOM(), fullScreen)
}
},
// 點(diǎn)擊全屏
videoFull() {
this.videoPlayer.requestFullScreen()
// #ifdef H5
if (this.showRate) {
this.videoFullRate()
}
// #endif
},
// 監(jiān)聽原生全屏事件
onFullScreen({
detail
}) {
if (detail.fullScreen) {
this.isFullScreen = true
} else {
this.isFullScreen = false
// #ifdef H5
const playRateMenuDom = document.querySelector('.full-play-rate-menu')
playRateMenuDom.style.display = 'none'
// #endif
}
}
}
}
</script>
<style lang="scss" scoped>
/deep/ .uni-video-fullscreen {
margin-left: 30px
}
.show {
opacity: 1 !important;
}
.hide {
opacity: 0 !important;
pointer-events: none;
}
.abs-center {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
::v-deep .full-play-rate {
color: #cbcbcb;
font-size: 32rpx;
margin-left: 24rpx;
margin-right: 24rpx;
}
::v-deep .full-play-rate-menu {
position: fixed !important;
}
::v-deep .play-rate-menu {
list-style-type: none;
background-color: rgba(0, 0, 0, 0.7);
width: 24%;
position: absolute;
right: 0;
bottom: 0;
padding-left: 0;
box-sizing: border-box;
}
::v-deep .play-rate-item {
line-height: 70rpx;
font-size: 28rpx;
text-align: center;
color: #fff;
}
::v-deep .activeRate {
color: #5785e3;
}
.video-wrap {
position: relative;
.play-btn {
width: 120rpx;
height: 120rpx;
}
@keyframes run {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.loading-btn {
width: 120rpx;
height: 120rpx;
animation: run 0.8s linear 0s infinite;
}
.controls-bar {
width: 100%;
padding: 1% 1% 1% 0;
position: absolute;
bottom: 0;
left: 0;
right: 0;
z-index: 99;
display: flex;
align-items: center;
background: rgba(59, 57, 57, 0.7);
color: #fff;
opacity: 1;
transition: opacity 1s;
height: 84rpx;
.play-box,
.mute-box,
.play-full {
width: 84rpx;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.mute-icon {
width: 40rpx;
height: 40rpx;
}
.play-icon {
width: 34rpx;
height: 34rpx;
}
.progress {
display: flex;
align-items: center;
flex: 1;
font-size: 24rpx;
margin-left: 16rpx;
.slider-container {
flex: 1;
max-width: 58%;
}
.currtime {
color: #ffffff;
width: 11%;
height: 100%;
text-align: center;
margin-right: 20rpx;
}
.druationTime {
color: #ffffff;
width: 12%;
height: 100%;
text-align: center;
}
}
.play-rate {
font-size: 32rpx;
margin-right: 24rpx;
}
.play-rate-menu {
padding-top: 26rpx;
}
.play-rate-item:first-child {
margin-top: 30rpx;
}
}
}
</style>
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-675384.html
到了這里,關(guān)于uniapp視頻video的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!