前言

最近在做的仿网易云播放器项目中,视频界面有个需求:当滚动条滚动到主视频界面离开可视窗口时,出现小窗继续播放。
HTML5 video 标签的画中画功能只能实现用户点击才会出现,滚动事件不能触发画中画
所以只能自己手写视频小窗播放

在这里插入图片描述


一、实现原理

创建一个主视频元素(以下简称mainVideo),和一个用于小窗播放的视频元素(以下简称miniVideo)。miniVideo 可以用 fixed 固定定位到想要的位置,然后隐藏。两个 video 共用同一个 src 视频地址。待 mainVideo 随页面滚动要一定位置而离开可视区域后显示 miniVideo ,同步播放时间播放状态mainVideo 进入可视区后隐藏 miniVideo, 同步播放时间播放状态

二、实现过程

1.创建出所需的元素和数据

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>

2.监听 mainVideo

利用 Intersection Observer API 实现

API 参考1
API 参考2

video 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 隐藏或显示时,会有一瞬间的卡顿。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐