移动端视频播放踩坑

H5 中很多场景交互较少强调动画展示的场景都可以借助视频实现,而且能够实现 100% 视觉还原😝,对于设计师来说最终呈现效果可控,对开发来说极大地降低了工作量,运营因为工期减少也会开心,简直是一石三鸟的事情!不过移动端 video 还是有很多坑的,以下是我做的一点调研。

video

去除 video 的播放控件

video 标签不设置 control 属性,在 iOS 与 Android 上也会出现播放控件,并且视屏元素的层级调整到最高,上方不能覆盖有其他元素。

Canvas 方案

可使用 pixi.js 播放 video 的功能,将视频画面用 Canvas 输出。

let app = new PIXI.Application({
    view: document.getElementById('stage'),
    forceCanvas: true,
});
// create a video texture from a path
let texture = PIXI.Texture.fromVideo(require('assets/video/video.mp4'));
// create a new Sprite using the video texture (yes it's that easy)
let videoSprite = new PIXI.Sprite(texture);
app.stage.addChild(videoSprite);

该方式在 iOS 高德端内还是会展示播放控件,至于 Android 端内表现良好,但是因为前者的问题所以该方案还是被舍弃了。

CSS 样式

非移动端直接去除 control 属性即可,但是移动端仅仅依靠这是不行的。

至于移动端这里还得分情况,Android 上的 Chrome 其实只需不声明 control 属性即可。即便申明了用如下 CSS 也可移除播放控件。

video::-webkit-media-controls-panel {
    display:none !important;
}

iOS 上的 Safari 上的播放控件貌似是无法使用 CSS 隐藏掉的,需去除 control 属性,但是只要播放后还是会进入全屏模式,这时候需要添加 playsinline,不过这种方案与 CSS 没啥关系了。

所以单纯依靠 CSS 去除播放控件也是不可行的,其余端的测试也没继续下去。

https://stackoverflow.com/questions/15126921/why-do-no-user-agents-implement-the-css-cursor-style-for-video-elements/15145555#15145555

iOS 微信端内

使用 playsinline="true"webkit-playsinline="true" 属性可以使视频内联播放。iOS 10 以及之后的版本支持 playsinline="true",iOS 10 之前的版本可使用webkit-playsinline="true"

具体参考 Webkit 官方文章: New <video> Policies for iOS

<video src="video.mp4"
       playsinline="true"
       webkit-playsinline="true"
></video>

如动态添加 playsinlinewebkit-playsinline 无效,需提前在 HTML 中定义好。上述属性配置需 iOS 中 WebView 中需要有相应的设置支持,否者前端已经设置上述属性也是无效的。

iOS 高德端内

使用 enableInlineVideo 插件,可实现端内无控件播放。

<video src="video.mp4"
       id="video"
       playsinline="true"
       webkit-playsinline="true">
</video>

<script>  
    let video = document.querySelector('#video');
    video.play();
    enableInlineVideo(video);
</script>

<style lang="scss">
.IIV::-webkit-media-controls-play-button,
.IIV::-webkit-media-controls-start-playback-button {
    opacity: 0;
    pointer-events: none;
    width: 5px;
    object-position: center center;
}
</style>

Android 高德端内

目前各个方案都能很好的隐藏播放控件。

Android 微信端内

腾讯浏览器 X5 内核 提供了 H5 同层播放器接入规范,由于微信内置浏览器使用的是腾讯浏览器 X5 内核,可使用其一些私有属性。

x5-video-player-type

通过 video 属性 x5-video-player-type声明启用同层 H5 播放器。

<video src="http://xxx.mp4" x5-video-player-type="h5"/>

这个属性需要在播放前设置好,播放之后设置无效。

x5-video-player-fullscreen

x5-video-player-fullscreen 可使视频播放时进入到全屏模式。如果不申明此属性,页面得到视口区域为原始视口大小(视频未播放前),比如在微信里,会有一个常驻的标题栏,如果不声明此属性,这个标题栏高度不会给页面,播放时会平均分为两块(上下黑块)。

<video id="video" src="xxx" x5-video-player-type="h5" x5-video-player-fullscreen="true"/>

声明此属性,需要页面自己重新适配新的视口大小变化。可以通过监听resize 事件来实现。

window.onresize = function(){
    video.style.width = window.innerWidth + "px";
    video.style.height = window.innerHeight + "px";
}

同样,这个属性需要在播放前设置好,播放之后设置无效。

控件隐藏测试结果

机型 高德端内 微信端内 说明
iPhone 8 Plus ✔️ ✔️
iPhone X ✔️ ✔️
Galaxy Note 4 ✔️ ✔️
红米 Note 4 ✔️ ✔️
魅族 Pro 6 ✔️ ✔️* Title Bar 透明
Galaxy S6 edge 高德 App 会自动假死退出,无法完成测试

特殊机型问题

魅族 Pro 6

魅族 Pro 6 在微信端内会新开一个层展示视频,且标题栏会变透明,去除属性 x5-video-player-fullscreen 也无效。

自动播放

与音频一样,视屏播放也需要用户的操作来触发,通过一些按钮来诱导用户触发视频播放。

let videoPlay = () => {
    video.play();
    document.removeEventListener('touchstart', videoPlay);
};
document.addEventListener('touchstart', videoPlay, false);

检测是否支持自动播放

play() 方法会返回一个 promise,如果无法播放会抛出一个错误,可以被 catch 到。

video.play().catch((error) => {
    // do something error handler
});

iOS

iOS 上的 Safari 视频自动播放

iOS 10 中允许自动视频播放的两种情况:

  1. 无音轨视频
  2. 无声音视频(设置了 muted 属性)
  3. 视频元素需在是视口内且是可见的
  4. 视频元素需插入到 DOM 中

Android

Android Chrome

Chrome 53 之后也支持了 iOS Safari 同样的策略,无音轨或者静音的视频可以自动播放。

<video autoplay muted>
  <source src="video.webm" type="video/webm" />
  <source src="video.mp4" type="video/mp4" />
</video>

Android 其他浏览器

Firefox 和 UC 浏览器已经在 Android 上支持自动播放,无论是否静音,它们不会阻止任何类型的自动播放。未验证

Android 高德端内

在 875 之后介入了 UC WebView,本以为可以实现同 UC 的策略但是测试。不可以自动播放,需用户手动触发。

iOS 与 Android 微信端内

在微信中集成 JS-SDK 后监听 WeixinJSBridgeReady 事件可触发视频播放,可无需经由用户操作从而实现音乐播放的目的,因该是微信内部做了处理。

document.addEventListener(
    'WeixinJSBridgeReady',
    function() {
        video.play();
    },
    false
);

预加载

如视频播放因资源未加载完毕而造成的卡顿会影响体验,需预先加载好视频文件。在PC 端可用 preload="auto" 属性实现视频的预加载。但 iOS 中 preload="auto" 属性是不生效的,可支持proload="metadata"到,同样的策略在 Android Chrome 也适用。

preload

尝试使用 preload 可以实现资源预加载,其其实是一个声明形的 fetch,可以强制浏览器在不阻塞 documentonload 事件的情况下请求资源,即强制提升视频文件的优先级。通过 Can I Use 查询 preload,iOS 11.3 与 Chrome 67 之后才开始支持。

<link rel="preload" href="video.mp4" as="video" type="video/mp4" onload="handleVidoeLoaded">
<video id="video"></video>
<script>
function handleVidoeLoaded() {
    const video = document.getElementById("video");
    video.src = 'video.mp4';
}
</script>

也通过 JavaScript 动态添加 video 预加载:

let link = document.createElement('link');
link.setAttribute('rel', 'preload');
link.setAttribute('href', 'video.mp4');
link.setAttribute('as', 'video');
link.setAttribute('type', 'video/mp4');
link.addEventListener('load', handleVideoLoaded)
document.head.appendChild(link);
// ... 

经过测试该方式并不能提前预加载视频文件

XMLHTTPRequest

Google Developer 中介绍使用 Fetch 手动缓冲视频文件的方法,借助这个思路,同理使用 XMLHTTPRequest 先将视频文件加载到本地,然后给 video 元素的 src 属性赋值。

const { video } = this.$refs;
const self = this;
const req = new XMLHttpRequest();

req.open('GET', `video.mp4?t=${new Date() * 1}`, true);
req.responseType = 'blob';
req.onload = () => {
    if (req.readyState === 4) {
        if (req.status === 200) {
            const videoBlob = req.response;
            const vid = URL.createObjectURL(videoBlob);
            video.src = vid;
        }
    }
};

req.onerror = () => {
// Error
};

req.onprogress = () => {
};

req.send();

机型测试

本次测试高的端内为 850 以及以上版本,为 UC 内核。

  1. 视频无音轨或者为 muted
  2. 使用 iphone-inline-video
  3. 视频位于页面可见范围内

高德端内

系统 系统版本 机型 自动播放1 无播放控件 可被覆盖 备注
iOS 11.41 iPhone 8 Plus ✔️ ✔️ ✔️
12.01 iPhone 6 ✔️ ✔️ ✔️
Android 6.0 红米 Note 4 ✔️ ✔️ ✔️
8.0 华为 Meta 9 ✔️ ✔️ ✔️ 播放延迟2
7.0 小米 Max ✔️ ✔️ ✔️
8.1 华为 P20 ✔️ ✔️ ✔️
8.1 华为 Meta 10 Pro ✔️ ✔️ ✔️
7.0 Moto Z Play ✔️ ✔️ ✔️

微信端内

系统 系统版本 机型 平台 自动播放 播放控件 可被覆盖 备注
iOS 12.01 iPhone 6 微信端内 ✔️ ✔️ ✔️
Android 6.0 红米 Note 4 微信端内 ✔️ ✔️ ✔️

参考


  1. 需用户手动触发播放 
  2. oncanplaythrough完成后也不回立即播放,会延迟 1s  

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注