瀏覽代碼

修改速度

lex 1 年之前
父節點
當前提交
8e940b1f5a

+ 73 - 1
src/styles/index.less

@@ -126,23 +126,28 @@ body {
 .btnGroup {
   padding: 0 25px;
   padding-bottom: calc(20px + env(safe-area-inset-bottom));
+
   .van-button {
     font-size: 18px !important;
     font-weight: 500;
   }
 }
+
 .btnMore {
   padding-left: 13px;
   padding-right: 13px;
   display: flex !important;
   justify-content: center !important;
+
   // :global {
   .van-button {
     width: 100% !important;
   }
-  .van-button + .van-button {
+
+  .van-button+.van-button {
     margin-left: 15px;
   }
+
   // }
 }
 
@@ -164,12 +169,14 @@ body {
 
 .sticky {
   position: relative;
+
   .van-sticky {
     height: inherit !important;
     top: var(--van-sticky-z-index) !important;
     position: fixed;
     width: 100%;
   }
+
   :global(.van-sticky--fixed) {
     box-shadow: 10px 10px 10px var(--box-shadow-color);
   }
@@ -178,6 +185,7 @@ body {
 .van-toast {
   z-index: 9999 !important;
 }
+
 .van-sticky--fixed {
   position: fixed;
   z-index: 1999;
@@ -206,10 +214,12 @@ body {
     // transition: all 0 ease;
     z-index: 2999 !important;
   }
+
   .van-fade-enter-from,
   .van-fade-enter-active {
     animation: none;
   }
+
   .van-toast__text {
     width: 100px;
     height: 100px;
@@ -219,6 +229,7 @@ body {
     flex-direction: column;
     justify-content: center;
   }
+
   .van-toast--text {
     // background-color: rgba(255, 255, 255, 1);
     // background-color: rgba(0, 0, 0, 0.9);
@@ -226,6 +237,7 @@ body {
     // box-shadow: 0px 8px 20px 2px #f0f0f0;
     z-index: 2999 !important;
   }
+
   .toastAnimate {
     width: 70px;
     height: 70px;
@@ -240,10 +252,12 @@ body {
     // transition: all 0 ease;
     z-index: 2999 !important;
   }
+
   .van-fade-enter-from,
   .van-fade-enter-active {
     animation: none;
   }
+
   .van-toast__text {
     width: 100px;
     height: 100px;
@@ -253,6 +267,7 @@ body {
     flex-direction: column;
     justify-content: center;
   }
+
   .van-toast--text {
     background-color: rgba(255, 255, 255, 1);
     // background-color: rgba(0, 0, 0, 0.9);
@@ -260,6 +275,7 @@ body {
     // box-shadow: 0px 8px 20px 2px #f0f0f0;
     z-index: 2999 !important;
   }
+
   .toastAnimate {
     width: 70px;
     height: 70px;
@@ -287,6 +303,7 @@ body {
   font-family: 'DINA';
   src: url('./font/DIN_Alternate_Bold.ttf');
 }
+
 // 数据为空时,空样式居中显示
 .emptyRootContainer {
   display: flex;
@@ -294,6 +311,7 @@ body {
   justify-content: center;
   min-height: 100vh;
   flex-direction: column;
+
   .o-result-container {
     flex: 1 auto;
     display: flex;
@@ -304,6 +322,7 @@ body {
 input {
   caret-color: #ff8057;
 }
+
 .emptyFixedHeightContainer {
   position: absolute;
   left: 0;
@@ -314,6 +333,7 @@ input {
   justify-content: center;
   align-items: center;
   padding: 0;
+
   .van-empty {
     transform: translateY(-15%);
   }
@@ -336,8 +356,10 @@ input {
 // 搜索公用样式
 .popupBottomSearch {
   --van-picker-toolbar-height: 44px !important;
+
   .van-picker__toolbar {
     position: relative;
+
     &::after {
       position: absolute;
       box-sizing: border-box;
@@ -350,15 +372,19 @@ input {
       transform: scaleY(0.5);
     }
   }
+
   .van-picker__columns {
     padding: 0 24px;
   }
+
   .van-picker-column {
     position: relative;
     z-index: 1;
   }
+
   .van-picker__frame {
     z-index: 0;
+
     &::after {
       background: #f2f2f2;
       border-radius: 8px;
@@ -371,6 +397,7 @@ input {
     padding-bottom: 0 !important;
   }
 }
+
 .searchGroup {
   padding: 0 13px !important;
   line-height: 44px;
@@ -378,6 +405,7 @@ input {
   display: flex;
   align-items: center;
   justify-content: space-around;
+
   .searchItem {
     display: inline-block;
     font-size: 14px;
@@ -389,8 +417,10 @@ input {
     display: flex;
     align-items: center;
     justify-content: center;
+
     &.searchItem-active {
       color: var(--van-primary);
+
       .arrow {
         // transform: rotateX(180deg);
         margin-top: -1px;
@@ -398,17 +428,20 @@ input {
         background-size: 100%;
       }
     }
+
     span {
       max-width: 80px;
       white-space: nowrap;
       overflow: hidden;
       text-overflow: ellipsis;
     }
+
     &.searchItem-large {
       span {
         max-width: 120px;
       }
     }
+
     &.searchItem-normal {
       span {
         max-width: 100px;
@@ -428,6 +461,7 @@ input {
 
 .searchGroup-single {
   padding: 12px 13px !important;
+
   .searchItem {
     position: relative;
     box-sizing: border-box;
@@ -441,6 +475,7 @@ input {
     line-height: 32px;
     display: inline-flex;
     align-items: center;
+
     &::after {
       position: absolute;
       top: 50%;
@@ -463,6 +498,7 @@ input {
         border-color: transparent transparent currentColor currentColor;
       }
     }
+
     span {
       max-width: 150px;
       white-space: nowrap;
@@ -479,6 +515,7 @@ input {
   // font-weight: 600;
   color: #333333;
   margin-right: 21px;
+
   &::after {
     position: absolute;
     top: 50%;
@@ -514,6 +551,7 @@ input {
 .van-sheet_content {
   margin: 0 13px;
   padding-top: 10px;
+
   .van-sheet-item {
     line-height: 52px;
     font-size: 16px;
@@ -521,11 +559,13 @@ input {
     color: #333333;
     text-align: center;
   }
+
   .van-sheet-item-active {
     background: #f2f2f2;
     border-radius: 8px;
   }
 }
+
 .van-action-sheet_bottom__cancel {
   margin: 0 13px;
   width: calc(100vw - 26px);
@@ -533,3 +573,35 @@ input {
   padding: 0;
   color: #aaaaaa;
 }
+
+
+.videoPlaySpeedItem {
+  padding: 4px 0 4px 12px;
+  display: flex;
+  align-items: center;
+  font-size: 12px;
+  font-weight: 500;
+
+  .point {
+    display: inline-block;
+    width: 12px;
+    height: 12px;
+    border-radius: 50%;
+    background: #0000001a;
+    margin-right: 6px;
+    margin-top: -1px;
+    position: relative;
+  }
+
+  &.active .point::before {
+    position: absolute;
+    left: 3px;
+    top: 3px;
+    content: '';
+    display: inline-block;
+    width: 6px;
+    height: 6px;
+    border-radius: 50%;
+    background: var(--van-primary);
+  }
+}

+ 127 - 101
src/views/coursewarePlay/component/video-item/index.module.less

@@ -1,133 +1,159 @@
 .videoWrap {
-    width: 100%;
-    height: 100%;
+  width: 100%;
+  height: 100%;
 
-    :global {
-        .plyr--video {
-            width: 100%;
-            height: 100%;
-        }
+  :global {
+    .plyr--video {
+      width: 100%;
+      height: 100%;
+    }
 
-        .plyr__time {
-            display: block !important;
-        }
-        .plyr__video-wrapper{
-            pointer-events: none;
-        }
+    .plyr__time {
+      display: block !important;
+    }
+
+    .plyr__video-wrapper {
+      pointer-events: none;
     }
+  }
 }
 
 :global(.bottomFixed).controls {
+  width: 100%;
+  background: linear-gradient(0deg, rgba(0, 0, 0, 0.5), transparent);
+  padding: 0 !important;
+  flex-direction: column;
+  transition: all 0.5s;
+
+  .time {
+    display: flex;
+    justify-content: space-between;
     width: 100%;
-    background: linear-gradient(0deg, rgba(0, 0, 0, 0.5), transparent);
-    padding: 0 !important;
-    flex-direction: column;
-    transition: all 0.5s;
-
-    .time {
-        display: flex;
-        justify-content: space-between;
-        width: 100%;
-        color: #fff;
-        font-size: 10px;
-        padding: 4px 20px;
-
-        :global {
-            .plyr__time+.plyr__time:before {
-                content: '';
-            }
-        }
+    color: #fff;
+    font-size: 10px;
+    padding: 4px 20px;
+
+    :global {
+      .plyr__time+.plyr__time:before {
+        content: '';
+      }
     }
+  }
+
+  .slider {
+    width: 100%;
+    padding: 0 20px;
+
+    :global {
+      .van-slider__button {
+        background: var(--van-primary);
+      }
 
-    .slider {
+      .van-loading {
         width: 100%;
-        padding: 0 20px;
+        height: 100%;
+      }
+    }
+  }
 
-        :global {
-            .van-slider__button {
-                background: var(--van-primary);
-            }
+  .actions {
+    display: flex;
+    justify-content: space-between;
+    width: 100%;
+    color: #fff;
+    font-size: 12px;
+    padding: 0 20px;
+    align-items: center;
 
-            .van-loading {
-                width: 100%;
-                height: 100%;
-            }
-        }
+    .actionWrap {
+      display: flex;
     }
 
-    .actions {
-        display: flex;
-        justify-content: space-between;
+    .actionBtn {
+      display: flex;
+      width: 30px;
+      height: 30px;
+      padding: 4px 0;
+      background: transparent;
+    }
+
+    .actionBtn>img {
+      width: 100%;
+      height: 100%;
+    }
+
+    :global {
+      .van-loading__circular {
         width: 100%;
-        color: #fff;
-        font-size: 12px;
-        padding: 0 20px;
-        align-items: center;
+        height: 100%;
+      }
+    }
 
-        .actionWrap {
-            display: flex;
-        }
+    .playIcon {
+      display: none;
+    }
 
-        .actionBtn {
-            display: flex;
-            width: 38px;
-            height: 38px;
-            padding: 4px 0;
-            background: transparent;
-        }
+    .btnPlay img:nth-child(2) {
+      display: block;
+    }
 
-        .actionBtn>img {
-            width: 100%;
-            height: 100%;
-        }
+    .btnPause img:nth-child(3) {
+      display: block;
+    }
 
-        :global {
-            .van-loading__circular {
-                width: 100%;
-                height: 100%;
-            }
+    .btnPlay,
+    .btnPause {
+      :global {
+        .van-loading {
+          display: none;
         }
+      }
+    }
 
-        .playIcon {
-            display: none;
+    .loopBtn {
+      :global {
+        .loop {
+          display: block;
         }
 
-        .btnPlay img:nth-child(2) {
-            display: block;
+        .loopActive {
+          display: none;
         }
+      }
+    }
 
-        .btnPause img:nth-child(3) {
-            display: block;
+    .loopBtn.active {
+      :global {
+        .loop {
+          display: none;
         }
 
-        .btnPlay,
-        .btnPause {
-            :global {
-                .van-loading {
-                    display: none;
-                }
-            }
-        }
-        .loopBtn{
-            :global{
-                .loop{
-                    display: block;
-                }
-                .loopActive{
-                    display: none;
-                }
-            }
-        }
-        .loopBtn.active{
-            :global{
-                .loop{
-                    display: none;
-                }
-                .loopActive{
-                    display: block;
-                }
-            }
+        .loopActive {
+          display: block;
         }
+      }
+    }
+
+
+    .speedBtn {
+      position: relative;
+    }
 
+    .popoverGroup {
+      position: absolute;
+      z-index: 99;
+      left: -22px;
+      background-color: #fff;
+      border-radius: 8px;
+      bottom: 38px;
+      width: 80px;
+      color: #333;
+      padding: 4px 0;
+      display: none;
+
+      &.active {
+        display: block;
+      }
     }
+  }
 }

+ 86 - 6
src/views/coursewarePlay/component/video-item/index.tsx

@@ -2,8 +2,9 @@ import { defineComponent, nextTick, onMounted, reactive, toRefs, watch } from 'v
 import 'plyr/dist/plyr.css'
 import Plyr from 'plyr'
 import styles from './index.module.less'
+import iconSpeed from '../../image/video-speed1.png'
 
-import { iconVideoBg, iconLoop, iconLoopActive, iconPlay,  iconPause } from '../../image/icons.json'
+import { iconVideoBg, iconLoop, iconLoopActive, iconPlay, iconPause } from '../../image/icons.json'
 
 export default defineComponent({
   name: 'video-play',
@@ -26,11 +27,15 @@ export default defineComponent({
       videoContianerRef: null as unknown as HTMLAudioElement,
       videoState: 'pause' as 'init' | 'play' | 'pause',
       animationState: 'start' as 'start' | 'end',
-      videoItem: null as unknown as Plyr
+      videoItem: null as unknown as Plyr,
+      speedControl: false,
+      defaultSpeed: 1 // 默认速度
     })
     const controlID = 'v' + Date.now() + Math.floor(Math.random() * 100)
     const playBtnId = 'play' + Date.now() + Math.floor(Math.random() * 100)
     const loopBtnId = 'loop' + Date.now() + Math.floor(Math.random() * 100)
+    const speedBtnId = 'speed' + Date.now() + Math.floor(Math.random() * 100)
+    const speedPopoverId = 'popover' + Date.now() + Math.floor(Math.random() * 100)
 
     const togglePlay = (e: Event) => {
       e.stopPropagation()
@@ -63,6 +68,33 @@ export default defineComponent({
       })
       document.getElementById(playBtnId)?.addEventListener('click', togglePlay)
       document.getElementById(loopBtnId)?.addEventListener('click', toggleLoop)
+
+      document.getElementById(speedBtnId)?.addEventListener('click', () => {
+        data.speedControl = !data.speedControl
+        // const element = document.getElementById(speedPopoverId)
+        // if (data.speedControl) {
+        //   element?.classList.add(styles.active)
+        // } else {
+        //   element?.classList.remove(styles.active)
+        // }
+        toggleSpeedControl(data.speedControl)
+      })
+
+      const nodeList = document.querySelectorAll('#' + speedPopoverId + ' .videoPlaySpeedItem')
+      nodeList.forEach((item) => {
+        item?.addEventListener('click', (e) => {
+          nodeList.forEach((child) => {
+            child?.classList.remove('active')
+          })
+          const target: any = e?.target
+          item?.classList.add('active')
+          data.videoItem.speed = target?.dataset.value * 1
+          data.defaultSpeed = target?.dataset.value * 1
+          // 设置完速度之后关闭弹窗
+          toggleSpeedControl(false)
+        })
+      })
+
       setName()
     }
     const setName = () => {
@@ -107,6 +139,33 @@ export default defineComponent({
                             <img class="loop" src="${iconLoop}" />
                             <img class="loopActive" src="${iconLoopActive}" />
                         </button>
+                        <div style="position: relative">
+                          <button id="${speedBtnId}" class="${styles.actionBtn} ${styles.speedBtn}">
+                              <img class="loop" src="${iconSpeed}" />
+                          </button>
+                          <div class="${styles.popoverGroup}" id="${speedPopoverId}">
+                            <div class="videoPlaySpeedItem active" data-value="1">
+                              <i class="point" data-value="1"></i>
+                              <span data-value="1">1倍</span>
+                            </div>
+                            <div class="videoPlaySpeedItem" data-value="1.25">
+                              <i class="point" data-value="1.25"></i>
+                              <span data-value="1.25">1.25倍</span>
+                            </div>
+                            <div class="videoPlaySpeedItem" data-value="1.5">
+                              <i class="point" data-value="1.5"></i>
+                              <span data-value="1.5">1.5倍</span>
+                            </div>
+                            <div class="videoPlaySpeedItem" data-value="1.75">
+                              <i class="point" data-value="1.75"></i>
+                              <span data-value="1.75">1.75倍</span>
+                            </div>
+                            <div class="videoPlaySpeedItem" data-value="2">
+                              <i class="point" data-value="2"></i>
+                              <span data-value="2">2倍</span>
+                            </div>
+                          </div>
+                        </div>
                     </div>
                     <div id="videoItemName"></div>
                 </div>
@@ -127,8 +186,23 @@ export default defineComponent({
       })
     })
 
+    const toggleSpeedControl = (isShow: boolean) => {
+      const element = document.getElementById(speedPopoverId)
+      if (isShow) {
+        element?.classList.add(styles.active)
+        data.speedControl = true
+      } else {
+        element?.classList.remove(styles.active)
+        data.speedControl = false
+      }
+    }
+
     const toggleHideControl = (isShow: boolean) => {
       data.videoItem?.toggleControls(isShow)
+
+      if (!isShow) {
+        toggleSpeedControl(isShow)
+      }
     }
     watch(
       () => props.activeModel,
@@ -141,6 +215,11 @@ export default defineComponent({
       () => props.item,
       () => {
         setName()
+        // 设置视屏播放器的默认速度
+        if (data.videoItem) data.videoItem.speed = data.defaultSpeed || 1
+
+        // 切换的时候隐藏
+        toggleSpeedControl(false)
       }
     )
     let videoTimer = null as any
@@ -169,6 +248,7 @@ export default defineComponent({
       nextTick(() => {
         videoErrorTimer = setTimeout(() => {
           data.videoContianerRef.src = props.item?.content
+
           emit('play')
           data.videoContianerRef.load()
           // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -178,7 +258,7 @@ export default defineComponent({
       videoErrorCount++
     }
     const getVideoRef = () => {
-      return data.videoContianerRef;
+      return data.videoContianerRef
     }
     expose({
       getVideoRef
@@ -204,7 +284,7 @@ export default defineComponent({
           }}
           onPlay={() => {
             videoErrorCount = 0
-            console.log('开始播放')
+            // console.log('开始播放')
             data.videoState = 'play'
             changePlayBtn('pause')
             emit('close')
@@ -212,13 +292,13 @@ export default defineComponent({
             clearTimeout(videoErrorTimer)
           }}
           onPause={() => {
-            console.log('暂停播放')
+            // console.log('暂停播放')
             data.videoState = 'pause'
             changePlayBtn('play')
             emit('pause')
           }}
           onEnded={() => {
-            console.log('播放结束')
+            // console.log('播放结束')
             data.videoState = 'pause'
             changePlayBtn('play')
             emit('ended')

二進制
src/views/coursewarePlay/image/video-speed.png


二進制
src/views/coursewarePlay/image/video-speed1.png


+ 1 - 1
src/views/coursewarePlay/index.tsx

@@ -318,7 +318,7 @@ export default defineComponent({
     onMounted(async () => {
       await getDetail()
       const hasFree = String(data.detail?.accessScope) === '0'
-      if (!hasFree){
+      if (!hasFree) {
         const hasVip = handleCheckVip()
         if (!hasVip) {
           nextTick(() => {

+ 1 - 1
vite.config.ts

@@ -13,7 +13,7 @@ function resolve(dir: string) {
 // https://github.com/vitejs/vite/issues/1930 .env
 // const proxyUrl = 'https://online.lexiaoya.cn/'
 // const proxyUrl = 'https://test.lexiaoya.cn/'
-const proxyUrl = 'https://dev.lexiaoya.cn/'
+const proxyUrl = 'https://test.lexiaoya.cn/'
 // const proxyUrl = 'http://47.98.131.38:8989/'
 // const proxyUrl = 'http://192.168.3.20:8989/' // 邹旋
 // const proxyUrl = 'http://192.168.3.143:8989/' // 尚科