当前位置: 首页 > 编程日记 > 正文

X5同层播放器应用实践

移动端浏览器中的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>
复制代码

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

调整位置

在全屏状态下,调整视频位置的通用做法是:把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 属性后,问题就解决了(上方右图):

<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-orientationplayer.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月进行修订,发布第二版。

本文同时发布于作者个人博客: mrluo.life/article/det…

转载于:https://juejin.im/post/5be553a7e51d450e6f661d67

相关文章:

1007 Maximum Subsequence Sum(两种思路)

1.解法1 思路 对于动态规划来说&#xff0c;最关键的就是找到状态转移方程&#xff0c;本题设置一个前向数组&#xff0c;元素predp[i]表示的是以元素i结尾的连续数列和的最大值&#xff0c;转移方程是predp[i] max(predp[i-1]a[i],a[i])。要做的事就是完成这个dp数组&#x…

C#学习-EF在三层中使用

1.搭建普通三层 DAL层&#xff0c;BLL层&#xff0c;Model层&#xff0c;Web层&#xff1b; DAL层引用Model层 BLL层引用DAL层和Model层 Web层引用BLL层和Model层 2.实现EF三层的搭建&#xff08;添加引用&#xff0c;修改配置信息&#xff09; 2.1添加EF对象 在Model中添加一个…

各大IT公司笔试真题汇总开发人员一定要加入收藏夹的网站(收藏)

巨人网络java笔试基础题分享 http://www.coderarea.net/bbs/read.php?tid834 百度笔试题 http://www.coderarea.net/bbs/read.php?tid811 百度2010校招运维部门笔试 http://www.coderarea.net/bbs/read.php?tid779 百度2010年校园招聘软件测试笔试题 http://www.coderarea.n…

Python编写Hive UDF

2019独角兽企业重金招聘Python工程师标准>>> 1. 目的 从string类型的字段中解析并汇总每种category类型的总amount 2. 素材 表名&#xff1a;test_table order_no hotel_seq discount_detail D8662EF4E 10212527 NULL 45C024849 …

1045 Favorite Color Stripe(LIS解法)

解题思路 本题属于Longest Increasing Sequence最长不下降子序列&#xff0c;但是要注意&#xff0c;LIS当中不会有无效的元素&#xff0c;而本题是有的&#xff0c;所以先要把无效元素过滤掉&#xff0c;才能转化成为LIS问题。 这里用到了hashTable(用map更慢)&#xff0c;初…

5.8fork父子进程

实验4-2&#xff1a;fork父子进程 实验目的&#xff1a; 理解fork创建子进程的本质 实验要求&#xff1a; 1、按如下要求编写程序: &#xff08;1&#xff09;、打开一个有内容的文件; &#xff08;2&#xff09;、调用fork创建子进程; &#xff08;3&#xff09;、读文件…

word表格自动编号

选中全部内容--右键--项目符号和编号--自定义--编号样式--选01,02样式.则生成所有编号选中第二批编号,--重新编号,就又从01开始了转载于:https://www.cnblogs.com/wzshhynk/archive/2009/12/30/1635805.html

Vue API(directives) 自定义指令

前言&#xff1a;除了vue的内置指令以外&#xff0c;我们可以定义自定义指令。内置指令表相见&#xff1a;https://www.cnblogs.com/ilovexiaoming/p/6840383.html 我们定义一个最简单的 <script> export default {name: App,data(){return{yanse:red}},// 所有自定义指令…

1045 Favorite Color Stripe(LCS解法) 需再理解

解题思路 使用LCS方法解这一题&#xff0c;首先要把现有的颜色和Eva的颜色看成即将取材的序列s和e。 而且注意2个序列都要从1开始读入&#xff0c;因为递归边界&#xff08;待后叙&#xff09;。 dp[i][j]代表的是e[1]-e[i]和s[1]-s[j]的范围内&#xff0c;公共子序列的长度…

C++ primer第五版随笔--2015年1月6日

记录自己看这本书时的一些内容。 一、引用&#xff08;reference&#xff09; 引用为对象起了另外一个名字。例如&#xff1a; int ival1024&#xff1b; int &relVal1ival;//对&#xff0c;注意尽量不要用这方式&#xff1a;int& relvalival&#xff1b; int &rel…

Python性能分析指南——中

程序使用了多少内存&#xff1f;现在我们对计时有了较好的理解&#xff0c;那么让我们继续弄清楚程序使用了多少内存。我们很幸运&#xff0c;Fabian Pedregosa模仿Robert Kern的line_profiler实现了一个不错的内存分析器。首先使用pip安装:这里建议安装psutil包&#xff0c;因…

1040 Longest Symmetric String 需再做

解题思路 本题属于最长回文子串专题下。与之前的LIS和LCS的动规有两个较大的不同 1. 虽然最后也是要求长度&#xff0c;但是长度信息不再蕴含在dp数组当中&#xff0c;dp[i][j]表示的仅仅是从s[i]起s[j]止这一段是否是回文&#xff0c;所以为了提醒自己&#xff0c;我设置成了…

回顾2009,展望2010。

回顾2009&#xff0c;展望2010。 2009即将过去&#xff0c;总结2009年&#xff0c;计划2010年。 2009年12月31日。转载于:https://www.cnblogs.com/finehappy/archive/2009/12/31/1654975.html

Linux Mint 19 安装Gnome Boxes 新建失败

之前在Ubuntu论坛提出&#xff0c;一直没有解决.http://forum.ubuntu.org.cn/viewtopic.php?f65&t488821 后参照&#xff1a; https://askubuntu.com/questions/836703/ ... sue/836715对方报错&#xff1a; (gnome-boxes:15984): Boxes-WARNING **: wizard.vala:463: Fai…

1057 Stack

目录 解题思路 AC代码 解题思路 虽然题目的名字是栈&#xff0c;但是这题和栈的关系很小&#xff0c;甚至我都没有用到stack这个数据结构&#xff0c;而是用vector<int>的pop_back()来模拟栈的弹出。 主要考察的是&#xff1a;在线查询&#xff0c;也就是查询过程中…

【译】使用自定义ViewHelper来简化Asp.net MVC view的开发------part1

从开发者的角度来看&#xff0c;创建Asp.net MVC的View是一件很爽的事&#xff0c;因为你可以精确控制最终生成的HTML。具有讽刺意味的是不得不写出每一行HTML代码同时也是Asp.net MVC的View中让人不爽的地方。让我用我的一个经历来告诉我创建ASP.Net MVC view Helpers背后灵感…

看书挑剔,只看经典

如何选择经典&#xff0c;可以到网上做做功课&#xff0c;看看评价&#xff0c;综合分析一下。书籍是我们知识的主要来源。在选择书籍的时候做足功课是对我们自己时间的负责&#xff1b;这和在超市里买东西时对比各个品牌是一个道理&#xff1b;只不过奇怪的是&#xff0c;我很…

0-1背包使用一维dp数组时为何v要从大到小枚举

样例数据 5 8 3 5 1 2 2 4 5 2 1 3 如若不然&#xff0c;也就是让v按照从小到大的顺序枚举&#xff0c;就会出现 注意高亮的那一行&#xff0c;第一件物品的重量只有3&#xff0c;怎么会得到6呢&#xff1f; 代码如下 #include<cstdio> #include<cmath> #inclu…

异步编程模型--使用 IAsyncResult 对象

先推荐阅读下面的资料&#xff1a;MSDN&#xff1a;异步编程设计模式IBM developerworks&#xff1a; 使用异步 I/O 大大提高应用程序的性能参考博文&#xff1a;1、正确使用异步操作 2、Lab&#xff1a;体会ASP.NET异步处理请求的效果 3、WCF中的异步调用 4、WCF从理论到实践(…

对XX证券报关于物联网操作系统的几个问题的答复

XX证券报提问了几个关于物联网和物联网操作系统的问题&#xff0c;个人表达了一些粗陋的观点&#xff0c;在这里发表出来&#xff0c;与行业朋友交流和探讨。物联网行业最需要解决的问题是什么&#xff1f;虽然物联网这个行业被炒得比较热&#xff0c;但是截至目前&#xff0c;…

Java基础 - 面向对象 - 构造方法

在类中除了成员方法之外&#xff0c;还存在一种特殊类型的方法&#xff0c;那就是构造方法。构造方法是一个与类同名的方法&#xff0c;对象的创建就是通过构造方法完成的。每当类实例化一个对象时&#xff0c;类都会自动调用构造方法。 构造方法的特点&#xff1a; 构造方法没…

1105 Spiral Matrix 给定数组向螺旋矩阵中填入数据

两个测试用例超时&#xff0c;可直接跳转到 目录 超时点1 超时点2 ​​​​​​​ 要做的事情是&#xff0c;将数组按照非升序/降序&#xff0c;顺时针从外围到内部一圈一圈地把数据填到矩阵中&#xff0c;并打印出来。也就是将数组排好序后&#xff0c;将矩阵的坐标和数组…

一晚上就能让你小腹变小的方法 - 健康程序员,至尚生活!

仅一晚上针对小腹的锻炼就会让它明显收紧&#xff0c;很不可思议吧&#xff1f;但它确实发生了。形体教练向我们推荐&#xff1a;做30次转身运动(双手抱在脑后站立&#xff0c;迅速分别向左右两侧依次扭转上肢&#xff0c;注意不要以膝盖为轴&#xff0c;使运动轴心保持在骨盆以…

Alpha 冲刺 (2/10)

前言 队名&#xff1a;拖鞋旅游队组长博客&#xff1a;https://www.cnblogs.com/Sulumer/p/9960487.html作业博客&#xff1a;https://edu.cnblogs.com/campus/fzu/Grade2016SE/homework/2365组内情况 燃尽图任务分布github签入记录苏路明&#xff08;组长&#xff09;过去两天…

互联网对erp行业到底有什么影响

1 财务管理的影响 总账、 应收应付、资金计划&#xff0c;支付管理。 生产计划的影响。 重大的疑惑。 转载于:https://www.cnblogs.com/sdgxbooy/p/8892655.html

PAT甲级排队问题合集 (持续更新中)

已加入的习题 A1014,A1017 问题1和2共性 1. 都是排队问题 2. 都有一条黄线 3. 都需要找到最先离开人的队伍 4. 都有着服务时间段限制(迟于某个时间点来不予受理) 问题1&#xff1a;1014 Waiting in Line 问题链接&#xff1a;1014 Waiting in Line 这一题&#xff0c;…

第三章:创建用户界面组件--可视化组件(一)

1.可视化组件 1.1关于可视化组件 可视化组件的特征包括&#xff1a;size(大小&#xff09;、事件、样式、皮肤、行为。 行为&#xff1a;当组件被触发时&#xff0c;视觉&#xff0c;音乐效果的变化。 1.1 .1Spark and Halo 组件 Spark是flex 4中新加的组件。halo仍旧继承了以…

Rust 1.30带来更多元编程支持,并改进了模块系统

Rust的最新版本1.30扩展了过程宏&#xff0c;允许它们定义新的属性和类似于函数的宏。此外&#xff0c;它简化了Rust模块系统&#xff0c;使其更加一致、直观。 Rust 1.30引入了两种新类型的过程宏&#xff0c;“类属性的过程宏”和“类函数的过程宏”。过程宏是Rust元编程的基…

两种最大堆建堆方式

都是用完全二叉树的静态存储方式&#xff0c;下标从1开始。 No.1 先按照数组的次序填入完全二叉树&#xff0c;再从倒数第一个非叶子节点开始&#xff0c;一个个地看是不是要向下调整&#xff0c;一直下调到不能再调。 void downAdjust(int low,int high){int i low;int j …

汉字验证码和算式验证码

大家知道简单数字或者字母验证码很容易被破解&#xff0c;但是算式验证码或者中文汉字验证码不容易被破解&#xff0c; 所以建议大家在使用验证码的时候&#xff0c;尽量用算式验证码或者中文汉字验证码。 下面是我写的两种验证码代码&#xff0c;有用到的朋友可以参考下&#…