X5同层播放器应用实践

Heero.Luo发表于5年前,已被查看7606次

移动端浏览器中的video元素是比较特别的,早期无论是在iOS还是Android的浏览器中,它都位于页面的最顶层,无法被遮挡。后来,这个问题在iOS下得到了解决。但是对Android的大部分浏览器来说,问题仍然存在。X5是腾讯基于Webkit开发的浏览器内核,应用于Android端的微信、QQ、QQ浏览器等应用。它提供了一种名叫「同层播放器」的特殊video元素以解决遮挡问题。

简单使用

只要给普通的video元素加上X5的自定义属性 x5-video-player-type ,就可以调用同层播放器。示例代码如下:

body {
	margin: 0;
	background: #000;
}
.video {
	width: 100%;
}
<div class="player">
    <video id="video" class="video" controls="controls" playsinline x5-video-player-type="h5">
        <source src="video.mp4" />
    </video>
</div>

点击播放后,页面会瞬间拉伸(体验有点差),然后就进入了全屏状态,视频默认在中间位置:

同层播放器默认UI

调整位置

在全屏状态下,调整视频位置的通用做法是:把video元素的尺寸设成满屏,再通过 object-position 样式属性控制视频内容的位置。相关代码如下:

.fullscreen .video {
    object-position: center top;
}
var player = document.getElementById('video');
var isFullScreen;

// 进入全屏,设置状态
player.addEventListener('x5videoenterfullscreen', function() {
    isFullScreen = true;
    // 在body上添加样式类以控制全屏状态下的页面布局
    document.body.classList.add('fullscreen');
}, false);

// 退出全屏时,清空状态
player.addEventListener('x5videoexitfullscreen', function() {
    isFullScreen = false;
    document.body.classList.remove('fullscreen');
    player.style.width = player.style.height = '';
}, false);

// 同层播放器进入全屏状态会导致窗口resize,但退出全屏不会
window.addEventListener('resize', function() {
    if (isFullScreen) {
        // 设为屏幕尺寸
        player.style.width = window.screen.width + 'px';
        player.style.height = window.screen.height + 'px';
    }
}, false);

效果如下方左图所示,可见,此时视频距离顶部尚有一些距离。这个问题与 x5-video-player-fullscreen 属性有关。如果不声明这个属性,原标题栏的占位不会分配给页面,而是平均分成上下两块,分别位于窗口顶部和底部。因此,视频无法顶到最上方。

x5-video-player-fullscreen 的作用

补充 x5-video-player-fullscreen 属性后,问题就解决了(上方右图):

<video id="video" class="video" controls="controls" playsinline x5-video-player-type="h5" x5-video-player-fullscreen="true">
    <source src="video.mp4" />
</video>

大家还可以发现,顶部有一层黑色渐变(上图不太明显,可以看下文的图)以及两个按钮。据官方文档所述,这些都是无法移除的。

全屏状态下的布局

实际业务中,页面多半不会只有一个视频这么简单,下面就开始添加其他页面元素(请行引入rem布局所需的脚本)。首先是在视频之前加上标题栏:

.header {
	width: 100%;
	height: 1.14rem;
	line-height: 1.14rem;
	background: #fff;
	font-size: 0.36rem;
	text-align: center;
	color: #000;
}
<header id="header" class="header">标题栏</header>
<div class="player">
    <video id="video" class="video" controls="controls" playsinline x5-video-player-type="h5" x5-video-player-fullscreen="true">
        <source src="video.mp4" />
    </video>
</div>

然而,点击播放进入全屏状态后,标题栏就消失了,其实它是被视频挡住了。既然同层播放器是可以被遮挡的,那可以试试绝对定位:

.fullscreen .header {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 9999;
}

从下方左图可见,标题栏确实可以挡住视频了。

标题栏

此时视频内容被遮挡,所以要将其下移(上方右图):

.fullscreen .video {
	object-position: center 1.14rem;
}

接下来在视频之后添加其他页面元素,常规做法是限制视频区域高度,再进行后面的布局。但由于video元素本身在全屏状态下的宽高必须设成满屏,所以只能通过它的父元素(div.player)限制它的占位:

.player {
	height: 4.22rem;
}
.fullscreen .player {
	/* 全屏状态下重设高度,勿忘 */
	height: 5.36rem; /* 4.22 + 1.14 */
}
.video {
	width: 100%;
	height: 100%;
}
.main {
	box-sizing: border-box;
	padding: 0.3rem;
	height: 5rem;
	background: #fff;
}
<header id="header" class="header">...</header>
<div class="player">...</div>
<div id="main" class="main">这里是其他内容</div>

而div.main本身是不需要做任何特殊处理的。然而,此时又出现了一个新问题:进入全屏状态后,视频控制栏不见了。原因是,video元素的高度为屏幕高度,控制栏位于屏幕最底端,而div.player又限制了高度,导致video元素超出的区域被隐藏,自然就看不到控制栏了。幸好,我们还可以通过伪元素选择器修改控制栏的样式:

.fullscreen .player {
	position: relative;
	height: 5.36rem;
}
.fullscreen .video::-webkit-media-controls {
	position: absolute;
	bottom: 0;
}

通过使控制栏相对于div.player定位,就可以让它回到视频画面的底端了。最终效果如下图所示:

最终效果

综上所述,在全屏状态下,video元素之前的元素需要做布局调整,而在其后的元素则不需要

页面滚动

如果页面有滚动条,进入全屏状态后,是否还可以滚动呢?

笔者撰写本文第一版(2017年中)的时候,全屏状态下的页面滚动会变成抖动,效果非常糟糕,而目前则是滚不了了。

所以,如果页面内容确实较多,只能使用元素内滚动了

控制栏的坑

不得不说,原生控制栏的bug非常严重。

Bug 1:播放某些格式的视频时,进度条会出现错乱,即使退出全屏模式也还是错乱。

进度条错乱

Bug 2:控制栏的全屏按钮在某些情况(具体规律尚未查明)下无效。

Bug 3:整个控制栏在某些情况(具体规律尚未查明)下无法调出。

以上三个bug非必现。但为了躲开这些坑,建议屏蔽原生控制栏,自行开发一个控制栏。

视频全屏的实现

上一节提到,原生控制栏的全屏按钮在某些情况下无效,这样一来就必须想其他方法去实现视频的全屏了,这里面最关键就是video元素的 x5-video-orientation 属性。它决定了同层播放器进入全屏状态后,当前窗口是横屏还是竖屏(前文的所有描述中,都是竖屏的情况)。并且,它是可以动态设置的。

如果把 x5-video-orientation 设成横屏,再把页面上的其他元素隐藏掉,就跟全屏无异了。具体实现如下:

var isLandscape;

// 点击其他内容区域,进入全屏
var main = document.getElementById('main');
main.addEventListener('click', function() {
	// 同层播放器进入全屏状态之后,才能让视频全屏
	if (!isFullScreen) { return; }
	// 修改 x5-video-orientation
	player.setAttribute('x5-video-orientation', 'landscape');

	isLandscape = true;
}, false);

// 检测窗口方向改变,修改布局
window.addEventListener('orientationchange', function() {
	if (isLandscape) {
		document.body.classList.add('landscape');
		var width = window.screen.width;
		var height = window.screen.height;
		player.style.width = width + 'px';
		player.style.height = height + 'px';
	}
}, false);
.landscape .header { display: none; }
.landscape .video { object-position: center center; }
.landscape .main { display: none; }

要强调的是,从修改 x5-video-orientation 到窗口方向改变,需要一定的时间才能完成。因此,不要在修改之后马上调整布局,而是要监听 window 的 orientationchange 事件,在事件回调中调整布局。此外,还要在退出全屏时,清空相关状态:

player.addEventListener('x5videoexitfullscreen', function() {
	isFullScreen = false;
	isLandscape = false;
    document.body.classList.remove('fullscreen', 'landscape');
	player.style.width = player.style.height = '';
}, false);

最终效果如下(顶部的阴影和两个按钮仍然无法干掉):

视频全屏效果

后记

本文第一版写于2017年6月,当时刚接触同层播放器,所以文章内容只能算是一份试用报告。经过一年多的项目实践之后,有些旧问题找到了更好的解决方案,还发现并解决了一些新问题,而同层播放器本身也有一些变化,于是在2018年11月进行修订,发布第二版。

评论 (12条)

发表评论

(必填)

(选填,不公开)

(选填,不公开)

(必填)