总结手机视频播放的误区(不时更新)
以下为自己亲身踩过的坑,希望对你有帮助~
1、x5同层播放器
移动端浏览器中的video元素是比较特别的,早期无论是在iOS还是Android的浏览器中,它都位于页面的最顶层,无法被遮盖。后来这个问题在iOS下得到了解决,但是Android的浏览器则问题依旧。X5是腾讯基于Webkit开发的渲染引擎,它提供了一种名叫「同层播放器」的特殊video元素以解决遮盖问题。
只要给普通的video元素加上X5的自定义属性 x5-video-player-type,就可以调用同层播放器。
<div class="player">
<video id="video" class="video" controls="controls" playsinline x5-video-player-type="h5">
<source src="video.mp4" />
</video>
</div>
同层播放器的视频样式和ios的视频播放器是一样的。安卓的原生播放器(非同层播放器)无法控制是否自动播放、点击播放时是否自动全屏。
如果你需要安卓的视频样式和ios表现为一样,可以使用腾讯的H5同层播放器接入规范。
同层页面内播放是标准的视频播放形态,在video标签中添加x5-video-player-type:h5-page属性来控制网页内部同层播放,可以在视频上方显示html元素。
坑点注意:
使用x5的tbs同层播放器,在tbs版本4.5.2以下(这个是经过测试了10几台安卓手机得出来的),视频的右上方会出现一个无法去除的全屏按钮,如图:
想要去掉这个按钮,要通过css把视频区域挤出屏幕外,在通过object-opsition把视频定位回正常的位置,以下video-player__main__tech为video元素:
&.x5-android {
overflow: hidden;
& {
.video-player__main {
padding-left: 200px;
width: calc(100% + 200px);
.video-player__main__tech {
position: absolute;
width: calc(100% - 200px) !important;
right: 0;
object-position: calc(50% - 200px) 50% !important;
}
}
}
}
必须注意的是,x5下tbs版本在4.5.2以上是不需要做样式适配的,否则会造成视频画面缺失,因此使用前要先做判断:
isAndroidX5SupportInlinePlayer() {
const ua = window.navigator.userAgent;
const tbs = ua.match(/TBS\/([\d.]+)/);
const x5 = {
tbsWx: ua.match(/MicroMessenger/i) == 'micromessenger' && tbs && tbs[1] >= 36849,
// tbsQQ: browser.versions.QQ && isTBS && (isTBS[1]>= 36855) && (isTBS[1] <= 45200), // qq 内置 webview
MQQBrowser: ua.match(/MQQBrowser\/([\d.]+)/) && ua.match(/MQQBrowser\/([\d.]+)/)[1] >= 7.2, // qq 浏览器 或者 qq 内置 webview
tbsX5: tbs && tbs[1] >= 36900 && (isTBS[1] <= 45200)// 第三方接入 TBS x5 内核,如孕管安卓。注意:安卓 QQ 浏览器的 UA 没有 TBS(安卓 QQ 内置 webview 的 UA 有 TBS)
};
return (
(ua.indexOf('Android') > -1 || ua.indexOf('Linux') > -1) &&
(x5.tbsWx || x5.tbsQQ || x5.MQQBrowser || x5.tbsX5)
);
},
2、autoplay自动播放
video标签可以设置自动播放,只需在标签设置autoplay即可。但是,设置自动播放是会有兼容性问题的,并不是所有机型都可以。
1.我所遇到的,设置了autoply ios基本可以实现自动播放,但是要设置静音,即:没音频轨道,或者设置了muted属性。
2.安卓的话,只有部分机型可以自动播放。而且不能模拟自动播放,一定要有用户行为才可以触发播放。
微信自动播放
微信自动播放实在是太多坑了T_T,具体表现为
1、ios在用户无交互时触发play()方法会报错,导致视频无法加载。 解决方法:
document.addEventListener(
"WeixinJSBridgeReady",
() => {
this.video.play();
},
false
); // 兼容微信自动播放
但是这个方法也有一个问题,关乎于WeixinJSBridge的注入时机,有时候快有时候慢。因此如果项目是像vue这种单页应用,需要额外加载js生成dom元素,才能定义以上方法,会导致定义的WeixinJSBridgeReady不一定每次都可以触发,所以不一定每次自动播放都能成功。
2、安卓里面,如果设置了自动播放,视频会先播放(但是currentTime没有变,视频在加载,因此并没有播放进度),视频加载完会,就会自动暂停。如果封面图是自己实现的,不是使用video的,就会给用户一种不正常的表现(因为用户的体验是,视频并没有进行播放,封面图却消失了)。我的解决方法:
- 监听视频的pause事件
- 如果视频的currentTime为false(0或者NaN),是安卓并且是微信,则恢复封面图的显示。
onPause: function() {
self.status = "暂停";
self.isPlaying = false;
if(!self.currentTime && !util.isIOS() && util.isWeixin()) {
// 微信下安卓设置了自动播放,还没播放就会自动暂停,然后封面图就会消失了,会给人一种不正常的感觉,
// 因此用这种方法恢复封面图
self.isPlayed = false;
}else{
self._fixShowControl();
}
},
3、视频行内播放
默认情况下,点击video播放会全屏播放,如果想要视频在局部内可以播放的话,可以设置:x5-playsinline
坑点注意:
如果想播放时默认全屏播放,在设置了x5-video-player-type=h5这个属性,即使存在x5-video-player-fullscree=true,安卓也是是不能默认全屏播放的。解决方法:在点击播放视频的事件那里添加视频全屏的操作
4、视频全屏播放后的大小
这个情况只针对安卓的同层播放器播放时全屏播放的情况。在同层播放器全屏播放后,视频底色会变成黑色,然后视频只是在中间居中,大小是原来视频设置的大小,并不是会全屏铺满。
效果如图。
1、我第一次采用的方案是,当视频全屏时,会触发onresize方法,在该方法里面强制把视频的大小设置为屏幕的宽高:
let video = this.$refs.video;
// 以下代码是为了解决安卓播放无办法自动全屏
this.myPlayer.on('play',() => {
console.log('play')
window.onresize = function () {
document.querySelector('.video-container').style.width = window.innerWidth + "px";
document.querySelector('.video-container').style.height = document.documentElement.clientHeight + "px";
}
})
this.myPlayer.on('pause',() => {
console.log('pause')
window.onresize = function () {
document.querySelector('.video-container').style.width = "270px";
document.querySelector('.video-container').style.height = "170px";
}
})
但是这种方法,由于是整个视频的尺寸直接设置为当前屏幕的宽高,因此测试反映说视频被拉伸变形了,因为尺寸不是按照比例的。
2、因此,采取以下方案。videoHeight()和videoWidth()分别获取原视频的高和宽,然后与屏幕的宽高计算出比例。
if (MJSSDK.UA.android) {
this.myPlayer.on('play', () => {
// console.log('play');
window.onresize = () => {
// console.log('onresize-play');
this.isfull = true;
let vheight = this.myPlayer.videoHeight();
let vweight = this.myPlayer.videoWidth();
let clientHeight = document.documentElement.clientHeight;
document.querySelector('.video-container').style.width = (clientHeight * vweight) / vheight + 'px';
document.querySelector('.video-container').style.height = clientHeight + 'px';
document.querySelector('#my-video').style.backgroundColor = 'black';
};
});
this.myPlayer.on('pause', () => {
// console.log('pause');
window.onresize = () => {
// console.log('onresize-pause');
this.isfull = false;
// 全屏后,华为等部分机型会有白边,是页面的颜色,用该值控制背景色
document.querySelector('.video-container').style.width = '270px';
document.querySelector('.video-container').style.height = '170px';
};
});
}
},
5、视频全屏后出现白边
安卓启用同层播放器后全屏出现的,这是个很诡异的问题,仅在部分的安卓机型下出现。如图:
经过排查,该白边是页面的颜色,就是同层播放器的全屏是把这个页面旋转过来,然后区域放大这样。 解决方法:全屏时把页面背景色设置为黑色,取消全屏时改回来。
6、全屏方法调用问题
由于视频播放器的控制条是自己实现的,所以需要实现自己全屏方法。
1、原生全屏
原生全屏一般会使用这种方法:
if (video.requestFullscreen) {
video.requestFullscreen();
} else if (video.mozRequestFullScreen) {
video.mozRequestFullScreen();
} else if (video.webkitRequestFullscreen) {
video.webkitRequestFullscreen();
} else if (video.webkitSupportsFullscreen) {
video.webkitEnterFullscreen();
} else if (video.msRequestFullscreen) {
video.msRequestFullscreen();
} else {
this.addClass(el, "video-player--is-cssfullscreen");
}
后来看了videojs的源码,发现可以用screenfull.js这个库直接实现全屏,非常方便。
import screenfull from "screenfull";
if (screenfull.isEnabled) {
screenfull.request(video);
}
但是,在ios上面调试,发现screenfull.request(video)无法实现全屏,而且也无法监听得到全屏变化事件。只有webkitEnterFullscreen()才可以在ios实现全屏
因此,为了兼容ios:
if (util.isIOS()) {
document.getElementById(this.vId).webkitEnterFullscreen();
return;
}
2、伪全屏
这里的伪全屏是指通过css实现的样式全屏,可以实现自定义html元素覆盖在视频上。
1、竖版全屏
需要全屏时,给视频添加如下样式:
.video-player--is-cssfullscreen {
position: fixed !important;
left: 0 !important;
top: 0 !important;
width: 100% !important;
height: 100% !important;
z-index: $z-index-video !important;
}
2、横版全屏
点击全屏时,先把video元素的最外层父容器旋转90度,并设置为fixed,固定窗口。为了使视频能够居中,left设置为50%,top为当前文档宽度的一半,我这里为-375px。
.cross-screen {
position: fixed;
top: -375px;
left: 50%;
background: #000;
transform-origin: 0;
transform: rotate(90deg) translate3d(0, 0, 0);
}
video的上一层父元素的宽设置为窗口的高,高则设置为当前窗口的宽:
this.crossScreenStyle = {
width: window.innerHeight + "px",
height: window.innerWidth + "px",
};
坑点注意:
如果同时设置了position:fixed 以及transform属性,可能会导致z-index层级表现异常,层级高的元素被层级低的覆盖了。解决方法:层级高的元素额外添加属性transform: translateZ(100px),把元素提升上来
7、video 标签被浏览器劫持接管
现象:
强制出现controls控制条
强制全屏
强制画中画
强制浮动播放器且浮动有bug
等等。。。
这里是指即使使用了x5同层播放器,还是会出现以上现象。
我目前是在vivo的某台手机复现过。
解决方案
浏览器厂商添加域名白名单。如 uc ios 白名单的域名有 *.v.qq.com *.taobao.com
8、动态插入video标签的问题
为了提升页面渲染性能,会考虑动态插入插入video标签,例如点击了播放才把video标签插入到对应容器里面。
但是,如果启用了x5同层播放器,动态插入的video是会被浏览器劫持的。
解决方案
1、可以使用第7点的解决方案
2、判断是x5内核下,则不使用动态插入的方式
推荐阅读