hookehuyr

feat(Splash): 添加视频加载失败时的图片降级方案

当视频加载或播放失败时,自动切换到图片背景避免黑屏
添加视频加载状态检测和错误处理回调
优化微信环境下的视频自动播放处理
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
2 <div class="splash-container" :class="{ 'fade-out': isExiting }"> 2 <div class="splash-container" :class="{ 'fade-out': isExiting }">
3 <!-- 背景视频 --> 3 <!-- 背景视频 -->
4 <video 4 <video
5 + v-show="!use_image_bg"
5 ref="videoPlayer" 6 ref="videoPlayer"
6 class="background-video" 7 class="background-video"
7 autoplay 8 autoplay
...@@ -10,10 +11,22 @@ ...@@ -10,10 +11,22 @@
10 playsinline 11 playsinline
11 webkit-playsinline 12 webkit-playsinline
12 preload="auto" 13 preload="auto"
14 + @error="on_video_error"
15 + @stalled="on_video_error"
16 + @abort="on_video_error"
17 + @emptied="on_video_error"
18 + @loadeddata="on_video_loaded"
13 > 19 >
14 <source :src="videoUrl" type="video/mp4" /> 20 <source :src="videoUrl" type="video/mp4" />
15 </video> 21 </video>
16 22
23 + <!-- 图片降级背景(视频不可用时显示) -->
24 + <div
25 + v-if="use_image_bg"
26 + class="background-image"
27 + :style="{ backgroundImage: 'url(' + imgUrl + ')' }"
28 + ></div>
29 +
17 <!-- 黑色半透明蒙板 --> 30 <!-- 黑色半透明蒙板 -->
18 <div class="overlay"></div> 31 <div class="overlay"></div>
19 32
...@@ -50,44 +63,91 @@ import { useRouter } from 'vue-router' ...@@ -50,44 +63,91 @@ import { useRouter } from 'vue-router'
50 const router = useRouter() 63 const router = useRouter()
51 const isExiting = ref(false) 64 const isExiting = ref(false)
52 const videoPlayer = ref(null) 65 const videoPlayer = ref(null)
66 +const use_image_bg = ref(false)
67 +const is_video_ready = ref(false)
53 68
54 // TODO: 视频配置 69 // TODO: 视频配置
55 const videoUrl = ref('https://cdn.ipadbiz.cn/stdj/video/sample-10s.mp4') 70 const videoUrl = ref('https://cdn.ipadbiz.cn/stdj/video/sample-10s.mp4')
71 +const imgUrl = ref('https://cdn.ipadbiz.cn/stdj/images/%E5%90%AF%E5%8A%A8%E9%A1%B5%E6%B5%B7%E6%8A%A5%E8%83%8C%E6%99%AF@2x.png')
56 72
57 // 进入应用函数 73 // 进入应用函数
74 +/**
75 + * 进入应用
76 + * 说明:触发淡出动画后跳转到首页
77 + */
58 const enterApp = () => { 78 const enterApp = () => {
59 - isExiting.value = true 79 + isExiting.value = true
60 - // 等待淡出动画完成后跳转 80 + // 等待淡出动画完成后跳转
61 - setTimeout(() => { 81 + setTimeout(() => {
62 - router.push('/home') 82 + router.push('/home')
63 - }, 500) 83 + }, 500)
64 } 84 }
65 85
66 onMounted(() => { 86 onMounted(() => {
67 - const video = videoPlayer.value 87 + const video = videoPlayer.value
68 - if (video) { 88 + if (video) {
69 - // 尝试播放视频 89 + // 尝试播放视频
70 - const playPromise = video.play() 90 + const playPromise = video.play()
71 - 91 +
72 - if (playPromise !== undefined) { 92 + if (playPromise !== undefined) {
73 - playPromise.catch(error => { 93 + playPromise.catch(error => {
74 - // 自动播放被阻止,在微信环境下尝试通过 WeixinJSBridge 播放 94 + // 自动播放被阻止:在微信环境下尝试通过 WeixinJSBridge 播放
75 - console.log('自动播放失败:', error) 95 + // 说明:避免控制台输出引起诊断问题
76 - const ua = navigator.userAgent.toLowerCase() 96 + void error
77 - if (ua.match(/MicroMessenger/i) && typeof window.WeixinJSBridge !== 'undefined') { 97 + const ua = navigator.userAgent.toLowerCase()
78 - window.WeixinJSBridge.invoke('getNetworkType', {}, () => { 98 + if (ua.match(/MicroMessenger/i) && typeof window.WeixinJSBridge !== 'undefined') {
79 - video.play() 99 + window.WeixinJSBridge.invoke('getNetworkType', {}, () => {
80 - }) 100 + video.play()
101 + })
102 + }
103 + // 若短时间内仍无法播放,降级为图片背景,避免黑屏
104 + setTimeout(() => {
105 + if (!is_video_ready.value) {
106 + enable_image_fallback()
107 + }
108 + }, 1200)
109 + })
81 } 110 }
82 - }) 111 +
112 + // 监听微信JSBridgeReady事件
113 + document.addEventListener('WeixinJSBridgeReady', () => {
114 + video.play()
115 + }, false)
83 } 116 }
84 117
85 - // 监听微信JSBridgeReady事件 118 + // 兜底:在一定时间内仍未加载完成则切换到图片背景
86 - document.addEventListener('WeixinJSBridgeReady', () => { 119 + // setTimeout(() => {
87 - video.play() 120 + // if (!is_video_ready.value) {
88 - }, false) 121 + // enable_image_fallback()
89 - } 122 + // }
123 + // }, 5000)
90 }) 124 })
125 +
126 +/**
127 + * 视频加载成功回调
128 + * 说明:标记视频已可播放,用于取消降级处理
129 + */
130 +const on_video_loaded = () => {
131 + is_video_ready.value = true
132 +}
133 +
134 +/**
135 + * 视频错误回调
136 + * 说明:视频加载/播放失败时触发图片降级,避免黑屏
137 + * @param {Event} e 事件对象
138 + */
139 +const on_video_error = (e) => {
140 + void e
141 + enable_image_fallback()
142 +}
143 +
144 +/**
145 + * 启用图片降级背景
146 + * 说明:切换到全屏图片背景,保证用户视觉不出现黑屏
147 + */
148 +const enable_image_fallback = () => {
149 + use_image_bg.value = true
150 +}
91 </script> 151 </script>
92 152
93 <style scoped lang="less"> 153 <style scoped lang="less">
...@@ -121,6 +181,21 @@ onMounted(() => { ...@@ -121,6 +181,21 @@ onMounted(() => {
121 animation: backgroundFadeIn 0.8s ease-out forwards; 181 animation: backgroundFadeIn 0.8s ease-out forwards;
122 } 182 }
123 183
184 +/* 图片降级背景 */
185 +.background-image {
186 + position: absolute;
187 + top: 0;
188 + left: 0;
189 + width: 100vw;
190 + height: 100vh;
191 + z-index: 1;
192 + background-size: cover;
193 + background-position: center;
194 + background-repeat: no-repeat;
195 + opacity: 0;
196 + animation: backgroundFadeIn 0.8s ease-out forwards;
197 +}
198 +
124 /* 黑色半透明蒙板 */ 199 /* 黑色半透明蒙板 */
125 .overlay { 200 .overlay {
126 position: absolute; 201 position: absolute;
......