<template>
<view style="position: relative" @tap="maskTouchend">
<video
id="video"
src="https://sns-video-al.xhscdn.com/stream/110/259/01e5d9a0fe383374010371038dda299acb_259.mp4"
style="width: 100%;display: block"
:muted="muted"
:autoplay="false"
:controls="false"
:show-mute-btn="false"
:show-play-btn="false"
:show-center-play-btn="false"
:enable-progress-gesture="false"
:vslide-gesture-in-fullscreen="false"
@play="onPlay"
@pause="onPause"
@ended="onPlayFinished"
@timeupdate="onPlayTimeUpdate"
@loadedmetadata="onLoadedData"
@fullscreenchange="onFullScreenChange"
>
<!-- 兼容全屏操作 -->
<cover-view v-if="isFullScreen" class="full-screen-back-icon" @tap="toggleFullScreen">
</cover-view>
</video>
<!-- 操作区 -->
<view :class="{'video-mask' : true, 'video-mask-hide': maskHide}">
<view v-if="playStatus === 'init' || playStatus === 'pause'" @tap.stop="play">
<uni-icons custom-prefix="iconfont" type="icon-icon_play" size="30" color="#FFFFFF"></uni-icons>
</view>
<view v-if="playStatus === 'playing'" @tap.stop="pause">
<uni-icons custom-prefix="iconfont" type="icon-zanting" size="30" color="#FFFFFF"></uni-icons>
</view>
<view v-if="playStatus === 'played'" @tap.stop="play">
<uni-icons custom-prefix="iconfont" type="icon-zhongbo" size="30" color="#FFFFFF"></uni-icons>
</view>
<view class="control-bar">
<view class="voice" @tap.stop="toggleVoice">
<uni-icons custom-prefix="iconfont" :type="muted ? 'icon-jingyin' : 'icon-shengyin'" size="24" color="#FFFFFF"></uni-icons>
</view>
<view class="progress-bar">
<text>{{formatDuration(currentTime)}}</text>
<view class="progress-view">
<view class="progress" :style="getProgressPercent"></view>
</view>
<text>{{formatDuration(duration)}}</text>
</view>
<view class="full-screen" @tap="toggleFullScreen">
<uni-icons custom-prefix="iconfont" type="icon-quanping" size="24" color="#FFFFFF"></uni-icons>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import Utils from '../../utils/index'
import {computed, type Ref, ref, watch} from "vue";
import {onReady} from "@dcloudio/uni-app";
const playStatus = ref('init')
const muted = ref(true)
const duration = ref(0)
const currentTime = ref(0)
const hide = ref(false)
const ctx: Ref<any> = ref(null)
const playedPercent = ref(0)
const isFullScreen = ref(false)
const touchNum = ref(0)
let touchTimer: number | null | undefined = null
const maskTouchend = () => {
if(touchTimer) clearTimeout(touchTimer)
touchNum.value = touchNum.value + 1
touchTimer = setTimeout(()=>{
if(touchNum.value == 1){
console.log('单击')
toggleMaskHide()
}
if(touchNum.value >= 2){
console.log('双击')
if(playStatus.value === 'playing') pause()
if(playStatus.value === 'pause') play()
}
touchNum.value = 0
},250)
}
const maskHide = computed(() => {
if(playStatus.value === 'playing'){
return !!hide.value;
}else{
return false
}
})
onReady(()=>{
ctx.value = uni.createVideoContext('video')
})
const onLoadedData = (e) => {
console.log('onLoadedData', e)
duration.value = e.detail.duration
}
const onPlay = () => {
playStatus.value = 'playing'
}
const onPause = () => {
playStatus.value = 'pause'
}
const onPlayFinished = () => {
console.log('完播')
playStatus.value = 'played'
}
const onPlayTimeUpdate = (e) => {
currentTime.value = e.detail.currentTime
playedPercent.value = (e.detail.currentTime / e.detail.duration) * 100
if(playedPercent.value > 90){
console.log('播放进度超过90%', playedPercent.value)
// TODO 提交完播记录
}
}
const play = () => {
ctx.value.play()
hide.value = true
}
const pause = () => {
ctx.value.pause()
hide.value = true
}
const toggleMaskHide = () => {
if(playStatus.value === 'playing'){
hide.value = !hide.value
}else{
hide.value = false
}
}
const toggleVoice = () => {
muted.value = !muted.value
}
const toggleFullScreen = () => {
if(isFullScreen.value){
ctx.value.exitFullScreen()
}else{
ctx.value.requestFullScreen()
}
}
const onFullScreenChange = e => {
console.log('操作切换全屏',e)
isFullScreen.value = e.detail.fullScreen
}
const getProgressPercent = () => {
return {width: playedPercent + '%'}
}
const formatDuration = Utils.formatDuration
let hideTimer: number | null | undefined=null
watch(hide, () => {
if(!hide.value && playStatus.value === 'playing'){
if(hideTimer) clearTimeout(hideTimer)
hideTimer = setTimeout(() => {
hide.value = true
}, 5000)
}
})
</script>
<style lang="scss" scoped>
.video-mask{
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-color: rgba(0,0,0,0.5);
z-index: 100;
display: flex;
align-items: center;
justify-content: center;
opacity: 1;
transition: 0.3s;
}
.video-mask-hide{
opacity: 0;
}
.control-bar{
position: absolute;
bottom: 0;
left: 0;
height: 80rpx;
width: 100%;
background: linear-gradient(180deg,rgba(0,0,0,0), rgba(0,0,0,1));
display: flex;
align-items: center;
box-sizing: border-box;
}
.progress-bar{
display: flex;
color: #ffffff;
flex: 1;
align-items: center;
font-size: 24rpx;
}
.voice,
.full-screen{
width: 100rpx;
display: flex;
align-items: center;
justify-content: center;
}
.progress-view{
flex: 1;
height: 6rpx;
background: rgba(255,255,255,0.6);
border-radius: 3rpx;
margin: 0 20rpx;
}
.progress{
height: 100%;
background: #FFFFFF;
border-radius: 3rpx;
}
.full-screen-back-icon{
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.2);
z-index: 10000;
}
</style>
- 发布:2024-03-08 10:52
- 更新:2024-03-08 10:52
- 阅读:144
产品分类: uniapp/小程序/微信
PC开发环境操作系统: Mac
PC开发环境操作系统版本号: Sonoma 14.2.1 (23C71)
第三方开发者工具版本号: 1.06.2401020
基础库版本号: 3.3.4
项目创建方式: CLI
CLI版本号: @vue/cli 5.0.8
示例代码:
操作步骤:
调用exitFullScreen方法退出video全屏之后,有一定概率会触发video组件的ended事件
调用exitFullScreen方法退出video全屏之后,有一定概率会触发video组件的ended事件
预期结果:
调用exitFullScreen不触发video组件ended事件
调用exitFullScreen不触发video组件ended事件
实际结果:
调用exitFullScreen概率触发video组件ended事件
调用exitFullScreen概率触发video组件ended事件
bug描述:
通过uni.createVideoContext创建ctx,调用ctx.exitFullScreen方法,video组件的@ended事件有概率会触发。