| | |
| | | <template> |
| | | <view class="video-container"> |
| | | <!-- 视频加载 --> |
| | | <zero-loading v-show="videoLoading" type="circle" color="#0ebd57" text=""></zero-loading> |
| | | <!-- 视频列表 --> |
| | | <swiper |
| | | class="video-swiper" |
| | | vertical |
| | | :current="currentIndex" |
| | | @change="onSwiperChange" |
| | | :duration="250" |
| | | easing-function="linear" |
| | | > |
| | | <swiper-item |
| | |
| | | <image src="/static/video/play.png" style="width: 45px;height: 45px" mode="aspectFit"></image> |
| | | </view> |
| | | <video |
| | | v-if="index >= currentIndex - videoLiveOffset && index <= currentIndex + videoLiveOffset" |
| | | :id="'video'+index" |
| | | :ref="'video'+index" |
| | | :src="item.videoUrl" |
| | | :autoplay="false" |
| | | :autoplay="index === currentIndex" |
| | | :controls="false" |
| | | :loop="true" |
| | | :object-fit="item.objectFit" |
| | | :enable-progress-gesture="false" |
| | | :show-center-play-btn="false" |
| | | class="video-item" |
| | | @play="onPlay(item.id, index)" |
| | | @pause="onPause(index)" |
| | |
| | | @click="togglePlay(index)" |
| | | @timeupdate="onTimeUpdate($event)" |
| | | @loadedmetadata="onLoadedMetadata($event)" |
| | | @waiting="videoWaiting(index)" |
| | | ></video> |
| | | <!-- 自定义控制条 --> |
| | | <view |
| | | @touchstart="handleTouchStart" |
| | | @touchmove="handleTouchMove" |
| | | @touchend="handleTouchEnd" |
| | | @touchstart.stop="handleTouchStart" |
| | | @touchmove.stop="handleTouchMove" |
| | | @touchend.stop="handleTouchEnd" |
| | | class="container"> |
| | | <!-- 进度条 - 整个区域可拖动 --> |
| | | <view class="process-warp" :style="{ opacity: showProcess ? 1 : 0 }"> |
| | |
| | | startPlayTime: 0 // 这个视频从什么时候开始播放的 |
| | | }, |
| | | currentVideoIsPlaying: true, // 当前视频是否正在播放 |
| | | isFullScreen: false, |
| | | windowHeight: 0, |
| | | currentIndex: 0, // 当前播放的视频索引 |
| | | videoLoading: false, // 视频缓冲中 |
| | | videoList: [], // 视频列表数据 |
| | | videoContexts: [], // 视频上下文对象集合 |
| | | videoBufferOffset: 0.1 ,// 视频预加载参数 |
| | | videoLiveOffset: 5, // 保留当前视频前后各多少个视频上下文 |
| | | videoLiveOffset: 2, // 保留当前视频前后各多少个视频上下文 |
| | | touchXY: { // 监听左滑右滑 |
| | | startX: 0, |
| | | endX: 0, |
| | |
| | | } |
| | | }) |
| | | }, |
| | | // 初始化视频上下文 |
| | | initVideoContexts() { |
| | | const start = Math.max(0, this.currentIndex - this.videoLiveOffset); |
| | | const end = Math.min(this.currentIndex + this.videoLiveOffset, this.videoList.length - 1); |
| | | let contextsLength = this.videoContexts.length; |
| | | if (contextsLength === 0) { |
| | | // 第一次初始化 |
| | | for (let i = 0; i < this.videoList.length; i++) { |
| | | if (i < start || i > end) { |
| | | this.videoContexts.push(null) |
| | | } else { |
| | | let videoContent = uni.createVideoContext(`video${i}`, this); |
| | | videoContent.seek(this.videoBufferOffset); |
| | | videoContent.pause(); |
| | | this.videoContexts.push(videoContent); |
| | | } |
| | | } |
| | | } else { |
| | | for (let i = 0; i < this.videoList.length; i++) { |
| | | contextsLength = this.videoContexts.length |
| | | if (contextsLength - 1 >= i) { |
| | | // 如果已经是null了就不用管,因为视频加载只会在后面push,前面已经设置为null则无需处理 |
| | | if (this.videoContexts[i] == null) { |
| | | continue |
| | | } |
| | | // 超出可视化范围的视频直接释放资源,并置为null |
| | | if (i < start || i > end) { |
| | | if (this.videoContexts[i]) { |
| | | this.videoContexts[i].stop(); |
| | | this.videoContexts[i] = null |
| | | } |
| | | } |
| | | } else { |
| | | if (i < start || i > end) { |
| | | this.videoContexts.push(null); |
| | | } else { |
| | | let videoContent = uni.createVideoContext(`video${i}`, this); |
| | | videoContent.seek(this.videoBufferOffset); |
| | | videoContent.pause(); |
| | | this.videoContexts.push(videoContent); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | // 将当前视频设置为播放 |
| | | if (this.videoContexts[this.currentIndex]) { |
| | | this.videoContexts[this.currentIndex].play() |
| | | } |
| | | |
| | | }, |
| | | |
| | | // 加载视频数据 |
| | | async loadVideos() { |
| | |
| | | ), |
| | | ]; |
| | | } |
| | | this.$nextTick(() => { |
| | | this.initVideoContexts(); |
| | | }); |
| | | this.loading = false; |
| | | if(res.data.data.length < this.videoQuery.pageSize) { |
| | | this.videoNoMore = true; |
| | |
| | | |
| | | // 滑动切换视频 |
| | | onSwiperChange(e) { |
| | | this.videoLoading = false |
| | | // 如果视频处于暂停状态往下刷视频,那么需要再计算一次暂停时间 |
| | | if(!this.currentVideoIsPlaying) { |
| | | if(this.startPauseTime !== 0) { |
| | |
| | | this.savePlayRecord() |
| | | const oldIndex = this.currentIndex; |
| | | this.currentIndex = e.detail.current; |
| | | |
| | | const videoContext = uni.createVideoContext(`video${oldIndex}`, this); |
| | | // 暂停上一个视频 |
| | | if (this.videoContexts[oldIndex]) { |
| | | this.videoContexts[oldIndex].pause(); |
| | | } |
| | | |
| | | videoContext.pause(); |
| | | this.startPauseTime = 0; |
| | | |
| | | // 设置当前播放视频的总时长 |
| | | this.duration = this.videoList[this.currentIndex].videoDuration; |
| | | this.formartDuration = this.sliderFormatTime(this.duration); |
| | | // 播放当前视频 |
| | | if (this.videoContexts[this.currentIndex]) { |
| | | this.videoContexts[this.currentIndex].play(); |
| | | } |
| | | this.clearVideoContext() |
| | | }, |
| | | |
| | | // 清除超出视频可视化区域的视频上下文 |
| | | async clearVideoContext() { |
| | | // 对超出可视化区域的视频上下文做销毁处理 |
| | | const start = Math.max(0, this.currentIndex - this.videoLiveOffset); |
| | | const end = Math.min(this.currentIndex + this.videoLiveOffset, this.videoList.length - 1); |
| | | for (let i = 0; i < this.videoContexts.length; i++) { |
| | | if (i < start || i > end) { |
| | | if (this.videoContexts[i]) { |
| | | this.videoContexts[i].stop(); |
| | | this.videoContexts[i] = null |
| | | } |
| | | } else { |
| | | if (this.videoContexts[i] == null) { |
| | | let videoContent = uni.createVideoContext(`video${i}`, this); |
| | | videoContent.seek(this.videoBufferOffset); |
| | | videoContent.pause(); |
| | | this.videoContexts[i] = videoContent; |
| | | } |
| | | } |
| | | } |
| | | // 播放当前视频 |
| | | const videoContext1 = uni.createVideoContext(`video${this.currentIndex}`, this); |
| | | videoContext1.play() |
| | | // 如果剩余视频不足,触发请求获取更多视频 |
| | | if (this.videoList.length - 1 < this.currentIndex + this.videoLiveOffset) { |
| | | this.loadVideos() |
| | | } |
| | | }, |
| | | }, |
| | | |
| | | // 开始触摸 |
| | | handleSwiperStart(e) { |
| | |
| | | }, |
| | | // 触摸中 |
| | | handleSwiperMove(e) { |
| | | console.log("触摸中", e); |
| | | this.touchXY.endX = e.touches[0].pageX |
| | | this.touchXY.endY = e.touches[0].pageY |
| | | }, |
| | | // 结束触摸 |
| | | handleSwiperEnd(item) { |
| | | // 防止滑动滚动条也触发跳转 |
| | | if (this.showProcess) { |
| | | return |
| | | } |
| | | const diffX = this.touchXY.endX - this.touchXY.startX |
| | | const diffY = this.touchXY.endY - this.touchXY.startY |
| | | |
| | |
| | | }, |
| | | // 单击屏幕:暂停或继续播放 |
| | | togglePlay(index) { |
| | | console.log("单击视频", index, this.videoContexts); |
| | | console.log("单击视频", index); |
| | | const videoContext = uni.createVideoContext(`video${index}`, this); |
| | | if(this.currentVideoIsPlaying) { |
| | | this.videoContexts[index].pause(); |
| | | videoContext.pause(); |
| | | } else { |
| | | this.videoContexts[index].play(); |
| | | videoContext.play(); |
| | | } |
| | | }, |
| | | // 视频播放事件 |
| | | onPlay(id, index) { |
| | | this.getBarRect() |
| | | this.progress = 0 |
| | | console.log(id, index, "触发播放"); |
| | | if(index === this.currentIndex) { |
| | | this.currentVideoIsPlaying = true; |
| | | if(! this.duration) { |
| | |
| | | this.formartDuration = this.sliderFormatTime(this.duration); |
| | | } |
| | | } else { |
| | | this.currentVideoIsPlaying = false; |
| | | return |
| | | } |
| | | this.getBarRect() |
| | | this.progress = 0 |
| | | console.log(id, index, "触发播放"); |
| | | this.playRecord.videoId = id; |
| | | // 没初始化才赋值,因为一个视频重复播放onPlay会重复触发 |
| | | if(this.playRecord.startPlayTime === 0) { |
| | |
| | | const duration = Date.now() - this.startPauseTime |
| | | this.totalPauseTime += duration |
| | | } |
| | | |
| | | this.videoLoading = false |
| | | }, |
| | | |
| | | // 视频暂停事件 |
| | |
| | | console.log(index, "触发暂停"); |
| | | if(index === this.currentIndex) { |
| | | this.currentVideoIsPlaying = false; |
| | | } else { |
| | | this.currentVideoIsPlaying = true; |
| | | return |
| | | this.startPauseTime = Date.now() |
| | | } |
| | | this.startPauseTime = Date.now() |
| | | }, |
| | | // 视频结束事件 |
| | | onEnded(index) { |
| | |
| | | |
| | | // 记录播放时长 |
| | | onTimeUpdate(e) { |
| | | this.videoLoading = false |
| | | this.playRecord.playAt = e.detail.currentTime; |
| | | |
| | | this.currentTime = e.detail.currentTime; |
| | |
| | | this.startProgress = this.progress; // 记录开始时的进度 |
| | | this.startX = e.touches[0].pageX; |
| | | console.log("记录开始时的进度", this.startProgress); |
| | | this.videoContexts[this.currentIndex].pause() |
| | | const videoContext = uni.createVideoContext(`video${this.currentIndex}`, this); |
| | | videoContext.pause() |
| | | // this.updateProgress(e); |
| | | }, |
| | | |
| | |
| | | handleTouchMove(e) { |
| | | if (!this.isDragging || !this.barWidth) return; |
| | | clearTimeout(this.processHidenTimer) |
| | | this.videoContexts[this.currentIndex].pause() |
| | | this.updateProgress(e); |
| | | }, |
| | | |
| | |
| | | handleTouchEnd() { |
| | | this.isDragging = false; |
| | | console.log("滑动结束", this.duration * this.progress); |
| | | this.videoContexts[this.currentIndex].seek(this.duration * this.progress / 100) |
| | | this.videoContexts[this.currentIndex].play() |
| | | const videoContext = uni.createVideoContext(`video${this.currentIndex}`, this); |
| | | videoContext.seek(this.duration * this.progress / 100) |
| | | videoContext.play() |
| | | this.processHidenTimer = setTimeout(() => { |
| | | this.showProcess = false; |
| | | }, 1000); |
| | |
| | | |
| | | // 将像素距离转换为进度增量 |
| | | const deltaProgress = (deltaX / this.barWidth) * 100; |
| | | console.log("进度增量", deltaProgress); |
| | | // 计算新进度 = 开始时的进度 + 滑动增量 |
| | | let newProgress = this.startProgress + deltaProgress; |
| | | |
| | |
| | | newProgress = Math.max(0, Math.min(100, newProgress)); |
| | | |
| | | this.progress = newProgress; |
| | | }, |
| | | // 视频缓冲 |
| | | videoWaiting(index) { |
| | | if (index === this.currentIndex) { |
| | | console.log("视频缓冲中。。。"); |
| | | this.videoLoading = true; |
| | | } |
| | | }, |
| | | // 获取视频总时长 |
| | | onLoadedMetadata(e) { |
| | |
| | | .video-item { |
| | | width: 100%; |
| | | height: 100%; |
| | | object-fit: cover; |
| | | /* object-fit: cover; */ |
| | | } |
| | | .play-icon { |
| | | position: absolute; |