对于这个问题,其实网上已经有一些比较好的实践,但有时候并不明白为何这样配置;若是你想要知道一个弱小,可怜又无助的
<video>
如何变成丰满健壮的视频播放器,那就往下看吧!css
来问H5 医生端(PC & 微信)问题详情页面:html
<video>
标签进行视频播放src
属性poster
属性,值为后端根据视频第一帧生成的封面地址<video>
标签使用 <div>
容器包裹,并设置 display: none
隐藏,用户点击封面时调用 video
的 play()
方法进行播放<video>
元素后微信中调用play()
无反应display: none;
或者 width: 0; height: 0;
方式隐藏视频时,元素处于未激活状态,不响应 play()
方法,因此咱们设置宽高为1px
;play()
方法会触发播放事件,但播放器此时并未打开,而全屏播放中暂停视频并不会退出播放器。因此咱们能够在视频暂停时取消 loading);video
标签处播放,致使有声音无图像;iPad 中经过修改 PC 的判断条件能够解决此时再使用原生 video 标签事件和属性,已经没办法进一步突破以上的这些坑,解决千差万别的兼容性问题了。所以,咱们参考了其余的方案实现了预期的效果。vue
PS:项目是基于Vue & scss,但该功能能够不依赖这些实现web
webkit-playsinline, playsinline, x-webkit-airplay, x5-video-player-type, x5-video-player-fullscreen, x5-video-orientation
等解决不一样类型设备的播放差别;<!-- 视频容器 -->
<div class="video" :class="{'full-screen': isFullScreen}">
<!-- 视频主体 -->
<div class="video-content">
<video :controls="isFullScreen" :style="isFullScreen ? {} : img.style" :class="img.isVertical ? 'vertical-img' : 'horizontal-img'" :src="img.url" preload="metadata" :poster="img.preview_pic_url" :ref="`video${img.id}`" webkit-playsinline="true" playsinline="true" x-webkit-airplay="allow" x5-video-player-type="h5" x5-video-player-fullscreen="true" x5-video-orientation="portraint">
抱歉,您的浏览器不支持内嵌视频!
</video>
</div>
<!-- 遮罩层,显示播放按钮;仅在待播放状态显示 -->
<div v-if="!isFullScreen" class="video-mask">
<div>
<img src="~images/play.png" />
</div>
</div>
<!-- 全屏控制按钮;仅在非安卓高版本内核中显示 -->
<div v-else-if="!inHighTBS" class="video-controls">
<span class="video-controls-close" @click.stop="handleVideoControls('close')">
×
</span>
</div>
</div>
复制代码
属性说明windows
controls
: 经过 flag 设置仅在播放时出现,避免初始播放状态不一样style
:对待播放 dom 进行绝对定位计算视频偏移量;视频显示区为正方形窗口,所以要横向及纵向视频显示区都在正中间class
:设定横向视频高度或纵向视频宽度src
:视频源preload
:值为预加载但不阻塞;每一个问题最多仅有一个视频,保证用户点击播放后当即响应,而且不阻塞其余图片附件的渲染poster
:封面地址ref
:在vue
中获取并操做 video
元素webkit-playsinline
:IOS 10中设置有效,视频播放时局域播放,不脱离文档流;能够保证播放界面与PC端一致playsinline
:IOS 微信浏览器支持小窗内播放,和上一个属性一块儿食用可兼容几乎全部IOS设备x5-video-player-type
:启用H5同层播放器,是 wechat 安卓版特性x5-video-player-fullscreen
:视频播放时将会进入到全屏模式,若不设置仍是会新开播放器,但尺寸为原始视口大小(视频未播放前)x5-video-orientation
:控制横竖屏/* 外层还有其余已定位容器 */
.video {
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
transition: all 0.3s;
background-color: rgba(0, 0, 0, 0.5);
&.full-screen {
position: fixed;
z-index: 99;
.video-content {
width: 100%;
height: 100%;
video {
position: initial;
&.vertical-img {
height: 100%;
width: auto;
margin: 0 auto;
}
&.horizontal-img {
width: 100%;
height: auto;
max-height: 100%;
}
}
}
}
&-mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
background-color: rgba(0, 0, 0, 0.5);
div {
width: 100%;
text-align: center;
img {
width: 30%;
position: inherit;
}
}
}
&-controls {
position: absolute;
right: 5%;
top: 5%;
display: flex;
align-items: center;
z-index: 1;
&-close {
width: 50px;
height: 50px;
line-height: 50px;
color: rgba(255, 255, 255, 0.7);
background: rgba(0, 0, 0, 0.3);
text-align: center;
border-radius: 50%;
font-size: 2rem;
cursor: pointer;
transition: all 0.3s;
&:hover {
background: rgba(0, 0, 0, 0.5);
color: #fff;
}
}
}
&-content {
max-width: 768px; // 限制PC端不超过768px;如要PC全屏可不作设置
margin: 0 auto;
display: flex;
align-items: center;
video {
display: block;
position: absolute;
object-fit: fill;
}
}
}
.horizontal-img {
height: 100%;
top: 0;
}
.vertical-img {
width: 100%;
left: 0;
}
复制代码
在安卓微信中,就算加上了上面的属性,还会出现上下有黑边,不能全屏。解决给<video>
加上object-fit: fill;
的样式便可。后端
修改上线后之前报Bug的用户纷纷反馈好了,没问题,但...浏览器
有一个用户反馈使用锤子的坚果Pro点击视频无反应,找来同型号测试机一番骚操做后...愣是没复现!缓存
以后经过和用户不断沟通发现该设备上竟然未使用X5内核浏览器(使用微信打开debugtbs.qq.com
可调试X5内核,未安装会有提示)微信
所以在下面一个安卓兼容性事件判断上报错了,使用try { } catch (e) {}
包一下,一样能够正常播放,但这是的播放效果已没法统一。dom
// 高版本微信安卓环境下会自动加上返回按钮而且点击触发退出全屏事件
// 需作未使用X5内核容错处理
inHighTBS() {
if (inAndroid) {
try {
const [, currentTbsVersion] = window.navigator.userAgent.match(/TBS\/(\d+)/);
return currentTbsVersion > '036900';
} catch() {
return false;
}
} else {
return false;
}
}
// 安卓环境中会启用同层H5播放器,跳转新窗口,所以监听x5videoexitfullscreen事件可获取状态
// https://x5.tencent.com/tbs/guide/video.html
this.inHighTBS && vDom.addEventListener('x5videoexitfullscreen', () => {
this.isFullScreen = false;
});
复制代码
const mediaProperties = [
'loadstart', // 在媒体开始加载时触发。
'progress', // 告知媒体相关部分的下载进度时周期性地触发。有关媒体当前已下载总计的信息能够在元素的buffered属性中获取到。
'suspend', // 在媒体资源加载终止时触发,这多是由于下载已完成或由于其余缘由暂停。
'abort', // 在播放被终止时触发,例如, 当播放中的视频从新开始播放时会触发这个事件。
'error', // 在发生错误时触发。元素的error属性会包含更多信息。参阅Error handling得到详细信息。
'emptied', // 媒体被清空(初始化)时触发。
'stalled', // 在尝试获取媒体数据,但数据不可用时触发。
'loadedmetadata', // 媒体的元数据已经加载完毕,如今全部的属性包含了它们应有的有效信息。
'loadeddata', // 媒体的第一帧已经加载完毕。
'canplay', // 在媒体数据已经有足够的数据(至少播放数帧)可供播放时触发。这个事件对应CAN_PLAY的readyState。
'canplaythrough', // 在媒体的readyState变为CAN_PLAY_THROUGH时触发,代表媒体能够在保持当前的下载速度的状况下不被中断地播放完毕。注意:手动设置currentTime会使得firefox触发一次canplaythrough事件,其余浏览器或许不会如此。
'playing', // 在媒体开始播放时触发(不管是初次播放、在暂停后恢复、或是在结束后从新开始)。
'waiting', // 在一个待执行的操做(如回放)因等待另外一个操做(如跳跃或下载)被延迟时触发。
'seeking', // 在跳跃操做开始时触发。
'seeked', // 在跳跃操做完成时触发。
'ended', // 播放结束时触发。
'durationchange', // 元信息已载入或已改变,代表媒体的长度发生了改变。例如,在媒体已被加载足够的长度从而得知总长度时会触发这个事件。
'timeupdate', // 元素的currentTime属性表示的时间已经改变。
'play', // 在媒体回放被暂停后再次开始时触发。即,在一次暂停事件后恢复媒体回放。
'pause', // 播放暂停时触发。
'ratechange', // 在回放速率变化时触发。
'resize',
'volumechange', // 在音频音量改变时触发(既能够是volume属性改变,也能够是muted属性改变).。
'mozaudioavailable' // 当音频数据缓存并交给音频层处理时
];
mediaProperties.forEach(item => {
vDom.addEventListener(item, e => console.log(item));
});
复制代码
做者:丁香医生团队 顾重