背景:
1、功能:类似weibo的动态列表,video滚动到某区域自动播放,滚出自动暂停
2、运行环境:app内,微信浏览器,普通浏览器
一、选取video库
video兼容性太难搞了,直接选了dplayer,还可以支持弹幕功能,还算好用吧,发现它的一个问题后面会讲到。
二、自动播放/暂停
整体思路
每个video组件有一个视频地址,将该地址(currentUrl)使用自定义指令传入IntersectionObserver的observer去监听,如果发现该视频组件在指定区域,就将video组件内的curSrc赋值。
video组件内监听curSrc,如果当前需要播放的curSrc和本组件内的视频地址(currentUrl)相等,就证明当前组件的video需要播放。
1、使用了IntersectionObserver这个API,性能比监听scroll好,封装成自定义指令也很方便。
import Vue from 'vue';
import 'intersection-observer';
// 设置合适的rootMargin,否则如果多个视频满足intersectionRatio===1,会造成页面卡死
// 节流
IntersectionObserver.prototype.POLL_INTERVAL = 200;
Vue.directive('autoplay', {
bind(el, binding, vnode) {
const autoPlay = binding.value;
const {autoArea, currentUrl} = binding.arg;
let rootMargin = '0px 0px 0px 0px';
if (autoPlay) {
rootMargin = autoArea ? autoArea : '0px 0px 0px 0px';
}
const _observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
console.log('entry.intersectionRatio ', entry.intersectionRatio, vnode.context.curSrc);
if (entry.intersectionRatio === 1 && autoPlay) {
vnode.context.curSrc = currentUrl;
}
else if (entry.intersectionRatio !== 1 ) {
vnode.context.curSrc = '';
}
});
}, {
root: null, // 所监听对象的具体祖先元素,未传入值或值为`null`,则默认使用顶级文档的视窗。
rootMargin: rootMargin, // 计算交叉时添加到根(root)边界盒bounding box的矩形偏移量, 可以有效的缩小或扩大根的判定范围从而满足计算需要
threshold: 1, // 一个包含阈值的列表, 按升序排列, 列表中的每个阈值都是监听对象的交叉区域与边界区域的比率。
});
if (_observer) {
_observer.observe(el);
}
},
})
2、之后封装好的video组件,挂上这个自定义指令,传入视频区域参数rootMargin,和该组件的视频地址currentUrl。
<template>
<div v-autoplay:[{currentUrl,autoArea}]="needAutoplay">
<d-player
:ref="uniqueRef + 'dplayer'"
:options="options"
@play="play"
@timeupdate="timeupdate"
@pause="onEndOrPause"
@ended="onEndOrPause"
@loadedmetadata="loadedmetadata"
></d-player>
</div>
</template>
<script>
watch: {
curSrc(val) {
if (val === this.currentUrl && !this.isPlaying && this.clientOs) {
this.curPlayer.switchVideo({
url: this.currentUrl,
pic: this.videoInfo.coverUrl,
});
this.curPlayer.play();
this.isPlaying = true;
}
else if (!val) {
this.$emit('onShowCover');
this.curPlayer.switchVideo({
url: '',
pic: this.videoInfo.coverUrl,
});
this.isPlaying = false;
this.hideCover = false;
}
}
},
mounted() {
this.videoStyle = this.videoInfo.videoStyle || 0;
this.coverUrl = this.videoInfo.coverUrl;
this.initPlayer();
},
methods: {
initPlayer() {
this.curPlayer = this.$refs[`${this.uniqueRef}dplayer`].dp;
this.curPlayer.container.classList.add('dplayer-hide-controller');
this.curPlayer.container.style.height= '100%';
if (this.isAndroid) {
this.curPlayer.video.setAttribute('x5-video-player-type', 'h5-page')
this.curPlayer.video.removeAttribute('playsinline')
this.curPlayer.video.removeAttribute('webkit-playsinline')
}
},
}
三、遇到的问题
1、h5内 自动播放
ios内都不行。安卓内,需要用户有点击行为(任意位置的)之后才能自动播放。
2、app内 自动播放:需要app伙伴协助
将webview的一个手势参数修改之后,app内ios和安卓都可以自动播放了。
详见:这里
3、微信内 自动播放
据说可以利用weixinjsBridege的加载时机,此时拿到video实例,可以调用play使它播放,此条没试。
4、封面问题
video的poster有兼容性问题,在某些手机上无法显示,或者暂停时会黑屏。
解决办法:自己盖一个假的封面。
5、其他
如果用户滑过多个视频,虽然当前只有一个视频在播放,其余视频都暂停了,但是其他视频也都处于下载中,非常影响页面使用体验,甚至APP卡死。
解决办法:自动暂停视频之后,展示封面,将它的src赋为空。手动暂停的视频则不做此处理。
6、在安卓微信中会被native播放器接管过去
解决办法:戳X5内核视频之问答汇总
if (this.isAndroid) {
this.curPlayer.video.setAttribute('x5-video-player-type', 'h5-page')
this.curPlayer.video.removeAttribute('playsinline')
this.curPlayer.video.removeAttribute('webkit-playsinline')
}
7、视频全屏后样式问题
在浏览器内会被系统浏览器接管,样式无法控制。
解决办法:在app内,可以全屏调用app的全屏能力,app伙伴自定义样式。
8、视频全屏后返回,页面不能回到原位置
部分机型的兼容问题
dplayer中是有对这个问题做处理的:全屏前记住scroll的高度,退出全屏后滚动到指定高度。但是在某些机型下,document.documentElement.scrollTop = x
应该改成document.body.scrollTop = x ;