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>

<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>

更多关于uni-app 调用createVideoContext的exitFullScreen方法概率触发video的ended事件的实战教程也可以访问 https://www.itying.com/category-93-b0.html

1 回复

更多关于uni-app 调用createVideoContext的exitFullScreen方法概率触发video的ended事件的实战教程也可以访问 https://www.itying.com/category-93-b0.html


uni-app 中调用 createVideoContextexitFullScreen 方法时,有时会触发 videoended 事件,这可能是由于某些特定条件下,退出全屏操作被误认为视频播放结束。以下是一些可能的原因及解决方案:


可能的原因

  1. 视频播放状态的误判

    • 退出全屏时,播放器可能会重新加载或重置,导致播放状态被误判为结束。
  2. 平台差异

    • 不同平台(如微信小程序、H5、App)对 video 组件的实现可能有差异,导致行为不一致。
  3. 事件触发顺序问题

    • exitFullScreenended 事件的触发顺序可能存在冲突。
  4. 视频播放器内部逻辑

    • 某些播放器在退出全屏时可能会触发 pauseended 事件。

解决方案

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 的其他方法

尝试使用 pausestop 方法代替直接退出全屏,看看是否能避免触发 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);
    },
  },
};
回到顶部