0、前情提要
为了解决下面这些问题,我考虑到抽出一个公共的Video组件,使其能在多端的效果一致
-
1.
Video组件即使未点击也会预先拉取视频资源,小程序的内存非常容易溢出,当一个页面渲染多个Video在部分老旧机型上会直接爆内存,导致微信卡顿/闪退。 -
2.ios的浏览器播放video是直接
全屏打开,安卓则是无论video的宽高多小都是在video内部播放,必须手动全屏。 -
3.当一个页面需要渲染多个video(比如视频列表的时候),安卓&安卓h5端会出现
多个video同时播放的问题。 -
4.Video组件在Taro(小程序)和H5部分API不一致的问题,例如
Taro.createVideoContext的到的Video实例,无法操控h5端的Video播放/暂停/全屏等行为,如下所示,仅在小程序端可用。 -
5.Video组件在Taro(小程序)和H5
部分样式不一致
1、需求分析
综上所述,我们需要的Video需求如下
- 能在单个页面
渲染多个video,且在小程序能流畅运行。 - 实现ios和安卓
小程序端的点击播放效果统一。 - 实现ios和安卓
h5端的点击播放效果统一。
2、思考需求解决方案
2.1、解决小程序单个页面渲染多个Video内存溢出
- 这个问题在寻找小程序官方社区以后有人给出了可行的方案,在接口中传递一张视频的
缩略图,当点击以后再切换Image组件为Video组件,经简单测试,思路可行,在老旧的手机只要视频不是很大上也不会出现爆内存的情况。
2.2、实现ios和安卓的点击播放效果统一
- 这一点在思考后决定
统一为ios的效果,如果Video都是点击就全屏播放,退出全屏就暂停播放的话,安卓端单个页面多个Video同时播放的问题也得到了解决,可以说是用一个方案解决了两个Issue。
2.3、解决Taro.createVideoContext的实例方法无法控制h5的video
- 这个目前思考到只能通过
环境变量拿到当前的环境是weapp还是h5,对h5上使用useRef绑定Video的引用,来操控h5的video行为,期待后续Taro对Video组件的支持更好吧。
2.4、样式不一致的问题
- css的问题,不会就问GPT。
3、思考组件的props需要什么
src:string,video组件播放的视频资源- 为了要更加通用,要控制Video的播放,我们需要传入一个
play:Boolean,来控制当前Video是否播放 thumb:string,Video组件的缩略图,点击后切换视频id:string,video组件的id,用于拿到Video实例(目前看来意义不大,可供后续拓展h5渲染多个video实现更流畅的效果)// props Type type Props = { src: string play: Boolean thumb: string id?: number }
4、编写CommonVideo组件
- 为了通过Play控制Video的播放/同一个页面只渲染一个Video,未点击的用缩略图占位(解决2.1和2.3)
// 我们根据props传入的play属性,判断是渲染video还是渲染image // uniqueId的传入是为了多个video和imgae之间切换全屏异常的问题 return ( <View className={styles.video_wrapper}> {play ? ( <Video id={`videoTaroPlayer${uniqueId}`} ref={videoH5Ref} showBottomProgress="false" x5-video-player-type="h5" style={{ width: '100%', height: '100%' }} x5-video-player-fullscreen="true" {...rest} ></Video> ) : ( <Image src={thumb} className={styles.video_wrapper_thumb} mode="widthFix" /> )} </View> ) } - 统一ios和安卓在小程序端打开视频的效果一致,全部统一为ios打开视频的效果,既点击视频就打开全屏播放,退出全屏就暂停播放(解决2.2)
- 先拿到video的小程序和h5的引用,在根据环境调用commonVideoFullScreen方法,点击就全屏并播放
- 这里有一个问题:ios和安卓在h5上全屏的api是不一样的,所以只能各写一遍了
// ref 控制 h5 的播放暂停 const videoH5Ref = useRef(null) // videoTaroPlayer 控制 Taro 的播放暂停 const videoTaroPlayer = Taro.createVideoContext(`videoTaroPlayer${id}`) const commonVideoFullScreen = (video: HTMLVideoElement, pageEnv: PageEnv) => { switch (pageEnv) { case 'weapp': videoTaroPlayer.requestFullScreen({ direction: 0 }) // 因为play的方法要等待视频资源加载完成才能拿到,默认等待0.5秒,防止用户一进来立刻点击大视频导致卡顿 delayOnVideoReady(videoTaroPlayer.play, 500) break case 'h5': if (video.requestFullscreen) { video.requestFullscreen() video.addEventListener('loadedmetadata', () => { video?.play() }) } break default: // 如果 pageEnv 不是 'weapp' 或 'h5',可以在这里处理默认情况 break } } useLayoutEffect(() => { const video: HTMLVideoElement = videoH5Ref.current! const pageEnv: PageEnv = isWeapp ? 'weapp' : 'h5' if (play) { commonVideoFullScreen(video, pageEnv) } }, [play])
5、遇到的疑难杂症
直接调用video.play()报错:调试的时候发现在useEffect直接调用video的play属性会报错:App.jsx:17 Uncaught (in promise) DOMException: play() failed because the user didn't interact with the document first., 查找stackoverflow发现是因为大多数现代浏览器都会阻止自动播放带有声音的视频,除非用户首先与页面进行了交互,以防止页面加载时自动播放声音可能会打扰用户的体验,但是使用muted属性让视频静音可以跳过这个浏览器限制,虽然不影响我们这次组件的编写(因为我们是点击后再全屏播放),但是可以扩展一下这方面的知识!Taro Video在h5的效果和原生的Video不一致:在chorme中,默认使用const video = videoRef.current;video.requestFullscreen()就可以全屏并自动播放,但是在Taro的h5开发中,必须要等待loadedmetadata再调用play方法/这是因为视频资源还没加载完成调用play会报错。- 在切换image和video的过程中,ios浏览器(特指
safari),在切换成video后无法在代码层面全屏,这是因为ios的video浏览器限制,不支持在播放时默认全屏,但是这也属于多端之间可以接受的差异