uni-app 调用createVideoContext的exitFullScreen方法概率触发video的ended事件
uni-app 调用createVideoContext的exitFullScreen方法概率触发video的ended事件
| 开发环境 | 版本号 | 项目创建方式 |
|---|---|---|
| Mac | Sonoma 14.2.1 | CLI |
产品分类:uniapp/小程序/微信
第三方开发者工具版本号:1.06.2401020
基础库版本号:3.3.4
CLI版本号:@vue/cli 5.0.8
示例代码:
<template> <view style="position: relative" [@tap](/user/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](/user/tap)="toggleFullScreen">
</cover-view>
</video>
<!-- 操作区 -->
<view :class="{'video-mask' : true, 'video-mask-hide': maskHide}">
<view v-if="playStatus === 'init' || playStatus === 'pause'" [@tap](/user/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](/user/tap).stop="pause">
<uni-icons custom-prefix="iconfont" type="icon-zanting" size="30" color="#FFFFFF"></uni-icons>
</view>
<view v-if="playStatus === 'played'" [@tap](/user/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](/user/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](/user/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](/user/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>
更多关于uni-app 调用createVideoContext的exitFullScreen方法概率触发video的ended事件的实战教程也可以访问 https://www.itying.com/category-93-b0.html
更多关于uni-app 调用createVideoContext的exitFullScreen方法概率触发video的ended事件的实战教程也可以访问 https://www.itying.com/category-93-b0.html
在 uni-app 中调用 createVideoContext 的 exitFullScreen 方法时,有时会触发 video 的 ended 事件,这可能是由于某些特定条件下,退出全屏操作被误认为视频播放结束。以下是一些可能的原因及解决方案:
可能的原因
-
视频播放状态的误判:
- 退出全屏时,播放器可能会重新加载或重置,导致播放状态被误判为结束。
-
平台差异:
- 不同平台(如微信小程序、H5、App)对
video组件的实现可能有差异,导致行为不一致。
- 不同平台(如微信小程序、H5、App)对
-
事件触发顺序问题:
exitFullScreen和ended事件的触发顺序可能存在冲突。
-
视频播放器内部逻辑:
- 某些播放器在退出全屏时可能会触发
pause或ended事件。
- 某些播放器在退出全屏时可能会触发
解决方案
1. 检查事件监听逻辑
确保 ended 事件的处理逻辑不会因为退出全屏而被误触发。可以在事件处理函数中添加调试信息,确认触发原因。
videoContext.onEnded(() => {
console.log('视频播放结束');
// 处理视频结束逻辑
});
2. 延迟处理 ended 事件
如果 ended 事件是误触发的,可以在事件处理函数中增加延迟或条件判断,避免直接执行逻辑。
let isEnded = false;
videoContext.onEnded(() => {
if (!isEnded) {
isEnded = true;
setTimeout(() => {
console.log('视频播放结束');
// 处理视频结束逻辑
}, 500); // 延迟 500ms 确认事件
}
});
3. 手动控制播放状态
在调用 exitFullScreen 后,手动检查视频的播放状态,避免误触发 ended 事件。
videoContext.exitFullScreen();
setTimeout(() => {
videoContext.play(); // 重新播放视频
}, 100);
4. 平台兼容性处理
针对不同平台(如微信小程序、H5、App),使用条件编译或平台特性检查,确保逻辑正确。
// #ifdef MP-WEIXIN
// 微信小程序特定逻辑
// #endif
// #ifdef H5
// H5 特定逻辑
// #endif
5. 使用 uni.createVideoContext 的其他方法
尝试使用 pause 或 stop 方法代替直接退出全屏,看看是否能避免触发 ended 事件。
videoContext.pause();
videoContext.exitFullScreen();
6. 更新 uni-app 版本
如果问题是由于 uni-app 的 Bug 导致的,尝试更新到最新版本,看看问题是否已修复。
示例代码
以下是一个完整的示例,结合了延迟处理和手动控制播放状态的逻辑:
export default {
data() {
return {
videoContext: null,
isEnded: false,
};
},
mounted() {
this.videoContext = uni.createVideoContext('myVideo', this);
this.videoContext.onEnded(() => {
if (!this.isEnded) {
this.isEnded = true;
setTimeout(() => {
console.log('视频播放结束');
// 处理视频结束逻辑
}, 500);
}
});
},
methods: {
exitFullScreen() {
this.videoContext.exitFullScreen();
setTimeout(() => {
this.videoContext.play(); // 重新播放视频
}, 100);
},
},
};

