Ver código fonte

修改视频显示

lex 1 ano atrás
pai
commit
97056d0fe8

+ 113 - 11
src/views/attend-class/component/audio-pay.tsx

@@ -56,6 +56,8 @@ export default defineComponent({
     const canvas: any = ref();
     const audio: any = ref();
     let vudio: any = null;
+    const videoSlider =
+      'videoSlider' + Date.now() + Math.floor(Math.random() * 100);
 
     // 切换音频播放
     const onToggleAudio = (e?: any) => {
@@ -156,6 +158,7 @@ export default defineComponent({
       let previousBytesLoaded = 0;
       let timer: any = null;
       let previousTime = Date.now();
+      let buffterCatchArray = [] as any; // 缓存数据显示
       let isWaiting = false;
 
       // 缓存检测状态
@@ -172,7 +175,7 @@ export default defineComponent({
         }, 1500);
       }
 
-      function buffterCatch() {
+      function buffterCatch(time = 0) {
         // 设定一个计时器,检查是否在指定的时间内再次触发了progress事件
         bufferTimeout = setTimeout(() => {
           if (isBuffering) {
@@ -181,7 +184,7 @@ export default defineComponent({
             isBuffering = false;
             audioForms.speedInKbps = '';
           }
-        }, BUFFER_CHECK_INTERVAL);
+        }, time || BUFFER_CHECK_INTERVAL);
       }
 
       function resetBuffterCatch() {
@@ -195,14 +198,41 @@ export default defineComponent({
         buffterCatch();
       }
 
+      /**
+       * 格式化视屏播放有效时间 - 合并区间
+       * @param intervals [[], []]
+       * @example [[4, 8],[0, 4],[10, 30]]
+       * @returns [[0, 8], [10, 30]]
+       */
+      const formatEffectiveTime = (intervals: any[]) => {
+        const res: any = [];
+        intervals.sort((a, b) => a[0] - b[0]);
+        let prev = intervals[0];
+        for (let i = 1; i < intervals.length; i++) {
+          const cur = intervals[i];
+          if (prev[1] >= cur[0]) {
+            // 有重合
+            prev[1] = Math.max(cur[1], prev[1]);
+          } else {
+            // 不重合,prev推入res数组
+            res.push(prev);
+            prev = cur; // 更新 prev
+          }
+        }
+        res.push(prev);
+        return res;
+      };
+
       element.addEventListener('loadedmetadata', () => {
         // 获取视频总时长
         const duration = element.duration;
+        let noData = false;
 
         element.addEventListener('progress', () => {
           const currentTime = Date.now();
           const buffered = element.buffered;
           let currentBytesLoaded = 0;
+          let currentLength = 0;
 
           // 计算视频已缓存的总时长
           let cachedDuration = 0;
@@ -210,10 +240,53 @@ export default defineComponent({
             for (let i = 0; i < buffered.length; i++) {
               currentBytesLoaded += buffered.end(i) - buffered.start(i);
               cachedDuration += buffered.end(i) - buffered.start(i);
+              buffterCatchArray = formatEffectiveTime([
+                ...buffterCatchArray,
+                [buffered.start(i), buffered.end(i)]
+              ]);
+            }
+            for (let i = 0; i < buffered.length; i++) {
+              // 寻找当前时间之后最近的点
+              if (
+                buffered.start(buffered.length - 1 - i) < element.currentTime
+              ) {
+                currentLength =
+                  (buffered.end(buffered.length - 1 - i) / duration) * 100;
+                break;
+              }
             }
             currentBytesLoaded *= element.duration * element.seekable.end(0); // 更精确地近似字节加载量
           }
 
+          // 计算未缓存的时间段
+          const uncachedDuration = duration - cachedDuration;
+          let uncachedTime = true; // 没有缓存时间
+          buffterCatchArray.forEach((item: any) => {
+            if (element.currentTime >= item[0] && uncachedTime <= item[1]) {
+              uncachedTime = false;
+            }
+          });
+          if (duration) {
+            const sliderDom: any = document.querySelector(
+              '#' + videoSlider + ' .n-slider'
+            );
+            if (sliderDom) {
+              sliderDom.style.setProperty(
+                '--catch-width',
+                uncachedDuration > 0 ? `${currentLength}%` : 'calc(100% + 17px)'
+              );
+            }
+            // console.log(
+            //   uncachedTime,
+            //   duration,
+            //   cachedDuration,
+            //   'duration',
+            //   buffterCatchArray,
+            //   element.currentTime,
+            //   bufferedLength
+            // );
+          }
+
           // console.log(
           //   'progress',
           //   currentBytesLoaded,
@@ -221,8 +294,8 @@ export default defineComponent({
           //   currentBytesLoaded > previousBytesLoaded
           // );
           // 计算未缓存的时间段
-          const uncachedDuration = duration - cachedDuration;
-          console.log(uncachedDuration, duration, cachedDuration, 'duration');
+          // const uncachedDuration = duration - cachedDuration;
+          // console.log(uncachedDuration, duration, cachedDuration, 'duration');
           // 如果存在未缓存的时间段,可以根据具体情况做出相应处理
           if (uncachedDuration > 0) {
             if (
@@ -243,30 +316,59 @@ export default defineComponent({
                 } else {
                   audioForms.speedInKbps = `${Number(speedInKbps)} KB/s`;
                 }
+
+                // 如果1秒钟没有返回就重置数据
+                clearTimeout(timer);
+                resetDownloadSpeed();
               }
 
               previousBytesLoaded = currentBytesLoaded;
               previousTime = currentTime;
+              noData = false;
             }
 
-            if (!element.paused && audioForms.isOnline) {
+            if (currentBytesLoaded <= previousBytesLoaded && !uncachedTime) {
               // 如果1秒钟没有返回就重置数据
+
+              if (!noData) {
+                clearTimeout(timer);
+                setTimeout(() => {
+                  if (isBuffering) {
+                    // 如果计时器到达且isBuffering仍为true,则认为缓存停止
+                    console.log('停止缓存数据。');
+                    isBuffering = false;
+                    videoFroms.speedInKbps = '';
+                  }
+                }, 800);
+              }
+
+              noData = true;
+            }
+
+            if (element.paused || !audioForms.isOnline) {
               clearTimeout(timer);
-              resetDownloadSpeed();
-            } else {
               audioForms.speedInKbps = '';
             }
 
-            resetBuffterCatch();
+            if (!isWaiting) {
+              resetBuffterCatch();
+            }
           } else {
-            resetBuffterCatch();
+            clearTimeout(timer);
+            buffterCatch(1000);
           }
         });
         element.addEventListener('waiting', () => {
           console.log('waiting');
           isWaiting = true;
+          let uncachedTime = true; // 没有缓存时间
+          buffterCatchArray.forEach((item: any) => {
+            if (element.currentTime >= item[0] && uncachedTime <= item[1]) {
+              uncachedTime = false;
+            }
+          });
 
-          if (!element.paused) {
+          if (!element.paused && audioForms.isOnline && uncachedTime) {
             // 如果1秒钟没有返回就重置数据
             clearTimeout(timer);
             resetDownloadSpeed();
@@ -391,7 +493,7 @@ export default defineComponent({
             e.stopPropagation();
             emit('reset');
           }}>
-          <div class={styles.slider}>
+          <div class={styles.slider} id={videoSlider}>
             <NSlider
               value={audioForms.currentTimeNum}
               step={0.01}

+ 20 - 0
src/views/attend-class/component/audio.module.less

@@ -153,5 +153,25 @@
     .n-slider .n-slider-handles .n-slider-handle-wrapper {
       transition: all .2s;
     }
+
+    .n-slider .n-slider-rail .n-slider-rail__fill {
+      z-index: 1;
+    }
+
+    .n-slider-handle-wrapper {
+      z-index: 1 !important;
+    }
+
+    .n-slider-handles::after {
+      content: '';
+      width: var(--catch-width);
+      position: absolute;
+      border-radius: calc(var(--n-rail-height) / 2);
+      top: 0;
+      bottom: 0;
+      left: -8Px;
+      // right: -8Px;
+      background-color: #c4c4c4;
+    }
   }
 }

+ 121 - 87
src/views/attend-class/component/video-play.tsx

@@ -75,6 +75,8 @@ export default defineComponent({
     const videoRef = ref();
     const videoItem = ref();
     const videoID = ref('video' + Date.now() + Math.floor(Math.random() * 100));
+    const videoSlider =
+      'videoSlider' + Date.now() + Math.floor(Math.random() * 100);
 
     // 对时间进行格式化
     const timeFormat = (num: number) => {
@@ -223,7 +225,7 @@ export default defineComponent({
       let previousBytesLoaded = 0;
       let timer: any = null;
       let previousTime = Date.now();
-
+      let buffterCatchArray = [] as any; // 缓存数据显示
       let isWaiting = false;
 
       // 缓存检测状态
@@ -240,7 +242,7 @@ export default defineComponent({
         }, time);
       }
 
-      function buffterCatch() {
+      function buffterCatch(time = 0) {
         // 设定一个计时器,检查是否在指定的时间内再次触发了progress事件
         bufferTimeout = setTimeout(() => {
           if (isBuffering) {
@@ -249,7 +251,7 @@ export default defineComponent({
             isBuffering = false;
             videoFroms.speedInKbps = '';
           }
-        }, BUFFER_CHECK_INTERVAL);
+        }, time || BUFFER_CHECK_INTERVAL);
       }
 
       function resetBuffterCatch() {
@@ -263,14 +265,41 @@ export default defineComponent({
         buffterCatch();
       }
 
+      /**
+       * 格式化视屏播放有效时间 - 合并区间
+       * @param intervals [[], []]
+       * @example [[4, 8],[0, 4],[10, 30]]
+       * @returns [[0, 8], [10, 30]]
+       */
+      const formatEffectiveTime = (intervals: any[]) => {
+        const res: any = [];
+        intervals.sort((a, b) => a[0] - b[0]);
+        let prev = intervals[0];
+        for (let i = 1; i < intervals.length; i++) {
+          const cur = intervals[i];
+          if (prev[1] >= cur[0]) {
+            // 有重合
+            prev[1] = Math.max(cur[1], prev[1]);
+          } else {
+            // 不重合,prev推入res数组
+            res.push(prev);
+            prev = cur; // 更新 prev
+          }
+        }
+        res.push(prev);
+        return res;
+      };
+
       element.addEventListener('loadedmetadata', () => {
         // 获取视频总时长
         const duration = element.duration;
+        let noData = false;
 
         element.addEventListener('progress', () => {
           const currentTime = Date.now();
           const buffered = element.buffered;
           let currentBytesLoaded = 0;
+          let currentLength = 0;
 
           // 计算视频已缓存的总时长
           let cachedDuration = 0;
@@ -278,18 +307,66 @@ export default defineComponent({
             for (let i = 0; i < buffered.length; i++) {
               currentBytesLoaded += buffered.end(i) - buffered.start(i);
               cachedDuration += buffered.end(i) - buffered.start(i);
+              // console.log(buffered.end(i), buffered.start(i), 'bufferd');
+              buffterCatchArray = formatEffectiveTime([
+                ...buffterCatchArray,
+                [buffered.start(i), buffered.end(i)]
+              ]);
+            }
+            for (let i = 0; i < buffered.length; i++) {
+              // 寻找当前时间之后最近的点
+              if (
+                buffered.start(buffered.length - 1 - i) < element.currentTime
+              ) {
+                cachedDuration += buffered.end(i) - buffered.start(i);
+                currentLength =
+                  (buffered.end(buffered.length - 1 - i) / duration) * 100;
+                break;
+              }
             }
             currentBytesLoaded *= element.duration * element.seekable.end(0); // 更精确地近似字节加载量
           }
 
           // 计算未缓存的时间段
           const uncachedDuration = duration - cachedDuration;
-          console.log(uncachedDuration, duration, cachedDuration, 'duration');
+          let uncachedTime = true; // 没有缓存时间
+          buffterCatchArray.forEach((item: any) => {
+            if (element.currentTime >= item[0] && uncachedTime <= item[1]) {
+              uncachedTime = false;
+            }
+          });
+
+          if (duration) {
+            const sliderDom: any = document.querySelector(
+              '#' + videoSlider + ' .n-slider'
+            );
+            if (sliderDom) {
+              sliderDom.style.setProperty(
+                '--catch-width',
+                uncachedDuration > 0 ? `${currentLength}%` : 'calc(100% + 17px)'
+              );
+            }
+
+            console.log(
+              uncachedTime,
+              duration,
+              cachedDuration,
+              'duration',
+              buffterCatchArray,
+              element.currentTime,
+              currentLength + '%',
+              isWaiting,
+              currentBytesLoaded <= previousBytesLoaded
+            );
+          }
+
           // 如果存在未缓存的时间段,可以根据具体情况做出相应处理
           if (uncachedDuration > 0) {
             // console.log(
             //   'progress',
             //   currentBytesLoaded > previousBytesLoaded,
+            //   currentBytesLoaded,
+            //   previousBytesLoaded,
             //   '------ 加载中'
             // );
             if (
@@ -299,8 +376,8 @@ export default defineComponent({
               const timeDiff = (currentTime - previousTime) / 1000; // 时间差转换为秒
               const bytesDiff = currentBytesLoaded - previousBytesLoaded; // 字节差值
               const speed = bytesDiff / timeDiff; // 字节每秒
-              console.log(timeDiff, bytesDiff, speed);
-              console.log(element.paused, 'element.paused');
+              // console.log(timeDiff, bytesDiff, speed);
+              // console.log(element.paused, 'element.paused');
               if (!element.paused) {
                 const kbps = speed / 1024;
                 const speedInKbps = kbps.toFixed(2); // 转换为千字节每秒并保留两位小数
@@ -311,16 +388,37 @@ export default defineComponent({
                 } else {
                   videoFroms.speedInKbps = `${Number(speedInKbps)} KB/s`;
                 }
+
+                // 如果1秒钟没有返回就重置数据
+                clearTimeout(timer);
+                resetDownloadSpeed();
               }
 
               previousBytesLoaded = currentBytesLoaded;
               previousTime = currentTime;
+              noData = false;
+              console.log('-------');
             }
-            if (!element.paused && videoFroms.isOnline) {
+            console.log(noData, 'noData');
+            if (currentBytesLoaded <= previousBytesLoaded && !uncachedTime) {
               // 如果1秒钟没有返回就重置数据
+
+              if (!noData) {
+                clearTimeout(timer);
+                setTimeout(() => {
+                  if (isBuffering) {
+                    // 如果计时器到达且isBuffering仍为true,则认为缓存停止
+                    console.log('停止缓存数据。');
+                    isBuffering = false;
+                    videoFroms.speedInKbps = '';
+                  }
+                }, 800);
+              }
+
+              noData = true;
+            }
+            if (element.paused || !videoFroms.isOnline) {
               clearTimeout(timer);
-              resetDownloadSpeed();
-            } else {
               videoFroms.speedInKbps = '';
             }
 
@@ -328,13 +426,21 @@ export default defineComponent({
               resetBuffterCatch();
             }
           } else {
-            resetBuffterCatch();
+            clearTimeout(timer);
+            buffterCatch(1000);
           }
         });
         element.addEventListener('waiting', () => {
           console.log('waiting');
           isWaiting = true;
-          if (!element.paused && videoFroms.isOnline) {
+          let uncachedTime = true; // 没有缓存时间
+          buffterCatchArray.forEach((item: any) => {
+            if (element.currentTime >= item[0] && uncachedTime <= item[1]) {
+              uncachedTime = false;
+            }
+          });
+
+          if (!element.paused && videoFroms.isOnline && uncachedTime) {
             // 如果1秒钟没有返回就重置数据
             clearTimeout(timer);
             resetDownloadSpeed();
@@ -359,87 +465,15 @@ export default defineComponent({
           }
           videoFroms.speedInKbps = '';
         });
-        //   element.addEventListener('progress', () => {
-        //     const currentTime = Date.now();
-        //     const buffered = element.buffered;
-        //     let currentBytesLoaded = 0;
-
-        //     // 计算视频已缓存的总时长
-        //     let cachedDuration = 0;
-        //     if (buffered.length > 0) {
-        //       for (let i = 0; i < buffered.length; i++) {
-        //         currentBytesLoaded += buffered.end(i) - buffered.start(i);
-        //         cachedDuration += buffered.end(i) - buffered.start(i);
-        //       }
-        //       currentBytesLoaded *= element.duration * element.seekable.end(0); // 更精确地近似字节加载量
-        //     }
-
-        //     // 输出未缓存的时间段
-        //     // console.log('未缓存的时间段:', uncachedDuration);
-        //     // 计算未缓存的时间段
-        //     const uncachedDuration = duration - cachedDuration;
-        //     // 如果存在未缓存的时间段,可以根据具体情况做出相应处理
-        //     if (uncachedDuration > 0) {
-        //       console.log('视频部分内容尚未缓存完全!');
-        //       if (currentBytesLoaded > previousBytesLoaded) {
-        //         const timeDiff = (currentTime - previousTime) / 1000; // 时间差转换为秒
-        //         const bytesDiff = currentBytesLoaded - previousBytesLoaded; // 字节差值
-        //         const speed = bytesDiff / timeDiff; // 字节每秒
-        //         // console.log(timeDiff, bytesDiff, speed);
-        //         // console.log(element.paused, 'element.paused');
-        //         if (!element.paused) {
-        //           const kbps = speed / 1024;
-        //           const speedInKbps = kbps.toFixed(2); // 转换为千字节每秒并保留两位小数
-        //           if (kbps > 1024) {
-        //             videoFroms.speedInKbps = `${Number(
-        //               (kbps / 1024).toFixed(2)
-        //             )} M/s`;
-        //           } else {
-        //             videoFroms.speedInKbps = `${Number(speedInKbps)} KB/s`;
-        //           }
-        //         }
-
-        //         previousBytesLoaded = currentBytesLoaded;
-        //         previousTime = currentTime;
-        //       }
-        //       if (!element.paused) {
-        //         // 如果1秒钟没有返回就重置数据
-        //         clearTimeout(timer);
-        //         resetDownloadSpeed();
-        //       }
-        //     } else {
-        //       console.log('整个视频已缓存完毕!');
-        //       // 如果有缓存检测计时器,则清除它
-        //       if (bufferTimeout) {
-        //         clearTimeout(bufferTimeout);
-        //       }
-        //       // 标记为正在缓存
-        //       isBuffering = true;
-
-        //       buffterCatch();
-        //     }
-        //   });
-        // });
-
-        // element.addEventListener('pause', () => {
-        //   clearTimeout(timer);
-        //   // 如果有缓存检测计时器,则清除它
-        //   if (bufferTimeout) {
-        //     clearTimeout(bufferTimeout);
-        //   }
-        //   videoFroms.speedInKbps = '';
-        // });
       });
-
-      // element.addEventListener('error', () => {
-      //   element.pause();
-      //   videoItem.value?.pause();
-      // });
     };
 
     const onChangeOnlineStatus = (val: any) => {
       if (val.type === 'online') {
         videoFroms.isOnline = true;
+        // const currentTime = videoItem.value.currentTime();
+        // videoItem.value.load();
+        // videoItem.value.currentTime(currentTime);
       } else if (val.type === 'offline') {
         videoFroms.isOnline = false;
       }
@@ -532,7 +566,7 @@ export default defineComponent({
             emit('close');
             emit('reset');
           }}>
-          <div class={styles.slider}>
+          <div class={styles.slider} id={videoSlider}>
             <NSlider
               value={videoFroms.currentTimeNum}
               step={0.01}

+ 20 - 0
src/views/attend-class/component/video.module.less

@@ -146,6 +146,26 @@
       .n-slider .n-slider-handles .n-slider-handle-wrapper {
         transition: all .2s;
       }
+
+      .n-slider .n-slider-rail .n-slider-rail__fill {
+        z-index: 1;
+      }
+
+      .n-slider-handle-wrapper {
+        z-index: 1 !important;
+      }
+
+      .n-slider-handles::after {
+        content: '';
+        width: var(--catch-width);
+        position: absolute;
+        border-radius: calc(var(--n-rail-height) / 2);
+        top: 0;
+        bottom: 0;
+        left: -8Px;
+        // right: -8Px;
+        background-color: #c4c4c4;
+      }
     }
   }