目前图文形式向视频化转型已经成为一种风向,其中H5视频化能做到一次开发,多端通用。
这里简单调研下H5播放视频的一些问题和解决方案。主要分为三部分:
- H5视频化所遇到的问题
- 对视频的一些基础操作
- 调研大厂,小红书,B站,抖音的
H5页面在各浏览器的播放情况
相信一定有所收获~
0.视频加载基本介绍
在视频/音频(audio/video)加载过程中,事件的触发顺序如下:
onloadstart(浏览器开始寻找指定资源)ondurationchange(视频/音频 的时长发生变化时触发)onloadedmetadata(指定视频/音频 的元数据加载后触发)onloadeddata(当前帧的数据加载完成且还没有足够的数据播放)onprogress(下载指定的视频/音频 时触发)oncanplay(用户可以开始播放视频/音频 时触发)oncanplaythrough(可以正常播放且无需停顿和缓冲时触发)
一.H5视频化所遇到的问题
1.初始化样式不统一
由于各家浏览器播放视频时展示的视频控件样式并不一致,所以建议隐藏默认的视频播放控件,自己开发统一。
const video = document.createElement('video');
const source = document.createElement('source');
source.setAttribute('src', src);
video.textContent = '您的浏览器不支持 video 标签。';
video.appendChild(source);
video.setAttribute('controls', false);
video.controls = false;
2.自动播放的问题
这是由Chrome的官方策略导致:developers.google.com/web/updates…
并且在IOS10后的safari和微信也均不能自动播放
解决方式有以下几种:
(1).视频静音状态自动播放
const video = document.createElement('video');
const source = document.createElement('source');
source.setAttribute('src', src);
video.textContent = '您的浏览器不支持 video 标签。';
video.appendChild(source);
video.muted = true;
video.setAttribute('autoplay', true);
video.setAttribute('controls', false);
video.controls = false;
(2).引导用户点击播放
考验产品的交互设计能力,可以参考B站的页面,截图见B站视频化测试一章
(3).媒体参与指数(MEI) 达到一定的值时可以自动播放
MEI是衡量个人在网站上消费媒体的倾向。
Chrome的策略可以看这个文档: 官方文档
规则大概如下:
- 媒体(音频/视频)的播放大于7秒
- 音频存在且未取消静音。
- 带视频的标签处于活跃状态
- 视频大小(以px为单位)必须大于200x140。
Chrome会计算媒体互动分数,当它足够高时,允许媒体 仅在桌面上 自动播放。(比如B站,youtube)
用户的MEI可在chrome://media-engagement/内部页面上找到。
(4).同源地址第二次进入时可以自动播放,刷新页面也可以
这点建议自己试试,依赖浏览器的兼容性,不是很稳定
3.视频全屏播放问题
ios端播放视频会弹出全屏的效果,而且视频会 “浮” 在页面上,页面上的所有元素都只能在视频元素下面,设置z-index也没有任何的效果。ios环境的微信浏览器也是如此。
解决方式:
设置webkit-playsinline和playsinline属性,基本就可以将视频内敛到webview里面了
video.setAttribute('webkit-playsinline', true);
video.setAttribute('playsinline', true);
4.poster封面问题
在android下,多数设备是不显示视频封面的,即使加上poster属性
微信浏览器不自动播放的时候,封面背景为黑色,且微信浏览器无法截取第一帧作为封面。
poster在android兼容性不好,更建议在视频上层加个div图片。
由于android不允许视频上层有东西(层级问题,可以采用下面的一种方式方式),所以首先将视频设为的width:1px,当播放后,上层的封面remove掉,同时width:100%或者你想要的宽度。
5.video层级问题
在X5浏览器里默认video播放时的层级是最高的,无论你调多大的z-index参数都不能改变,所幸的是微信在2017年9月提供了支持,只要我们在video中加上x5-video-player-type='h5' 就可以实现在video上再加DOM。
video.setAttribute('x5-video-player-type', 'h5');
video.setAttribute('x5-video-orientation', 'portrait');
二.对H5视频的简单操作
1.初始化video
const awareness = ({ src }) => {
const video = document.createElement('video');
const source = document.createElement('source');
source.setAttribute('src', src);
video.textContent = '您的浏览器不支持 video 标签。';
video.appendChild(source);
video.setAttribute('loop', true);
video.setAttribute('width', '100%');
video.setAttribute('preload', 'auto');
// 解决视频层级过高的问题,视频播放不会进入全屏状态,启用X5内核同层渲染
video.setAttribute('x5-video-player-type', 'h5-page');
// 播放器支付的方向, landscape横屏,portraint竖屏,默认值为竖屏
video.setAttribute('x5-video-orientation', 'andscape|portrait');
// ios 10中设置可以让视频在小窗内播放,也就是不是全屏播放
video.setAttribute('webkit-playsinline', true);
// IOS微信浏览器支持小窗内播放
video.setAttribute('playsinline', true);
// 更改视频资源,并重新载入视频
// video.load();
return video;
};
2.获取视频的第一帧
通过创建canvas标签,利用其drawImage() 方法在画布上绘制该视频,然后运用toDataURL方法转换canvas上的图片为base64格式,并将base64格式的图片作为video标签的poster属性。
需要注意的是,由于canvas无法对跨域的图片进行操作,需要提前处理好跨域问题。
function getVideoBase64(url) {
return new Promise(function (resolve, reject) {
let dataURL = ''
let video = document.createElement("video");
video.setAttribute('crossOrigin', 'anonymous');//处理跨域
video.setAttribute('src', url);
video.setAttribute('width', 400);
video.setAttribute('height', 240);
video.addEventListener('loadeddata', function () {
let canvas = document.createElement("canvas")
let width = video.width //canvas的尺寸和图片一样
let height = video.height
canvas.width = width
canvas.height = height
canvas.getContext("2d").drawImage(video, 0, 0, width, height); //绘制canvas
dataURL = canvas.toDataURL('image/jpeg'); //转换为base64
resolve(dataURL)
})
})
}
3. 获取视频尺寸
video.onloadedmetadata = () => {
const height = video.videoHeight
const width = video.videoWidth
}
4.给视频加封面,让视频上下居中
// 等到元数据加载完成
const loadedmetadata = () => {
const video = videoRef.current;
// 获取视频的宽和高
const height = video.videoHeight;
const width = video.videoWidth;
video.height = width / 750 * height;
// 根据设计稿尺寸设置高度
video.setAttribute('height', width / 750 * height);
video.setAttribute('poster', cover);
const { offsetHeight } = document.body;
const { offsetWidth } = document.body;
// 设置距离顶部的高度,让视频上下居中
const topHeight = `${(offsetHeight - video.offsetHeight) / 750 * offsetWidth - state.navBarHeight}px`;
video.style.top = `${(offsetHeight - video.offsetHeight) / 750 * offsetWidth - 15}px`;
// 获取视频的时长并设置
if (video.duration) {
setDuration(video.duration);
}
};
5.video事件监听
/**
* @description: video上需要监听的事件
*/
const videoMountEvent = () => {
const video = videoRef.current;
video.addEventListener("loadedmetadata", loadedmetadata);
video.addEventListener("play", play);
video.addEventListener("playing", playing);
video.addEventListener("waiting", waiting);
video.addEventListener("pause", pause);
video.addEventListener("ended", ended);
};
/**
* @description: video上需要卸载的事件
*/
const videoUnmountEvent = () => {
const video = videoRef.current;
video.removeEventListener("loadedmetadata", loadedmetadata);
video.removeEventListener("play", play);
video.removeEventListener("playing", playing);
video.removeEventListener("waiting", waiting);
video.removeEventListener("pause", pause);
video.removeEventListener("ended", ended);
};
6.设置进度条
/**
* @description: 设置进度条
*/
const setProgress = () => {
const video = videoRef.current;
timeID.current = requestAnimationFrame(() => {
setCurrentTime(video.currentTime);
setProgress();
});
};
/**
* @description: 视频暂停中
* @return {*}
*/
const pause = () => {
// 取消进度条更新
cancelAnimationFrame(timeID.current);
setVideoStatus('pause');
};
/**
* @description: 视频播放完成
* @return {*}
*/
const ended = () => {
// 取消进度条更新
cancelAnimationFrame(timeID.current);
setVideoStatus('ended');
};
const Progress = ({ duration, currentTime }) => {
return (
<>
{!!duration && (
<div className={cx('video-controll_progress')}>
<div
className={cx('video-controll_progress__line')}
style={{ width: `${(currentTime / duration) * 100}%` }}
>
</div>
</div>
)}
</>
);
};
三.测试各家大厂的H5播放情况
1.小红书
(1).设备信息
| 手机型号 |
|---|
(2). H5页面播放情况
使用的地址:www.xiaohongshu.com/discovery/i…
| 浏览器 | 图标 | 版本 | 展示情况 | 说明 |
|---|---|---|---|---|
| 360浏览器 | v10.0.5.300 | 1.进页面就会被浏览器的控件接管2.可以播放和暂停 | ||
| UC浏览器 | v15.1.2.1202 | 1.能播放,能暂停2.会展示浏览器自带的视频控件3.会提示下载app | ||
| 搜狗浏览器 | 11.9.7.7100 | 1.可以播放,第一次播放时会黑屏一会2.可以暂停3.会提示下载app | ||
| 百度浏览器 | 135010 | 1.点击能播放2.播放后会展示出浏览器默认的控件3.播放能暂停4.会提示下载app | ||
| chrome | 78.0.3904.96 | 1.点击能播放2.播放时能暂停3.暂停后会提示有可能危险文件,是否保留 | ||
| 火狐浏览器 | 68.12.0 | 没有播放按钮,点击不能播放,点啥都不行,没有任何反应 | ||
| 自带浏览器 | 48.8.4.1_f00a28e | 1.不能自动播放2.点击能播放,再点击能暂停3.点击暂停后,会有弹窗 | ||
| 微信浏览器 | 8.0.33 | 1.不能自动播放2.点播放按钮能播放3.播放状态下,点视频会跳转到下载页面 |
2. 抖音
(1).设备信息
| 手机型号 |
|---|
(2).H5页面播放情况
使用的地址:m.ixigua.com/douyin/shar…
| 浏览器 | 图标 | 版本 | 展示情况 | 说明 |
|---|---|---|---|---|
| 夸克浏览器 | 6.2.0.245 | 1.可以播放和暂停2.但会出现浏览器自带的控件 | ||
| 360浏览器 | v10.0.5.300 | 1.不能自动播放2.点击能播放,能暂停 | ||
| UC浏览器 | v15.1.2.1202 | 1.不能自动播放2.点击能播放,能暂停3.播放后会展示浏览器自带的控件,且控件会占满页面,看不到其他操作按钮,甚至没办法缩小 | ||
| 搜狗浏览器 | 11.9.7.7100 | 1.不能自动播放2.点击能播放,能暂停 | ||
| 百度浏览器 | 135010 | 1.不能自动播放2.点击能播放,能暂停 | ||
| chrome | 78.0.3904.96 | 1.不能自动播放2.点击能播放,能暂停 | ||
| 火狐浏览器 | 68.12.0 | 1.不能自动播放2.点击能播放,能暂停 | ||
| 自带浏览器 | 48.8.4.1_f00a28e | 1.不能自动播放2.点击能播放,能暂停 | ||
| 微信浏览器 | 8.0.33 | 1.不能自动播放2.点击能播放,能暂停 |
3. B站h5播放情况
(1).设备信息
| 手机型号 |
|---|
(2).H5页面播放情况
使用的地址:www.bilibili.com/video/BV1FM…
| 浏览器 | 图标 | 版本 | 展示情况 | 说明 |
|---|---|---|---|---|
| 浏览器 | 图标 | 版本 | 展示情况 | 说明 |
| 360浏览器 | v10.0.5.300 | 1.进入页面后会先提示跳转app页面2.点击立即播放能播放3.有自定义控件 | ||
| UC浏览器 | v15.1.2.1202 | 1.进入页面后会先弹出一个蒙层,推荐去app观看2.点几立即观看后,出现弹窗3.点击继续网页观看,能播放4.能自定义控件 | ||
| 搜狗浏览器 | 11.9.7.7100 | 1.进入页面后会先提示跳转app页面2.能自定义控件 | ||
| 百度浏览器 | 135010 | 1.进入页面后会推荐去app观看2.点几立即观看后,出现弹窗3.点击继续网页观看,能播放4.能自定义控件 | ||
| chrome | 78.0.3904.96 | 1.进入页面后会推荐去app观看2.点几立即观看后,出现弹窗3.点击继续网页观看,能播放4.能自定义控件 | ||
| 火狐浏览器 | 68.12.0 | 1.先进入跳转app的页面2.之后会进入到无法理解该地址的情况 | ||
| 自带浏览器 | 48.8.4.1_f00a28e | 1.有自带的缓冲2.点击后有自带的控件3.能自动播放 | ||
| 微信浏览器 | 8.0.33 | 1.不能自动播放2.点击后能播放,能自定义控件 |
总结
虽然会遇到各种各样的问题,甚至各大厂也没有完美的解决方案,但H5视频化已成互联网用户习惯,必须要做。
需要调研清楚,哪些是可以解决的问题,哪些是无法解决的feature,更考验与产品和测试的沟通(忽悠)能力。