需求背景
简单描述一下这个功能:在一个走马灯组件里面第一屏是一个视频,第二屏第三屏是图片,点击播放视频,播放过程中滚动窗口,视频 fixed 在窗口顶部,回到顶部,视频还原,两个窗口视频播放进度无缝衔接,类似于淘票票和手机淘宝的功能。
技术方案
在此之前不得不说 那些年我们踩过的坑了。。。
确定要做这个需求的时候,确定了以下几个方案:
- 用一个 video ,页面滚动 fixed 在顶部;但是视频是嵌套在 Carousel 里面的,display:fixed; 对于 parentNode 加了 transform 属性根本不生效,position: sticky; 根本就别想了。这时候你可能会想到 【画中画】video.requestPictureInPicture() 解决,但是 画中画 需要用户主动触发。。
- 《传送门 react.createProten》, 传送门可以任意挂载 dom ,但是一直拿不到合适的 currentTime(视频当前播放时间),并且部分浏览器(Safari)需要用户主动唤起 play()事件,最后选择放弃;
- 两个 video ,一个 在 Carousel 里面显示,一个在页面顶部 fixed,这里搞了个 黑科技,让 fixed 的 video display:none; 了,至于为什么这样做忘记了,目的就是让 dom 显示好像~等我记起来了补充在这。
下文中,便针对 方案三 进行阐述;
事件/方法
video.pause() // 暂停
video.play() // 播放
video.webkitRequestFullScreen() //全屏
video.currentTime //当前进度
video.duration //总进度
详情请移步 mdn 或 w3cSchool。
顽固 bugfix
视频只播放音频没有画面,预发视频的 url 是 http 的不是 https 的,后端帮忙改成 https 的发现也没用,也是查了很久,没找到解决方案,最后发现 Carousel 才是始作俑者!也就是说这个问题根本跟环境无关,而是 Carousel 里面轮播了多少个 item 有关!
查看一下页面元素:
发现 Carousel 里面轮播的内容多出了很多,看一下多出来了几个 video:
一个 video 是 fixed 的 video,默认 display:none; ,还有一个是 Carousel 里面轮播的 video ,另一个则是 Carousel 为了实现 无线循环 而多出来的 video!播放 video 的时候,触发了 onPlay,但其实真正播放的是隐藏起来为了实现 无限循环 的 video,并且 这两个 Carousel 里面的 video 的 index 是相同的~
『解决方案』:关掉 无限循环(infinite) !简单粗暴~
infinite
Type: bool
Default: true
Description: Infinitely wrap around contents
<Carousel infinite={false} afterChange={afterChange}>
// ……
</Carousel>
Android 和 ios 表现得不同形式
由于测试设备有限,用了三部手机测试兼容:
android:vivo IQOO 3、小米 11
ios:iPhone12
为了避免更多的雷,video 禁用了 controls ,设计师帮忙切了个 ⏯ 的图定位到 video 上面了~通过 onClick 事件触发 video 的 play 和 pause。
实现方案详细阐述
页面采用两个 video,一个现实,一个 display:none; ,当窗口 scroll 到一定距离时 fixed 的video 显示,并且在 需要fixed 和 需要 fixed 的变换过程中互换两个视频的 currentTime ,保证视频播放的 currentTime 是可以衔接上的。
播放是否自动全屏
ios : 自动全屏;
android: 不会自动全屏;
影响:ios 播放自动全屏;
解决方案:
x5-playsinline="true" // 安卓需要设置的属性
playsInline // ios需要设置的属性
webkit-playsinline="true" // ios10需要设置的属性
preload="auto" // 预加载
一个页面两个 video
ios : fixed 的视频 play 时另一个 自动 pause;
android: fixed 的视频 play 时另一个不会 pause;
影响:此时你可能会听间视频播放的时候会有两个声音,一前一后,虽然不仔细听听不出来,但是影响毕竟是在的。。。
hack:判断 android 机时,一个视频播放暂停另一个;监听一下 onPlay 和 onPause 事件就好了~
判断机型代码段:
/* 判断android与ios */
export function phoneModel() {
const u = navigator.userAgent;
// const isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1; //android终端或者uc浏览器
const isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); // ios终端
if (isiOS) {
return false;
}
return true;
}
controls={false} 是否生效
ios : 生效,并且 play 之后也不会再出现;
android: 生效,但是 play 之后会出现自己的 播放/暂停/进度条/全屏 按钮,大概有 3s 左右的 delay;
框出来的都是 android 机下 video play 之后自带的 controls
影响:上文说到是通过 onClick 去控制 开关变量 操作 video 的 play 和 pause,但是 android play 之后自带了一些 controls ,如果用它自带的 controls 暂停播放的时候,是无法触发 onClick 事件的,关键没哟偶很好的办法禁用,就很头痛~
hack:监听 onPlay 和 onPause 事件,事件里面操作开关变量~监听开关变量,控制 play 和 pause~
video 一旦播放是否会置于顶层
ios:不会置于顶层;
android:置于顶层;
hack:Carousel 调用 afterChange 判断 currentIndex 和 当前 index 不同的时候隐藏掉就好了, 不建议用 display:none;用 width:0;height:0; 也可以完美解决~