Bläddra i källkod

作品详情页

黄琪勇 11 månader sedan
förälder
incheckning
2708d2b078

BIN
src/views/creation/images/Landscape.png


BIN
src/views/creation/images/audioBg.png


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
src/views/creation/images/audioBga.json


BIN
src/views/creation/images/bg.png


BIN
src/views/creation/images/cance.png


BIN
src/views/creation/images/confirm.png


BIN
src/views/creation/images/edit.png


BIN
src/views/creation/images/icon-delete.png


BIN
src/views/creation/images/icon-download.png


BIN
src/views/creation/images/icon-member.png


BIN
src/views/creation/images/icon-share.png


BIN
src/views/creation/images/icon-zan.png


BIN
src/views/creation/images/midPlay.png


BIN
src/views/creation/images/prompt.png


BIN
src/views/creation/images/upward.png


BIN
src/views/creation/images/videobg.png


+ 281 - 491
src/views/creation/index.module.less

@@ -1,343 +1,236 @@
-.playSection {
-  min-height: 175px;
-
-  :global {
-    .vjs-poster {
-      background-size: cover;
-    }
-
-    .video-js .vjs-progress-control:hover .vjs-progress-holder {
-      font-size: inherit !important;
-      outline: none;
+.creationBg{
+  position: fixed;
+  z-index: -1;
+  width: 100vw;
+  height: 100vh;
+  top: 0;
+  left: 0;
+  background: url("./images/bg.png") no-repeat;
+  background-size: cover;
+}
+.creation{
+  :global{
+    .van-nav-bar .van-icon{
+      color: #ffffff;
     }
-
-    .video-js .vjs-slider:focus {
-      box-shadow: none !important;
-      text-shadow: none !important;
-      outline: none;
+  }
+  &.isScreenScroll{
+    :global{
+      .van-nav-bar .van-icon{
+        color: #333333;
+      }
     }
   }
 }
-
-@keyframes rotateImg {
-  100% {
-    transform: rotate(360deg);
-  }
+.singer{
+  text-align: center;
+  font-weight: 400;
+  font-size: 14px;
+  color: rgba(255,255,255,0.7);
+  line-height: 20px;
+  margin-bottom: 14vh;
 }
 
-.audioSection {
+.playSection{
+  height: 210px;
   position: relative;
-  background: url('./images/audio-banner-bg.png') no-repeat top center;
-  background-size: cover;
-  height: 175px;
-
-  .audioContainer {
-    position: absolute;
-    top: 0;
-    left: 50%;
-    width: 196px;
-    height: 35px;
-    transform: translate(-50%, 60px);
-
-    .waveActive,
-    .waveDefault {
+  :global {
+      .plyr {
+          width: 100%;
+          height: 100%;
+          .plyr__controls{
+              background: initial;
+              padding: 0 12px;
+              opacity: 1 !important;
+              transform: translateY(0) !important;
+              pointer-events: initial !important;
+              .plyr__controls__item.plyr__progress__container{
+                  input[type=range]{
+                      color: #73C1FF;
+                      height: 10px;
+                  }
+                  input[type="range"]::-webkit-slider-runnable-track {
+                      height: 2px;
+                  }
+                  input[type="range"]::-webkit-slider-thumb {
+                      width: 8px;
+                      height: 8px;
+                      margin-top: -3px;
+                  }
+                  .plyr__progress__buffer{
+                      height: 2px;
+                      color: rgba(115,193,255,0.8);
+                      background-color: #fff;
+                      margin-top: -1px;
+                  }
+              }
+          }
+      }
+  }
+  .audioBox{
       width: 100%;
       height: 100%;
-    }
-
-    .waveDefault {
-      position: absolute;
-      top: 0;
-      left: 0;
-      background: url('./images/wave-1.png')no-repeat center left;
-      background-size: cover;
-    }
-
-    .waveActive {
-      position: absolute;
-      top: 0;
-      left: 0;
-      z-index: 1;
-      background: url('./images/wave-2.png')no-repeat center left;
-      background-size: cover;
-    }
+      background: url("./images/audioBg.png") no-repeat;
+      background-size: 100% 100%;
+      position: relative;
+      .audioBga{
+        position: absolute;
+        left: 0;
+        bottom: 0;
+        width: 100%;
+        height: 100%;
+      }
+      :global {
+          .plyr {
+              position: absolute;
+              height: initial;
+              left: 0;
+              bottom: 0;
+          }
+      }
+      .audioVisualizer{
+          position: absolute;
+          top: 50%;
+          left: 50%;
+          transform: translate(-50%,-50%);
+          width: 308px;
+          height: 55px;
+      }
   }
-
-
-  .audioBox {
+  .playLarge{
     position: absolute;
     left: 50%;
-    transform: translate(-50%, 50%);
-    z-index: 2;
-    width: 74px;
-    height: 75px;
-    background: url('./images/audio-bg.png') no-repeat center;
-    background-size: contain;
-
-    // &::after {
-    //   content: '';
-    //   width: 134px;
-    //   height: 73px;
-    //   position: absolute;
-    //   left: 50%;
-    //   transform: translate(-50%, 50%);
-    //   background: url('./images/audio-shadow.png') no-repeat center;
-    //   background-size: contain;
-    //   z-index: -1;
-    // }
-    .audioPan {
-      position: absolute;
-      left: 8px;
-      top: 6px;
-      z-index: 8;
-      width: 59px;
-      height: 60px;
-      background: url('./images/audio-pan.png') no-repeat center;
-      background-size: contain;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-
-      animation: rotateImg 6s linear infinite;
-
-      &.imgRotate {
-        animation-play-state: paused;
-      }
+    top: 50%;
+    transform: translate(-50%, -50%);
+    width: 48px;
+    height: 48px;
+    background: url("./images/midPlay.png") no-repeat;
+    background-size: 100% 100%;
+    z-index: 1;
+    display: none;
+    &.playIngShow{
+      display: initial;
+    }
+  }
+  .mediaTime{
+    position: absolute;
+    left: 50%;
+    transform: translateX(-50%);
+    bottom: 56px;
+    display: none;
+    font-weight: 500;
+    font-size: 14px;
+    color: #FFFFFF;
+    line-height: 20px;
+    & div:first-child{
+      width: 50px;
+      text-align: right;
     }
-
-    .audioImg {
-      width: 32px;
-      height: 32px;
-      border-radius: 50%;
-      overflow: hidden;
+    .note{
+      margin: 0 4px;
     }
-
-    .audioPoint {
-      position: absolute;
-      z-index: 9;
-      left: 50%;
-      top: 50%;
-      transform: translate(-50%, -50%);
-      width: 8px;
-      height: 8px;
-      background: url('./images/audio-point.png') no-repeat center;
-      background-size: contain;
+    .duration{
+      color: rgba(255,255,255,0.5);
     }
-
-    .audioZhen {
-      position: absolute;
-      z-index: 9;
-      right: -4px;
-      top: -33px;
-      width: 26px;
-      height: 87px;
-      background: url('./images/audio-zhen.png') no-repeat center;
-      background-size: contain;
-      transition: transform .5s ease-in-out;
-
-      &.active {
-        transform: rotate(92deg) translate3d(0, 0, 3px);
-        transition: transform .5s ease-in-out;
-      }
+  }
+  &.mediaTimeShow{
+    .mediaTime{
+      display: flex;
     }
   }
-
 }
 
-.controls {
-  position: absolute;
-  left: 0;
-  bottom: 0;
-  right: 0;
-  height: 44px;
+.musicSection {
+  width: 100%;
+  min-height: calc(100vh - var(--barheight) - 20px - 14vh - 210px - 40px - 80px);
   display: flex;
   flex-direction: column;
-  justify-content: space-between;
-  flex-direction: row;
-  transition: all 0.5s;
-  padding: 0 12px;
+  justify-content: flex-end;
+  padding: 10px 12px 0;
 
-  &>div {
-    display: flex;
-    align-items: center;
-  }
-
-  &.hide {
-    transform: translateY(100%);
-  }
-
-
-
-  .actionBtn {
-    line-height: 0;
-    margin-right: 4px;
-
-    img {
-      width: 14px;
-      height: 14px;
-      margin-bottom: -2px;
-    }
-  }
-
-  .time {
+  .avatarInfoBox{
     display: flex;
     justify-content: space-between;
-    flex: 1;
-    min-width: 86px;
-    font-size: 12px;
-    color: #131415;
-    line-height: 20px;
-
-    span {
-      font-size: 12px;
-      padding: 0 1px;
-    }
-  }
-
-  .slider {
-    width: 100%;
-    margin: 0 12px;
-    --van-slider-bar-height: 4px;
-    --van-slider-button-width: 13px !important;
-    --van-slider-button-height: 13px !important;
-    --van-slider-inactive-background: #fff;
-    --van-slider-active-background: #269EFE !important;
-
-    :global {
-
-      .van-loading {
-        width: 100%;
-        height: 100%;
-      }
-
-    }
-  }
-
-}
-
-.userSection {
-  padding: 15px 12px;
-  background-color: transparent;
-
-  .userLogo {
-    width: 44px;
-    height: 44px;
-    border: 1px solid #FFFFFF;
-    margin-right: 10px;
-    border-radius: 50%;
-    overflow: hidden;
-  }
-
-  .userInfo {
-    .name {
+    align-items: center;
+    .avatar{
       display: flex;
       align-items: center;
-      font-size: 16px;
-      font-weight: 500;
-      color: #333333;
-      line-height: 22px;
-
-      span {
-        display: inline-block;
-        white-space: nowrap;
+      .userLogo{
+        width: 44px;
+        height: 44px;
+        border: 1px solid #FFFFFF;
+        margin-right: 10px;
+        border-radius: 50%;
         overflow: hidden;
-        text-overflow: ellipsis;
-        max-width: 100px;
+      }
+      .infoCon{
+        .info{
+          display: flex;
+          align-items: center;
+          .userName{
+            font-weight: 500;
+            font-size: 16px;
+            color: #FFFFFF;
+            line-height: 22px;
+          }
+          .iconMember{
+            margin-left: 6px;
+            width: 14px;
+            height: 14px;
+          }
+        }
+        .sub{
+          margin-top: 2px;
+          font-weight: 400;
+          font-size: 12px;
+          color: #FFFFFF;
+          line-height: 17px;
+        }
       }
     }
-
-    .sub {
-      padding-top: 2px;
-      font-size: 12px;
-      color: #777777;
-      line-height: 17px;
-    }
-
-    .iconMember {
-      margin-left: 6px;
-      width: 14px;
-      height: 14px;
-    }
-  }
-
-  .zan {
-    background: #FFFFFF;
-    border-radius: 13px;
-    font-size: 14px;
-    color: #777777;
-    line-height: 20px;
-    padding: 4px 9px 3px;
-    display: inline-flex;
-    align-items: center;
-
-    &.zanActive {
-      background: #F7EEEE;
-      color: #FF6A6A;
-    }
-
-    .iconZan {
-      width: 18px;
-      height: 18px;
-      margin-right: 2px;
-    }
-  }
-}
-
-.musicSection {
-  margin: 0 13px 12px;
-  padding: 14px 12px;
-  background: #FFFFFF;
-  border-radius: 10px;
-
-  .musicName {
-    font-size: 15px;
-    font-weight: 500;
-    color: #333333;
-    line-height: 21px;
-    // display: flex;
-    // align-items: center;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-    max-width: 100%;
-
-    .musicTag {
-      margin-right: 6px;
-      padding: 1px 6px;
-      font-size: 12px;
-      color: #FF7B31;
-      line-height: 17px;
-      background: rgba(255, 166, 115, 0.07);
-      border-radius: 9px;
-      border: 1px solid #FFBF9A;
+    .linkes{
+      display: flex;
+      align-items: center;
+      border-radius: 13px;
+      padding: 4px 8px 3px;
+      background-color: rgba(255,255,255,.12);
       font-weight: 400;
-      vertical-align: text-bottom;
+      font-size: 14px;
+      color: #FFFFFF;
+      .iconZan{
+        width: 18px;
+        height: 18px;
+        margin-right: 2px;
+      }
     }
   }
-
-  .musicDesc {
-    padding-top: 8px;
+  .textEllipsis{
+    margin-top: 10px;
+    font-weight: 400;
     font-size: 14px;
-    color: #777777;
+    color: #FFFFFF;
     line-height: 20px;
+    :global{
+      .van-text-ellipsis__action{
+        color: #FFFFFF;
+        font-weight: 500;
+      }
+    }
   }
 }
 
 .likeSection {
-  margin: 0 13px 12px;
-  background: #FFFFFF;
+  margin: 20px 12px;
+  background: rgba(255,255,255,.09);
   border-radius: 10px;
-  padding: 10px 12px;
-
+  padding: 12px 12px 0 12px;
   .likeTitle {
     display: flex;
     align-items: center;
     font-size: 17px;
     font-weight: 600;
-    color: #333333;
+    color: #ffffff;
     line-height: 24px;
-    padding-bottom: 8px;
-
     &::before {
       display: inline-block;
       content: '';
@@ -348,120 +241,141 @@
       margin-right: 6px;
     }
   }
-}
-
-.likeItem {
-  padding: 16px 0;
+  .likeItem {
+    padding: 13px 0 16px;
+    background-color: initial;
+    border-bottom: 1px solid rgba(242,242,242,0.12);
+    &.likeItemLast{
+      border-bottom: none;
+    }
+    .userLogo {
+      border-radius: 50%;
+      overflow: hidden;
+      width: 42px;
+      height: 42px;
+      margin-right: 7px;
+    }
 
-  .userLogo {
-    border-radius: 50%;
-    overflow: hidden;
-    width: 42px;
-    height: 42px;
-    margin-right: 7px;
-  }
+    .userInfo {
+      .name {
+        font-size: 16px;
+        font-weight: 500;
+        color: #ffffff;
+        line-height: 22px;
+      }
 
-  .userInfo {
-    .name {
-      font-size: 16px;
-      font-weight: 500;
-      color: #333333;
-      line-height: 22px;
+      .sub {
+        padding-top: 2px;
+        font-size: 13px;
+        color: #ffffff;
+        line-height: 18px;
+      }
     }
 
-    .sub {
-      padding-top: 2px;
+    .time {
+      font-weight: 400;
       font-size: 13px;
-      color: #777777;
+      color: #FFFFFF;
       line-height: 18px;
     }
   }
-
-  .time {
-    font-size: 13px;
-    color: #777777;
-    line-height: 18px;
+  .mEmpty{
+    padding: 0;
   }
 }
 
-
+.upward{
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  height: 40px;
+  background: linear-gradient( 180deg, rgba(20,39,85,0) 0%, #152956 26%, #1B3454 100%);
+  > img{
+    width: 19px;
+    height: 15px;
+  }
+}
 .bottomSection {
   display: flex;
   align-items: center;
   justify-content: space-between;
-  background-color: #fff;
-  padding: 15px 12px;
-
+  padding: 0 12px 0 20px;
+  width: 100%;
+  height: 80px;
+  background: #1F1F1F;
+  box-shadow: 0px -1px 10px 0px rgba(0,0,0,0.05);
   .bottomShare {
     display: flex;
     align-items: center;
-
     p {
-      padding: 0 15px;
-      text-align: center;
-      line-height: 0;
-
-      &:first-child {
-        padding-left: 5px;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+      margin-right: 28px;
+      &:last-child{
+        margin-right: 0;
       }
     }
-
     img {
       width: 18px;
       height: 18px;
     }
-
     span {
-      padding-top: 8px;
+      margin-top: 8px;
+      font-weight: 400;
       font-size: 12px;
-      color: #333333;
+      color: #ffffff;
       line-height: 17px;
-      display: block;
     }
   }
 
   .btnEdit {
-    font-size: 14px;
-    font-weight: 500;
-    background: linear-gradient(135deg, #19F1E1 0%, #0094FF 100%);
-    color: #FFFFFF;
-    line-height: 22px;
-    min-width: 80px;
+    width: 80px;
     height: 30px;
-    border: none;
   }
 }
 
 .popupContainer {
-  width: 80%;
+  width: 287px;
+  background: rgba(255,255,255,0.31);
+  border-radius: 12px !important;
+  border: 1px solid rgba(255,252,252,0.53);
+  padding: 9px 8px;
+  overflow: initial;
+  .prompt{
+    width: 151px;
+    height: 32px;
+    position: absolute;
+    top: -7px;
+    left: 50%;
+    transform: translateX(-50%);
+  }
+  .deleteBox{
+    background: linear-gradient( 224deg, #ECF5FF 0%, #D5E8FF 100%);
+    border-radius: 12px;
+    padding: 37px 0 15px;
+  }
 
 
   .popupContent {
-    padding: 29px 0 25px;
     text-align: center;
-    font-size: 18px;
-    font-weight: 500;
-    color: #333333;
-    line-height: 25px;
+    font-weight: 400;
+    font-size: 16px;
+    color: #334A64;
+    line-height: 26px;
   }
 
   .popupBtnGroup {
-    text-align: center;
-    margin-bottom: 22px;
-
-    :global {
-      .van-button {
-        height: 40px;
-        font-size: 16px;
-        font-weight: 400 !important;
-        line-height: 22px;
-        min-width: 122px;
-
-        &:last-child {
-          margin-left: 10px;
-          background: linear-gradient(to right, #5BECFF, #259CFE);
-          border: none;
-        }
+    margin-top: 18px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    >img{
+      width: 118px;
+      height: 37px;
+      & + img{
+        margin-left: 11px;
       }
     }
   }
@@ -572,130 +486,6 @@
 }
 
 // 平板样式
-.creationTablet {
-
-  .videoSection {
-    height: 390px;
-  }
-
-  .audioSection {
-    height: 300px;
-
-    .audioContainer {
-      width: 400px;
-      height: 70px;
-      transform: translate(-50%, 120px);
-    }
-
-    .audioBox {
-      width: 152px;
-      height: 155px;
-
-      .audioPan {
-        left: 17px;
-        top: 14px;
-        width: 119px;
-        height: 120px;
-      }
-
-      .audioImg {
-        width: 65px;
-        height: 65px;
-      }
+.creationTablet{
 
-      .audioPoint {
-        width: 14px;
-        height: 14px;
-      }
-
-      .audioZhen {
-        width: 52px;
-        height: 176px;
-        top: -60px;
-      }
-    }
-  }
-
-  .bottomSection {
-    .btnEdit {
-      height: 42px;
-      font-size: 18px;
-      padding: 0 32px
-    }
-  }
-
-  .bottomShare {
-    img {
-      width: 24px;
-      height: 24px;
-    }
-
-    span {
-      font-size: 15px;
-    }
-  }
-
-  .controls {
-    .slider {
-      width: 100%;
-      margin: 0 12px;
-      --van-slider-bar-height: 8px;
-      --van-slider-button-width: 20px !important;
-      --van-slider-button-height: 20px !important;
-      --van-slider-inactive-background: #fff;
-      --van-slider-active-background: #269EFE !important;
-    }
-
-    .actionBtn {
-      img {
-        width: 20px;
-        height: 20px;
-      }
-    }
-
-    .time {
-      font-size: 16px;
-    }
-  }
-
-  .cell {
-    .cellImage {
-      width: 99px;
-      height: 99px;
-
-
-    }
-
-    .cellImg {
-      &::before {
-        top: 2px;
-        width: 95px;
-        height: 95px;
-        right: -12px;
-      }
-
-      .iconZan {
-        font-size: 12px;
-        bottom: 8px;
-        left: 5px;
-        border-radius: 20px;
-        padding-right: 5px;
-
-
-        &::before {
-          padding-right: 3px;
-        }
-      }
-    }
-
-    margin-right: 34px;
-
-    &:nth-child(3n + 3) {
-      margin-right: 30px;
-    }
-
-    &:nth-child(5n + 5) {
-      margin-right: 0;
-    }
-  }
-}
+}

+ 311 - 240
src/views/creation/index.tsx

@@ -4,7 +4,8 @@ import {
   onMounted,
   onUnmounted,
   reactive,
-  watch
+  watch,
+  ref
 } from 'vue';
 // import WaveSurfer from 'wavesurfer.js';
 import styles from './index.module.less';
@@ -18,13 +19,20 @@ import {
   Popup,
   Slider,
   showDialog,
-  showToast
+  showToast,
+  Sticky,
+  TextEllipsis
 } from 'vant';
 import iconDownload from './images/icon-download.png';
 import iconShare from './images/icon-share.png';
 import iconDelete from './images/icon-delete.png';
+import iconEdit from './images/edit.png';
+import iconUpward from './images/upward.png';
 import iconMember from './images/icon-member.png';
 import iconZan from './images/icon-zan.png';
+import promptImg from './images/prompt.png';
+import confirmImg from './images/confirm.png';
+import canceImg from './images/cance.png';
 import iconZanActive from './images/icon-zan-active.png';
 import iconPlay from './images/icon-play.png';
 import iconPause from './images/icon-pause.png';
@@ -40,29 +48,26 @@ import MEmpty from '@/components/m-empty';
 import dayjs from 'dayjs';
 import MVideo from '@/components/m-video';
 import ShareModel from './share-model';
-import { usePageVisibility } from '@vant/use';
-import videoBg from './images/video-bg.png';
+import { usePageVisibility, useEventListener } from '@vant/use';
+import "plyr/dist/plyr.css";
+import Plyr from "plyr";
+import { Vue3Lottie } from "vue3-lottie";
+import audioBga from "./images/audioBga.json";
+import videobg from "./images/videobg.png";
 
 export default defineComponent({
   name: 'creation-detail',
   setup() {
     const route = useRoute();
     const router = useRouter();
-    const audioId = 'a' + +Date.now() + Math.floor(Math.random() * 100);
-
+    const isScreenScroll = ref(false)
+    const lottieDom = ref()
     const state = reactive({
       id: route.query.id,
       deleteStatus: false,
       shareStatus: false,
       playType: '' as 'Audio' | 'Video' | '', // 播放类型
       musicDetail: {} as any,
-      timer: null as any,
-      audioWidth: 0,
-      paused: true,
-      currentTime: 0,
-      duration: 0.1,
-      loop: false,
-      dragStatus: false, // 是否开始拖动
       isClick: false,
       list: [] as any,
       listState: {
@@ -73,36 +78,17 @@ export default defineComponent({
       params: {
         page: 1,
         rows: 20
-      }
+      },
+      _plrl: null as any,
+      heightV:0,
+      heightB:0
     });
-    const audioDom = new Audio();
-    audioDom.controls = true;
-    audioDom.style.width = '100%';
-    audioDom.className = styles.audio;
-
-    /** 改变播放时间 */
-    const handleChangeTime = (val: number) => {
-      state.currentTime = val;
-      clearTimeout(state.timer);
-      state.timer = setTimeout(() => {
-        // audioRef.value.currentTime = val;
-        audioDom.currentTime = val;
-        state.timer = null;
-      }, 60);
-    };
-
-    // 切换音频播放
-    const onToggleAudio = (e: any) => {
-      e.stopPropagation();
-      if (audioDom.paused) {
-        audioDom.play();
-      } else {
-        audioDom.pause();
-      }
-
-      state.paused = audioDom.paused;
-    };
-
+    const plyrState = reactive({
+      duration: 0,
+      currentTime: 0,
+      mediaTimeShow: false,
+      playIngShow: true
+    })
     // 获取列表
     const getStarList = async () => {
       try {
@@ -129,70 +115,10 @@ export default defineComponent({
         state.isClick = false;
       }
     };
-
-    const initAudio = () => {
-      audioDom.src = state.musicDetail.videoUrl;
-      audioDom.load();
-      audioDom.oncanplaythrough = () => {
-        state.paused = audioDom.paused;
-        state.duration = audioDom.duration;
-      };
-      // 播放时监听
-      audioDom.addEventListener('timeupdate', () => {
-        state.duration = audioDom.duration;
-        state.currentTime = audioDom.currentTime;
-        const rate = (state.currentTime / state.duration) * 100;
-        state.audioWidth = rate > 100 ? 100 : rate;
-      });
-      audioDom.addEventListener('ended', () => {
-        state.paused = audioDom.paused;
-      });
-      // const wavesurfer = WaveSurfer.create({
-      //   container: document.querySelector(`#${audioId}`) as HTMLElement,
-      //   waveColor: '#fff',
-      //   progressColor: '#2FA1FD',
-      //   url: state.musicDetail.videoUrl,
-      //   cursorWidth: 0,
-      //   height: 35,
-      //   width: 'auto',
-      //   normalize: true,
-      //   // Set a bar width
-      //   barWidth: 2,
-      //   // Optionally, specify the spacing between bars
-      //   barGap: 2,
-      //   // And the bar radius
-      //   barRadius: 4,
-      //   barHeight: 0.6,
-      //   autoScroll: true,
-      //   /** If autoScroll is enabled, keep the cursor in the center of the waveform during playback */
-      //   autoCenter: true,
-      //   hideScrollbar: false,
-      //   media: audioDom
-      // });
-
-      // wavesurfer.once('interaction', () => {
-      //   // wavesurfer.play();
-      // });
-      // wavesurfer.once('ready', () => {
-      //   state.paused = audioDom.paused;
-      //   state.duration = audioDom.duration;
-      // });
-
-      // wavesurfer.on('finish', () => {
-      //   state.paused = true;
-      // });
-
-      // // 播放时监听
-      // audioDom.addEventListener('timeupdate', () => {
-      //   state.currentTime = audioDom.currentTime;
-      // });
-    };
-
     // 删除作品
     const onDelete = async () => {
       try {
         await api_userMusicRemove({ id: state.id });
-
         setTimeout(() => {
           state.deleteStatus = false;
           showToast('删除成功');
@@ -221,17 +147,184 @@ export default defineComponent({
         }
       });
     };
-
-    const pageVisibility = usePageVisibility();
-    watch(pageVisibility, value => {
-      if (value === 'hidden') {
-        if (audioDom) {
-          audioDom.pause();
-          state.paused = audioDom.paused;
+    // 滚动事件
+    const cleanScrollEvent = useEventListener('scroll', () => {
+      const height =
+        window.scrollY ||
+        document.documentElement.scrollTop
+        // 防止多次调用
+        if(height > 0 && isScreenScroll.value === false){
+          isScreenScroll.value = true
+          setStatusBarTextColor(false)
+        }
+        if(height <= 0){
+          isScreenScroll.value = false
+          setStatusBarTextColor(true)
         }
+    })
+    // 设置导航栏颜色
+    function setStatusBarTextColor(isWhite:boolean){
+      postMessage({
+        api: 'setStatusBarTextColor',
+        content: { statusBarTextColor: isWhite }
+      })
+    }
+    // 初始化 媒体播放
+    function initMediaPlay(){
+      const id = state.playType === "Audio" ? "#audioMediaSrc" : "#videoMediaSrc";
+      state._plrl = new Plyr(id, {
+        controls: ["progress"],
+        fullscreen: { enabled: false },
+      });
+      const player = state._plrl
+        // 创建音波数据
+      if(state.playType === "Audio"){
+        setTimeout(() => {
+          const audioDom = document.querySelector("#audioMediaSrc") as HTMLAudioElement
+          const canvasDom = document.querySelector("#audioVisualizer") as HTMLCanvasElement
+          const { pauseVisualDraw, playVisualDraw } = audioVisualDraw(audioDom, canvasDom)
+          player.on('play', () => {
+            lottieDom.value.play()
+            playVisualDraw()
+          });
+          player.on('pause', () => {
+            lottieDom.value.pause()
+            pauseVisualDraw()
+          });
+        }, 300); // 弹窗动画是0.25秒 这里用定时器 确保canvas 能获取到宽高
+      }
+      player.on("timeupdate", ()=>{
+        plyrState.currentTime = player.currentTime
+      })
+      player.on('play', () => {
+        plyrState.playIngShow = false
+      });
+      player.on('pause', () => {
+        plyrState.playIngShow = true
+      });
+      // 处理按压事件
+      const handleStart = () => {
+        plyrState.duration = player.duration
+        plyrState.mediaTimeShow = true
+      };
+      // 处理松开事件
+      const handleEnd = () => {
+        plyrState.mediaTimeShow = false
+      };
+      const progressDom = document.querySelector("#playMediaSection .plyr__controls .plyr__progress__container") as HTMLElement
+      progressDom.addEventListener('mousedown', handleStart);
+      progressDom.addEventListener('touchstart', handleStart);
+      progressDom.addEventListener('mouseup', handleEnd);
+      progressDom.addEventListener('touchend', handleEnd);
+    }
+    //点击改变播放状态
+    function handlerClickPlay(){
+      if (state._plrl.playing) {
+        state._plrl.pause();
+      } else {
+        state._plrl.play();
       }
+    }
+    /**
+		 * 音频可视化
+		 * @param audioDom
+		 * @param canvasDom
+		 * @param fftSize  2的幂数,最小为32
+		 */
+		function audioVisualDraw(audioDom: HTMLAudioElement, canvasDom: HTMLCanvasElement, fftSize = 128) {
+			type propsType = { canvWidth: number; canvHeight: number; canvFillColor: string; lineColor: string; lineGap: number }
+			// canvas
+			const canvasCtx = canvasDom.getContext("2d")!
+			const { width, height } = canvasDom.getBoundingClientRect()
+			canvasDom.width = width
+			canvasDom.height = height
+			// audio
+			const audioCtx = new AudioContext()
+			const analyser = audioCtx.createAnalyser()
+			const source = audioCtx.createMediaElementSource(audioDom)
+			analyser.fftSize = fftSize
+			source.connect(analyser)
+			analyser.connect(audioCtx.destination)
+			const dataArray = new Uint8Array(analyser.frequencyBinCount)
+			const draw = (data: Uint8Array, ctx: CanvasRenderingContext2D, { lineGap, canvWidth, canvHeight, canvFillColor, lineColor }: propsType) => {
+				if (!ctx) return
+				const w = canvWidth
+				const h = canvHeight
+				fillCanvasBackground(ctx, w, h, canvFillColor)
+			   // 可视化
+				const dataLen = data.length
+				const step = (w / 2 - lineGap * dataLen) / dataLen
+				const midX = w / 2
+				const midY = h / 2
+				let xLeft = midX
+				for (let i = 0; i < dataLen; i++) {
+					const value = data[i]
+					const percent = value / 255 // 最大值为255
+					const barHeight = percent * midY
+					canvasCtx.fillStyle = lineColor
+					// 中间加间隙
+					if (i === 0) {
+						xLeft -= lineGap / 2
+					}
+					canvasCtx.fillRect(xLeft - step, midY - barHeight, step, barHeight)
+					canvasCtx.fillRect(xLeft - step, midY, step, barHeight)
+					xLeft -= step + lineGap
+				}
+				let xRight = midX
+				for (let i = 0; i < dataLen; i++) {
+					const value = data[i]
+					const percent = value / 255 // 最大值为255
+					const barHeight = percent * midY
+					canvasCtx.fillStyle = lineColor
+					if (i === 0) {
+						xRight += lineGap / 2
+					}
+					canvasCtx.fillRect(xRight, midY - barHeight, step, barHeight)
+					canvasCtx.fillRect(xRight, midY, step, barHeight)
+					xRight += step + lineGap
+				}
+			}
+			const fillCanvasBackground = (ctx: CanvasRenderingContext2D, w: number, h: number, colors: string) => {
+				ctx.clearRect(0, 0, w, h)
+				ctx.fillStyle = colors
+				ctx.fillRect(0, 0, w, h)
+			}
+			const requestAnimationFrameFun = () => {
+				requestAnimationFrame(() => {
+					analyser.getByteFrequencyData(dataArray)
+					draw(dataArray, canvasCtx, {
+						lineGap: 2,
+						canvWidth: width,
+						canvHeight: height,
+						canvFillColor: "transparent",
+						lineColor: "rgba(255, 255, 255, 0.7)"
+					})
+					if (!isPause) {
+						requestAnimationFrameFun()
+					}
+				})
+			}
+			let isPause = true
+			const playVisualDraw = () => {
+				isPause = false
+				audioCtx.resume()
+				requestAnimationFrameFun()
+			}
+			const pauseVisualDraw = () => {
+				isPause = true
+				audioCtx.suspend()
+			}
+			return {
+				playVisualDraw,
+				pauseVisualDraw
+			}
+		}
+    const pageVisibility = usePageVisibility();
+    watch(pageVisibility, value => {
+      if (value === 'hidden') {}
     });
     onMounted(async () => {
+      setStatusBarTextColor(true)
       try {
         const res = await api_userMusicDetail(state.id);
         // console.log(res);
@@ -259,161 +352,127 @@ export default defineComponent({
           state.playType = 'Video';
         } else {
           state.playType = 'Audio';
-          // 初始化
-          nextTick(() => {
-            initAudio();
-          });
         }
+        nextTick(()=>{
+          initMediaPlay()
+        })
       } catch {
         //
       }
     });
 
     onUnmounted(() => {
-      if (audioDom) {
-        audioDom.pause();
-        state.paused = audioDom.paused;
-      }
+      setStatusBarTextColor(false)
+      cleanScrollEvent()
     });
     return () => (
       <div
+        style={
+          {
+            '--barheight':state.heightV + "px"
+          }
+        }
         class={[
           styles.creation,
-          browser().isTablet ? styles.creationTablet : ''
+          browser().isTablet && styles.creationTablet,
+          isScreenScroll.value && styles.isScreenScroll
         ]}>
-        <MSticky position="top">
+        <div class={styles.creationBg}></div>
+        <MSticky position="top"
+          onBarHeight={(height: any) => {
+            console.log(height, 'height', height)
+            state.heightV = height
+          }}
+        >
           <MHeader
+            color={isScreenScroll.value ? "#333333" : "#ffffff"}
+            background={isScreenScroll.value ? `rgb(255,255,255` : "transparent"}
+            title={state.musicDetail?.musicSheetName}
             border={false}
             isBack={route.query.platformType != 'ANALYSIS'}
           />
         </MSticky>
-        <div class={styles.playSection}>
-          {state.playType === 'Video' && (
-            <MVideo
-              class={styles.videoSection}
-              src={state.musicDetail?.videoUrl}
-              poster={state.musicDetail?.videoImg || videoBg}
-            />
-          )}
-          {state.playType === 'Audio' && (
-            <div class={styles.audioSection}>
-              <div class={styles.audioContainer}>
-                {/* <div
-                  id={audioId}
-                  onClick={(e: MouseEvent) => {
-                    e.stopPropagation();
-                  }}></div> */}
-                <div
-                  class={styles.waveActive}
-                  style={{
-                    width: state.audioWidth + '%'
-                  }}></div>
-                <div class={styles.waveDefault}></div>
-              </div>
-
+        <div class={styles.singer}>
+          演奏:{state.musicDetail?.username}
+        </div>
+        <Sticky offsetTop={state.heightV - 1 + "px"}>
+          <div class={[styles.playSection, plyrState.mediaTimeShow && styles.mediaTimeShow]} id="playMediaSection" onClick={handlerClickPlay}>
+            {
+              state.playType === 'Audio' &&
               <div class={styles.audioBox}>
-                <div
-                  class={[styles.audioPan, state.paused && styles.imgRotate]}>
-                  <Image class={styles.audioImg} src={state.musicDetail?.img} />
-                </div>
-                <i class={styles.audioPoint}></i>
-                <i
-                  class={[styles.audioZhen, state.paused && styles.active]}></i>
+                <canvas class={styles.audioVisualizer} id="audioVisualizer"></canvas>
+                <Vue3Lottie ref={lottieDom} class={styles.audioBga} animationData={audioBga} autoPlay={false} loop={true}></Vue3Lottie>
+                <audio
+                  crossorigin="anonymous"
+                  id="audioMediaSrc"
+                  src={state.musicDetail?.videoUrl}
+                  controls="false"
+                  preload="metadata"
+                  playsinline
+                />
               </div>
-              <div
-                class={[styles.controls]}
-                onClick={(e: Event) => {
-                  e.stopPropagation();
-                }}
-                onTouchmove={(e: TouchEvent) => {
-                  // emit('close');
-                }}>
-                <div class={styles.actions}>
-                  <div class={styles.actionBtn} onClick={onToggleAudio}>
-                    <img src={state.paused ? iconPlay : iconPause} />
-                  </div>
-                </div>
-                <div class={[styles.slider]}>
-                  <Slider
-                    step={0.01}
-                    class={styles.timeProgress}
-                    v-model={state.currentTime}
-                    max={state.duration}
-                    onUpdate:modelValue={val => {
-                      handleChangeTime(val);
-                    }}
-                    onDragStart={() => {
-                      state.dragStatus = true;
-                      console.log('onDragStart');
-                    }}
-                    onDragEnd={() => {
-                      state.dragStatus = false;
-                      console.log('onDragEnd');
-                    }}
-                  />
-                </div>
-                <div class={styles.time}>
-                  <div>{getSecondRPM(state.currentTime)}</div>
-                  <span>/</span>
-                  <div>{getSecondRPM(state.duration)}</div>
-                </div>
+            }
+            {
+              state.playType === 'Video' &&
+              <video
+                id="videoMediaSrc"
+                class={styles.videoBox}
+                src={state.musicDetail?.videoUrl}
+                data-poster={videobg}
+                preload="metadata"
+                playsinline
+              />
+            }
+            <div class={[styles.playLarge, plyrState.playIngShow && styles.playIngShow]}></div>
+            <div class={styles.mediaTime}>
+              <div>
+                {getSecondRPM(plyrState.currentTime)}
+              </div>
+              <div class={styles.note}>/</div>
+              <div class={styles.duration}>
+                {getSecondRPM(plyrState.duration)}
               </div>
             </div>
-          )}
-        </div>
-
-        <Cell class={styles.userSection} center border={false}>
-          {{
-            icon: () => (
+          </div>
+        </Sticky>
+        <div class={styles.musicSection}>
+          <div class={styles.avatarInfoBox}>
+            <div class={styles.avatar}>
               <Image class={styles.userLogo} src={state.musicDetail.avatar} />
-            ),
-            title: () => (
-              <div class={styles.userInfo}>
-                <p class={styles.name}>
-                  <span>{state.musicDetail?.username}</span>
+              <div class={styles.infoCon}>
+                <div class={styles.info}>
+                  <span class={styles.userName}>{state.musicDetail?.username}</span>
                   {state.musicDetail.vipFlag && (
                     <img src={iconMember} class={styles.iconMember} />
                   )}
-                </p>
-                <p class={styles.sub}>
+                </div>
+                <div class={styles.sub}>
                   {state.musicDetail.subjectName}{' '}
                   {getGradeCh(state.musicDetail.currentGradeNum - 1)}
-                </p>
-              </div>
-            ),
-            value: () => (
-              <div class={[styles.zan, styles.zanActive]}>
-                <img src={iconZanActive} class={styles.iconZan} />
-                {state.musicDetail.likeNum}
+                </div>
               </div>
-            )
-          }}
-        </Cell>
-
-        <div class={styles.musicSection}>
-          <div class={styles.musicName}>
-            <span class={styles.musicTag}>曲目名称</span>
-            {state.musicDetail?.musicSheetName}
+            </div>
+            <div class={styles.linkes}>
+              <img src={iconZan} class={styles.iconZan} />
+              <span>{state.musicDetail.likeNum}</span>
+            </div>
           </div>
-          {state.musicDetail.desc && (
-            <div class={styles.musicDesc}>{state.musicDetail.desc}</div>
-          )}
+          <TextEllipsis class={styles.textEllipsis} rows={2} content={state.musicDetail?.desc} expand-text="展开" collapse-text="收起" />
         </div>
 
         <div class={styles.likeSection}>
           <div class={styles.likeTitle}>点赞记录</div>
-
           {state.listState.dataShow ? (
             <List
               finished={state.listState.finished}
               finishedText=" "
-              class={[styles.container, styles.containerInformation]}
               onLoad={getStarList}
               immediateCheck={false}>
               {state.list.map((item: any, index: number) => (
                 <Cell
-                  class={styles.likeItem}
-                  border={state.list.length - 1 == index ? false : true}>
+                  class={[styles.likeItem, index===state.list.length-1&&styles.likeItemLast]}
+                  border={false}
+                >
                   {{
                     icon: () => (
                       <Image src={item.userAvatar} class={styles.userLogo} />
@@ -437,11 +496,21 @@ export default defineComponent({
               ))}
             </List>
           ) : (
-            <MEmpty description="暂无数据" />
+            <MEmpty class={styles.mEmpty} description="暂无数据" />
           )}
         </div>
-
-        <MSticky position="bottom">
+        {
+          !isScreenScroll.value &&
+          <MSticky position="bottom" offsetBottom={state.heightB - 1 + "px"} >
+            <div class={styles.upward}>
+              <img src={iconUpward} />
+            </div>
+          </MSticky>
+        }
+        <MSticky position="bottom" onBarHeight={(height: any) => {
+            console.log(height, 'height', height)
+            state.heightB = height
+        }}>
           <div class={styles.bottomSection}>
             <div class={styles.bottomShare}>
               <p onClick={onDownload}>
@@ -457,10 +526,8 @@ export default defineComponent({
                 <span>删除</span>
               </p>
             </div>
-            <Button
-              round
+            <img src={iconEdit}
               class={styles.btnEdit}
-              type="primary"
               onClick={() => {
                 router.push({
                   path: '/creation-edit',
@@ -468,25 +535,28 @@ export default defineComponent({
                     id: state.id
                   }
                 });
-              }}>
-              编辑
-            </Button>
+              }}
+            />
           </div>
         </MSticky>
 
         <Popup
           v-model:show={state.deleteStatus}
+          overlay-style={
+            {
+              backgroundColor:"rgba(0,0,0,.5)"
+            }
+          }
           round
           class={styles.popupContainer}>
-          <p class={styles.popupContent}>确定删除吗?</p>
-          <div class={styles.popupBtnGroup}>
-            <Button round onClick={() => (state.deleteStatus = false)}>
-              取消
-            </Button>
-            <Button round type="primary" onClick={onDelete}>
-              确定
-            </Button>
-          </div>
+            <img class={styles.prompt} src={promptImg} />
+            <div class={styles.deleteBox}>
+              <p class={styles.popupContent}>确定删除吗?</p>
+              <div class={styles.popupBtnGroup}>
+                <img src={canceImg} onClick={() => (state.deleteStatus = false)} />
+                <img src={confirmImg} onClick={onDelete} />
+              </div>
+            </div>
         </Popup>
 
         <Popup
@@ -494,6 +564,7 @@ export default defineComponent({
           v-model:show={state.shareStatus}
           style={{ background: 'transparent' }}>
           <ShareModel
+            playType={state.playType}
             musicDetail={state.musicDetail}
             onClose={() => (state.shareStatus = false)}
           />

BIN
src/views/creation/share-model/images/audio-share-bg.png


BIN
src/views/creation/share-model/images/audioLabel.png


BIN
src/views/creation/share-model/images/share-bg.png


BIN
src/views/creation/share-model/images/video-share-bg.png


BIN
src/views/creation/share-model/images/videoLabel.png


+ 16 - 7
src/views/creation/share-model/index.module.less

@@ -68,7 +68,14 @@
       width: 56px;
       height: 56px;
     }
-
+    .imgLabel{
+      position: absolute;
+      right: 0;
+      top: 0;
+      width: 28px;
+      height: 14px;
+      z-index: 10;
+    }
 
   }
 
@@ -96,7 +103,6 @@
 
 .downloadSection {
   display: flex;
-  align-items: center;
   margin: 0 20px;
   position: relative;
   z-index: 3;
@@ -135,17 +141,20 @@
     margin-left: 10px;
     padding-left: 9px;
     border-left: 1px dashed #D8D8D8;
-
+    font-weight: 500;
+    font-size: 12px;
+    color: #28A0FE;
+    line-height: 18px;
+    padding-top: 5px;
     .tip {
-      font-size: 12px;
       color: #777777;
-      line-height: 17px;
     }
 
     .iconLogo {
       width: 102px;
       height: 20px;
-      margin: 12px 0 4px;
+      margin: 5px 0 0;
+      display: block;
     }
 
     .downTip {
@@ -192,4 +201,4 @@
   color: #AAAAAA;
   line-height: 55px;
   text-align: center;
-}
+}

+ 12 - 3
src/views/creation/share-model/index.tsx

@@ -18,7 +18,10 @@ import iconWeChat from './images/icon-wechat.png';
 import iconFriendRing from './images/icon-friend-ring.png';
 import iconLink from './images/icon-link.png';
 import iconLogo from './images/icon-logo.png';
-import shareBg from './images/share-bg.png';
+import audioLabel from './images/audioLabel.png';
+import videoLabel from './images/videoLabel.png';
+import videoShareBg from './images/video-share-bg.png';
+import audioShareBg from './images/audio-share-bg.png';
 import audioPan from '../images/audio-pan.png';
 import smallLogo from '@common/images/logo.png';
 import musicBg from './images/music-bg.png';
@@ -32,6 +35,10 @@ export default defineComponent({
     musicDetail: {
       type: Object,
       default: () => {}
+    },
+    playType:{
+      type: String,
+      default: ''
     }
   },
   emits: ['close'],
@@ -191,7 +198,7 @@ export default defineComponent({
     return () => (
       <div class={styles.shareModel}>
         <div class={styles.shareContent} id="shareContent">
-          <img src={shareBg} class={styles.shareBg} />
+          <img src={props.playType === "Audio" ? audioShareBg : videoShareBg} class={styles.shareBg} />
           <div class={styles.share_content__title}>
             <p class={styles.large}>我在音乐数字课堂发布了演奏作品</p>
             <p class={styles.small}>赶快扫码看看吧!</p>
@@ -213,6 +220,7 @@ export default defineComponent({
                 class={styles.muploader}
                 crossorigin="anonymous"
               />
+              <img class={styles.imgLabel} src={props.playType === "Audio" ? audioLabel : videoLabel} />
             </div>
             <div class={styles.musicDetail}>
               <p class={[styles.musicName, 'van-ellipsis']}>
@@ -230,8 +238,9 @@ export default defineComponent({
               <img src={smallLogo} class={styles.qrcodeLogo} />
             </div>
             <div class={styles.qrtips}>
+              <div>温馨提示:</div>
               <p class={styles.tip}>
-                温馨提示:保存图片到相册或长按识别二维码进入查看喔
+                保存图片到相册后,请在微信里扫码查看
               </p>
               <img src={iconLogo} class={styles.iconLogo} />
               {/* <p class={styles.downTip}>扫码下载音乐数字课堂App</p> */}

Vissa filer visades inte eftersom för många filer har ändrats