最近在做的仿网易云播放器项目中,视频界面有个需求:当滚动条滚动到主视频界面离开可视窗口时,出现小窗继续播放。
HTML5 video 标签的画中画功能只能实现用户点击才会出现,滚动事件不能触发画中画
所以只能自己手写视频小窗播放
html 代码如下(css样式就不贴了):
<!-- 主视频 -->
<div class="video">
<video
:src="url" data 中的数据
controls
ref="video"
class="player"
:poster="detail.cover || detail.coverUrl"
@play="isVideoPlay = true" 记录主视频的播放状态,这里也可以不用记录,后面
@pause="isVideoPlay = false" 通过videoElement.paused 判断
></video>
</div>
<!-- 小窗视频 -->
<div class="miniVideo" v-show="showMiniVideo">
<video
:src="url"
ref="miniVideo"
class="player"
:poster="detail.cover || detail.coverUrl"
></video>
<!-- 遮罩,参考b站小窗 -->
<div class="mask">
<i
class="iconfont"
:class="{
'icon-icon_play': !isMiniVideoPlay, 通过miniVideo的播放状态显示不同的按钮,如:播放,暂停
'icon-zanting2': isMiniVideoPlay,
}"
@click="miniVideoChangePlay(!isMiniVideoPlay)" 改变播放状态函数
></i>
</div>
</div>
利用 Intersection Observer API 实现
vue代码如下(我写在 mounted 生命周期函数中):
// 滚动 出现小窗播放
let video = this.$refs.video;
let miniVideo = this.$refs.miniVideo;
let videoOnce = false; // 是否执行过一次
let miniVideoOnce = false;
// 网页支持播放视频时
video.oncanplay = () => {
let observer = new IntersectionObserver((entries) => {
// 视频窗口出现在可视窗口时
if (entries[0].isIntersecting) {
if (!videoOnce) {
// 隐藏小窗
this.showMiniVideo = false;
// 同步播放时间
video.currentTime = miniVideo.currentTime;
// 同步播放状态
this.isVideoPlay = this.isMiniVideoPlay;
this.isVideoPlay ? video.play() : video.pause();
// 暂停小窗
this.isMiniVideoPlay = false;
miniVideo.pause();
// 因为会出现执行两次的情况,所有用这个变量来判断是否执行过一次
// 执行过一次后要在执行else里的动作才会解除执行过一次的状态
// 避免出现视频已暂停还执行暂停而报错,或其他问题
videoOnce = true;
miniVideoOnce = false;
}
} else {
if (!miniVideoOnce) {
this.showMiniVideo = true;
miniVideo.currentTime = video.currentTime;
this.isMiniVideoPlay = this.isVideoPlay;
this.isVideoPlay = false;
this.isMiniVideoPlay ? miniVideo.play() : miniVideo.pause();
video.pause();
miniVideoOnce = true;
videoOnce = false;
}
}
});
observer.observe(video);
};
有个缺点就是 miniVideo 隐藏或显示时,会有一瞬间的卡顿。