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,11 +63,18 @@ import { useRouter } from 'vue-router' ...@@ -50,11 +63,18 @@ 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 // 等待淡出动画完成后跳转
...@@ -71,14 +91,21 @@ onMounted(() => { ...@@ -71,14 +91,21 @@ onMounted(() => {
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 + // 说明:避免控制台输出引起诊断问题
96 + void error
76 const ua = navigator.userAgent.toLowerCase() 97 const ua = navigator.userAgent.toLowerCase()
77 if (ua.match(/MicroMessenger/i) && typeof window.WeixinJSBridge !== 'undefined') { 98 if (ua.match(/MicroMessenger/i) && typeof window.WeixinJSBridge !== 'undefined') {
78 window.WeixinJSBridge.invoke('getNetworkType', {}, () => { 99 window.WeixinJSBridge.invoke('getNetworkType', {}, () => {
79 video.play() 100 video.play()
80 }) 101 })
81 } 102 }
103 + // 若短时间内仍无法播放,降级为图片背景,避免黑屏
104 + setTimeout(() => {
105 + if (!is_video_ready.value) {
106 + enable_image_fallback()
107 + }
108 + }, 1200)
82 }) 109 })
83 } 110 }
84 111
...@@ -87,7 +114,40 @@ onMounted(() => { ...@@ -87,7 +114,40 @@ onMounted(() => {
87 video.play() 114 video.play()
88 }, false) 115 }, false)
89 } 116 }
117 +
118 + // 兜底:在一定时间内仍未加载完成则切换到图片背景
119 + // setTimeout(() => {
120 + // if (!is_video_ready.value) {
121 + // enable_image_fallback()
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;
......