一. 背景
[医生端-2024年度执医成就]需要使用复杂的转场动画,单纯使用 css 方式开发成本高。经调研,使用 mp4 作为背景动画可以低成本实现业务需求。 线上地址,大家可以感受一下:muzhi.baidu.com/f2c/twentyF…
但存在问题如下:
- 视觉侧交付的视频体积过大,难以保证用户体验
- 考虑到网络情况等不可控因素,视频加载 / 播放可能会出现失败
二. 资源体积优化
2.1 名词解释
分辨率:
分辨率是指图像或视频中的像素数量。常见的分辨率有720p(1280×720)、1080p(1920×1080)和4K(3840×2160)等。分辨率越高,图像或视频的细节和清晰度就越高。
码率:
码率是指视频或音频数据传输速率,通常以每秒传输的比特数来表示,单位为kbps(千比特每秒)或Mbps(兆比特每秒)。码率越高,视频或音频的质量就越高,但同时占用的带宽也会更大。
帧率:
帧率是指每秒显示的图像帧数。通常以“帧每秒”来表示。常见的帧率有24、30、60等。帧率越高,视频播放就越流畅,尤其在快速运动的场景中能够更好地展现细节。
编码格式:
编码的目的是压缩数据量,采用编码算法压缩冗余数据。常用的编码格式有如下这两种
视频元数据(Metadata)
技术性元数据 包括:
- 文件大小
- 文件格式(如MP4, AVI, MOV等)
- 编码格式(如H.264, H.265)
- 创建和修改日期
- ......
内容相关元数据 包括:
- 创建日期
- 内容描述
- ......
2.2 视频格式处理
2.2.1 编码格式
判断设备是否支持 H265 格式,支持则采用 H265 编码格式视频,否则采用 H264 编码格式
- H265 视频压缩率高 & 对硬件设备要求较低
- 内嵌 webveiw H5 页面可能会不兼容 H265 格式,则采用 H264 视频格式
各编码格式具体对比如下:
- MPEG(MPEG-2、MPEG-4)
- 压缩效率:较低,主要用于早期数字电视和DVD等应用。
- 兼容性:历史最久,兼容性好
- H.26X(H.263、H.264/AVC、H.265/HEVC)
- H264
- 压缩效率:显著提升,比MPEG-4高50%以上。适合高清(HD)和全高清(FHD)内容。
- 兼容性:广泛支持,几乎所有现代设备(手机、PC、电视等)均提供硬件解码。
- H265
- 压缩效率:比H.264高约50%,适合4K和HDR视频。
- 兼容性:逐渐普及,支持的设备较新(2015年后发布的设备多支持)。
- H264
- AV1
- 压缩效率:比H.265和VP9高约20-30%,目前压缩效率最高的主流标准,适合8K、HDR视频。
- 兼容性:较新,2020年后发布的高端设备逐渐支持(如新款GPU、旗舰手机)。
- VP9
- 压缩效率:与H.265相近,比H.264高约50%。适合4K和高动态范围(HDR)。
- 兼容性:现代设备(如Android手机、Chrome浏览器支持良好),但普及度略低于H.265。
编码标准 | 压缩效率 | 硬解码兼容性 | 软解码开销 | 硬解码开销 |
---|---|---|---|---|
MPEG-2 | 低 | 全面 | 高 | 低 |
MPEG-4 | 中 | 部分支持 | 中 | 中 |
H.263 | 较低 | 几乎无 | 较低 | 较低 |
H.264 | 高 | 非常全面 | 中高 | 低 |
H.265 | 更高 | 新设备支持较好 | 高 | 较低 |
VP9 | 类似H.265 | Android/Chrome友好 | 高 | 较低 |
AV1 | 最高 | 新设备逐渐普及 | 极高 | 较高 |
2.2.2 原视频格式分析
视觉侧交付的文件中,视频编码格式是 H264,2s 的视频体积为 3.3M,不符合预期
使用 ffmpeg 查看具体视频信息
ffprobe -show_streams 登榜次数-264.mp4
[STREAM]
index=0
codec_name=h264
profile=Main
codec_type=video
width=1242
height=2688
coded_width=1242
coded_height=2688
level=60 !!!
duration=2.000000
TAG:encoder=AVC Coding
......
[/STREAM]
当使用H.264编码时,profile和level是指视频编码的配置参数,它们决定了视频的质量、兼容性和性能。具体来说,这些参数包括:
Profile(配置文件):
指定了编码器可以使用的特定功能和算法,影响了视频的压缩效率和质量。常见的profile包括Baseline、Main和High。Baseline适用于较低质量的视频,Main适用于一般质量的视频,而High适用于高质量的视频。
Level(级别):
指定了视频的参数,如分辨率、帧率和比特率的限制。不同的level对应不同的视频参数限制,例如Level 3.0适用于标清视频,Level 4.1适用于高清视频,Level 5.1适用于超高清视频。
简单来说:Profile 和 Level 的等级越高,文件压缩得越小,传输越快,但cpu消耗越多。
压缩级别越高不仅在压缩时cpu的消耗越高,视频在播放时也需要消耗更多的cpu进行解压,各类型手机的硬件参数不一样,所以支持的压缩级别也不同。
ios 设备中最高支持的 profile 为 High,Level 为 4.1
在兼顾视频质量 & 视频体积后,选择 Profile 为 Main,Level 为 4.1 等级的视频
2.2.3 视频格式处理
H265的视频体积会比H264 体积更小,但webview 内嵌 h5 页面可能对H265 的兼容性不好
通过运行时判断是否支持H265 格式,采用不同的视频格式
#!/bin/bash
# 指定你的视频文件夹路径
VIDEO_FOLDER="/Users/chenwenliang/Desktop/24"
OUTPUT_DIR="${VIDEO_FOLDER}/h264"
# 进入视频文件夹
cd "${VIDEO_FOLDER}"
# 循环处理文件夹内的每个MP4文件
for input in *.mp4; do
# 跳过不存在的文件(例如如果没有找到任何匹配的.mp4文件)
[ -f "$input" ] || continu
# 使用ffmpeg转换视频到H.264编码
ffmpeg -i "${input}" -c:v libx264 -profile:v main -level 4.1 "${OUTPUT_DIR}/${input%.mp4}_h264.mp4"
# 使用ffmpeg转换视频到H.265编码
ffmpeg -i "${input}" -c:v libx265 -vtag hvc1 "${OUTPUT_DIR}/${input%.mp4}_h265.mp4"
done
echo "转换完成!"
2.3 图片格式处理
- SVG 是基于XML的矢量图片格式,不失真无限放大。支持动画。
- JPEG 是有损压缩,不支持透明度或者动画。
- PNG 是无损压缩,支持透明度。APNG 是 PNG 的扩展,支持动胡奥。
- WebP 是无损和有损压缩,支持动画或者透明度。
- GIF 是位图图片格式,支持动画。
结论:
* 小图标或 logo 可以使用 SVG
* 背景图片不需要透明度,使用 JPEG 格式
* 对于超过特定大小的 GIF动图,可使用CSS动效或者视频替代。
2.4 处理后总体积
H264 视频18个 体积12.96 MB
H265 视频18个 体积3.93 MB
图片资源(含 gif) 42个 体积 5.48 MB
音频资源 1 个 体积 938.106KB
共计:23.29MB 最优情况只需加载 H265 视频 + 图片 + 音频 ≈ 11MB
三. 兜底处理
3.1 视频加载失败
**原因:**由于网络原因等不可控因素,视频加载可能会出现失败
**目的:**视频获取失败时能正常展示背景图片,保证用户体验
**核心逻辑:**在每个页面组件外层使用 backgroundImage 作为兜底处理
return pages.map((item, index) => {
const match = index === currentPage;
return (
<div
key={index}
style={{
zIndex,
pointerEvents: match ? 'auto' : 'none',
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
backgroundSize: 'cover',
backgroundPosition: 'center center',
backgroundRepeat: 'no-repeat',
backgroundImage: match ? `url(${item.bg})` : '',
}}
>
<CSSTransition
in={match}
timeout={100}
classNames={
enter: styles.reportEnter,
enterDone: styles.reportEnterDone,
exit: styles.reportExit,
exitDone: styles.reportExitDone
}
appear
unmountOnExit={!!isApp}
>
<div
style={{
width: '100%', height: '100%',
visibility: currentPage === index ? 'visible' : 'hidden'
}}
>
<item.component
order={index}
src={videoSrc}
bg={bg}
videoRef={videoRefArr.current[index]}
/>
</div>
</CSSTransition>
</div>
);
});
3.2 播放视频失败
原因:
- 视频加载失败导致用户上滑时播放视频失败
- 兼容性问题,某些浏览器视频播放时机必须和用户交互强绑定。但通过视频转场时必须监听上一页的视频播放完毕,触发下一页视频的播放,无法和用户操作强绑定。
目的: 播放转场视频失败时也能正常翻页
核心逻辑: 子组件内监听用户手势操作**,并重写 video.play 方法,**播放成功后 / 失败都需翻页
import { useCallback, useEffect } from 'react';
import { ON_PAGE_NEXT } from '@/constants';
import { useTwentyFourContext } from './useTwentyFourContext';
const useHandleVideoEnd = (secondVideoRef, setShowFirstVideo) => {
const { dcBill: { currentPage }, eventBus, updateDcBillData } = useTwentyFourContext();
const goNext = useCallback(() => {
updateDcBillData({
key: 'currentPage',
value: currentPage + 1
});
}, [currentPage, updateDcBillData]);
const onNextPage = useCallback(() => {
setShowFirstVideo(false);
secondVideoRef.current?.play(goNext);
secondVideoRef.current?.onVideoEnd(oNext);
}, [goNext, secondVideoRef, setShowFirstVideo]);
useEffect(() => {
eventBus?.on(ON_PAGE_NEXT, onNextPage);
return () => {
eventBus?.off(ON_PAGE_NEXT, onNextPage);
};
}, [eventBus, onNextPage]);
return onNextPage;
};
export default useHandleVideoEnd;
四. 兼容性问题处理
4.1 微信导航栏问题
问题: 微信路由跳转后会出现底部前进/后退导航栏,造成空间压缩影响视觉效果,且没有暴露 api 进行屏蔽
解决方案: 改造成单路由页面
4.2 safari 100vh问题
问题: safari 浏览器中 100vh 并不是指的可视区中的高度,会包括下面工具栏
使用 vh 进行布局时,就会可能造成功能列遮挡文字
解决方案: 通过 innerHeight 获取可视区高度,top / bottom 采用百分比布局
const width = window.innerWidth;
const height = window.innerHeight;
<div
style={{ maxHeight: height, maxWidth: width }}
ref={contentRef}
>
{buildPages}
</div>