Browse Source

增加谱面

黄琪勇 11 months ago
parent
commit
adf3f46ddb

+ 126 - 9
src/views/creation/index-share.tsx

@@ -23,7 +23,7 @@ import audioLabel from './share-model/images/audioLabel.png';
 import videoLabel from './share-model/images/videoLabel.png';
 import videoLabel from './share-model/images/videoLabel.png';
 import musicBg from './share-model/images/music-bg.png';
 import musicBg from './share-model/images/music-bg.png';
 import playImg from './images/play.png';
 import playImg from './images/play.png';
-import { browser, getGradeCh, getSecondRPM } from '@/helpers/utils';
+import { browser, getGradeCh, getSecondRPM, vaildMusicScoreUrl } from '@/helpers/utils';
 import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
 import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
 import {
 import {
   api_openUserMusicDetail,
   api_openUserMusicDetail,
@@ -90,6 +90,14 @@ export default defineComponent({
       playIngShow: true,
       playIngShow: true,
       loaded:false
       loaded:false
     })
     })
+    // 谱面
+    const staffState = reactive({
+      staffSrc: "",
+      isShow: false,
+      height:"initial"
+    })
+    const staffDom= ref<HTMLIFrameElement>()
+    const {playStaff, pauseStaff, updateProgressStaff} = staffMoveInstance()
     // 点赞
     // 点赞
     const onStarChange = async () => {
     const onStarChange = async () => {
       await checkLogin();
       await checkLogin();
@@ -183,9 +191,11 @@ export default defineComponent({
       })
       })
       player.on('play', () => {
       player.on('play', () => {
         plyrState.playIngShow = false
         plyrState.playIngShow = false
+        playStaff()
       });
       });
       player.on('pause', () => {
       player.on('pause', () => {
         plyrState.playIngShow = true
         plyrState.playIngShow = true
+        pauseStaff()
       });
       });
       // 处理按压事件
       // 处理按压事件
       const handleStart = () => {
       const handleStart = () => {
@@ -195,6 +205,10 @@ export default defineComponent({
       // 处理松开事件
       // 处理松开事件
       const handleEnd = () => {
       const handleEnd = () => {
         plyrState.mediaTimeShow = false
         plyrState.mediaTimeShow = false
+        // 暂停的时候调用
+        if(!player.playing){
+          updateProgressStaff(player.currentTime)
+        }
       };
       };
       const progressDom = document.querySelector("#playMediaSection .plyr__controls .plyr__progress__container") as HTMLElement
       const progressDom = document.querySelector("#playMediaSection .plyr__controls .plyr__progress__container") as HTMLElement
       progressDom.addEventListener('mousedown', handleStart);
       progressDom.addEventListener('mousedown', handleStart);
@@ -292,20 +306,20 @@ export default defineComponent({
         if (!audioCtx) {
         if (!audioCtx) {
           audioCtx = new AudioContext()
           audioCtx = new AudioContext()
           source = audioCtx.createMediaElementSource(audioDom)
           source = audioCtx.createMediaElementSource(audioDom)
+          analyser = audioCtx.createAnalyser()
+          analyser.fftSize = fftSize
+          source?.connect(analyser)
+          analyser.connect(audioCtx.destination)
         }
         }
-        analyser = audioCtx.createAnalyser()
-        analyser.fftSize = fftSize
-        source?.connect(analyser)
-        analyser.connect(audioCtx.destination)
-        audioCtx.resume()
+        //audioCtx.resume()  // 重新更新状态   加了暂停和恢复音频音质发生了变化  所以这里取消了
         isPause = false
         isPause = false
         requestAnimationFrameFun()
         requestAnimationFrameFun()
       }
       }
       const pauseVisualDraw = () => {
       const pauseVisualDraw = () => {
         isPause = true
         isPause = true
-        audioCtx?.suspend()
-        source?.disconnect()
-        analyser?.disconnect()
+        //audioCtx?.suspend()  // 暂停   加了暂停和恢复音频音质发生了变化  所以这里取消了
+        // source?.disconnect()
+        // analyser?.disconnect()
       }
       }
       return {
       return {
         playVisualDraw,
         playVisualDraw,
@@ -320,6 +334,7 @@ export default defineComponent({
           resourceUrl:encodeURIComponent(state.musicDetail?.videoUrl),
           resourceUrl:encodeURIComponent(state.musicDetail?.videoUrl),
           musicSheetName:encodeURIComponent(state.musicDetail?.musicSheetName),
           musicSheetName:encodeURIComponent(state.musicDetail?.musicSheetName),
           username:encodeURIComponent(state.musicDetail?.username),
           username:encodeURIComponent(state.musicDetail?.username),
+          musicSheetId:encodeURIComponent(state.musicDetail?.musicSheetId)
         }
         }
       })
       })
     }
     }
@@ -356,6 +371,8 @@ export default defineComponent({
           return;
           return;
         } else {
         } else {
           state.musicDetail = res.data;
           state.musicDetail = res.data;
+          // 五线谱
+          initStaff()
           getList();
           getList();
           // 判断是视频还是音频
           // 判断是视频还是音频
           if (res.data.videoUrl.lastIndexOf('mp4') !== -1) {
           if (res.data.videoUrl.lastIndexOf('mp4') !== -1) {
@@ -398,6 +415,83 @@ export default defineComponent({
         state._plrl?.pause();
         state._plrl?.pause();
       }
       }
     });
     });
+    // 初始化五线谱
+    function initStaff(){
+      //const src = `${vaildMusicScoreUrl()}/instrument/#/simple-detail?id=${state.musicDetail.musicSheetId}&musicRenderType=staff`;
+      const src = `http://192.168.3.68:3000/instrument.html#/simple-detail?id=${state.musicDetail.musicSheetId}&musicRenderType=staff`;
+      staffState.staffSrc = src
+      window.addEventListener('message', (event) => {
+        const { api, height } = event.data;
+        if (api === 'api_musicPage') {
+          staffState.isShow = true
+          staffState.height = height + "px"
+          // 如果是播放中自动开始 播放
+          if(state._plrl.playing){
+            playStaff()
+          }
+        }
+      });
+    }
+    function staffMoveInstance(){
+      let isPause = true
+      const requestAnimationFrameFun = () => {
+        requestAnimationFrame(() => {
+          staffDom.value?.contentWindow?.postMessage(
+            {
+              api: 'api_playProgress',
+              content: {
+                currentTime: state._plrl.currentTime
+              }
+            },
+            "*"
+          )
+          if (!isPause) {
+            requestAnimationFrameFun()
+          }
+        })
+      }
+      const playStaff = () => {
+        // 没渲染不执行
+        if(!staffState.isShow) return
+        isPause = false
+        staffDom.value?.contentWindow?.postMessage(
+          {
+            api: 'api_play'
+          },
+          "*"
+        )
+        requestAnimationFrameFun()
+      }
+      const pauseStaff = () => {
+        // 没渲染不执行
+        if(!staffState.isShow) return
+        isPause = true
+        staffDom.value?.contentWindow?.postMessage(
+          {
+            api: 'api_paused'
+          },
+          "*"
+        )
+      }
+      const updateProgressStaff = (currentTime: string) => {
+        // 没渲染不执行
+        if(!staffState.isShow) return
+        staffDom.value?.contentWindow?.postMessage(
+          {
+            api: 'api_updateProgress',
+            content: {
+              currentTime: state._plrl.currentTime
+            }
+          },
+          "*"
+        )
+      }
+      return {
+        playStaff,
+        pauseStaff,
+        updateProgressStaff
+      }
+    }
     onMounted(async () => {
     onMounted(async () => {
       __init();
       __init();
     });
     });
@@ -414,6 +508,9 @@ export default defineComponent({
       if(state._plrl){
       if(state._plrl){
         state._plrl.destroy()
         state._plrl.destroy()
       }
       }
+      staffState.staffSrc = ""
+      staffState.isShow = false
+      staffState.height = "initial"
       __init();
       __init();
     });
     });
     return () => (
     return () => (
@@ -496,6 +593,26 @@ export default defineComponent({
                       </div>
                       </div>
                     </div>
                     </div>
                     <div class={styles.landscapeScreen} onClick={handlerLandscapeScreen}></div>
                     <div class={styles.landscapeScreen} onClick={handlerLandscapeScreen}></div>
+                    {/* 谱面 */}
+                    {
+                      staffState.staffSrc &&
+                      <div
+                        class={[styles.staffBox, staffState.isShow && styles.staffBoxShow]}
+                        style={
+                          {
+                            '--staffBoxHeight':staffState.height
+                          }
+                        }
+                      >
+                        <div class={styles.mask}></div>
+                        <iframe
+                          ref={staffDom}
+                          class={styles.staff}
+                          frameborder="0"
+                          src={staffState.staffSrc}>
+                        </iframe>
+                      </div>
+                    }
                   </>
                   </>
                 }
                 }
               </div>
               </div>

+ 219 - 99
src/views/creation/index.tsx

@@ -37,7 +37,7 @@ import iconZanActive from './images/icon-zan-active.png';
 import iconPlay from './images/icon-play.png';
 import iconPlay from './images/icon-play.png';
 import iconPause from './images/icon-pause.png';
 import iconPause from './images/icon-pause.png';
 import { postMessage, promisefiyPostMessage } from '@/helpers/native-message';
 import { postMessage, promisefiyPostMessage } from '@/helpers/native-message';
-import { browser, getGradeCh, getSecondRPM } from '@/helpers/utils';
+import { browser, getGradeCh, getSecondRPM, vaildMusicScoreUrl } from '@/helpers/utils';
 import { useRoute, useRouter } from 'vue-router';
 import { useRoute, useRouter } from 'vue-router';
 import {
 import {
   api_userMusicDetail,
   api_userMusicDetail,
@@ -89,6 +89,14 @@ export default defineComponent({
       mediaTimeShow: false,
       mediaTimeShow: false,
       playIngShow: true
       playIngShow: true
     })
     })
+    // 谱面
+    const staffState = reactive({
+      staffSrc: "",
+      isShow: false,
+      height:"initial"
+    })
+    const staffDom= ref<HTMLIFrameElement>()
+    const {playStaff, pauseStaff, updateProgressStaff} = staffMoveInstance()
     // 获取列表
     // 获取列表
     const getStarList = async () => {
     const getStarList = async () => {
       try {
       try {
@@ -179,28 +187,28 @@ export default defineComponent({
       const player = state._plrl
       const player = state._plrl
         // 创建音波数据
         // 创建音波数据
       if(state.playType === "Audio"){
       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 能获取到宽高
+        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()
+        });
       }
       }
       player.on("timeupdate", ()=>{
       player.on("timeupdate", ()=>{
         plyrState.currentTime = player.currentTime
         plyrState.currentTime = player.currentTime
       })
       })
       player.on('play', () => {
       player.on('play', () => {
         plyrState.playIngShow = false
         plyrState.playIngShow = false
+        playStaff()
       });
       });
       player.on('pause', () => {
       player.on('pause', () => {
         plyrState.playIngShow = true
         plyrState.playIngShow = true
+        pauseStaff()
       });
       });
       // 处理按压事件
       // 处理按压事件
       const handleStart = () => {
       const handleStart = () => {
@@ -210,6 +218,10 @@ export default defineComponent({
       // 处理松开事件
       // 处理松开事件
       const handleEnd = () => {
       const handleEnd = () => {
         plyrState.mediaTimeShow = false
         plyrState.mediaTimeShow = false
+        // 暂停的时候调用
+        if(!player.playing){
+          updateProgressStaff(player.currentTime)
+        }
       };
       };
       const progressDom = document.querySelector("#playMediaSection .plyr__controls .plyr__progress__container") as HTMLElement
       const progressDom = document.querySelector("#playMediaSection .plyr__controls .plyr__progress__container") as HTMLElement
       progressDom.addEventListener('mousedown', handleStart);
       progressDom.addEventListener('mousedown', handleStart);
@@ -231,104 +243,190 @@ export default defineComponent({
 		 * @param canvasDom
 		 * @param canvasDom
 		 * @param fftSize  2的幂数,最小为32
 		 * @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 = () => {
+    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
+      let audioCtx : AudioContext | null = null
+      let analyser : AnalyserNode | null = null
+      let source : MediaElementAudioSourceNode | null = null
+      const dataArray = new Uint8Array(fftSize / 2)
+      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
+        let step = (w / 2 - lineGap * dataLen) / dataLen
+        step < 1 && (step = 1)
+        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 = () => {
+        if (!audioCtx) {
+          audioCtx = new AudioContext()
+          source = audioCtx.createMediaElementSource(audioDom)
+          analyser = audioCtx.createAnalyser()
+          analyser.fftSize = fftSize
+          source?.connect(analyser)
+          analyser.connect(audioCtx.destination)
+        }
+        //audioCtx.resume()  // 重新更新状态   加了暂停和恢复音频音质发生了变化  所以这里取消了
+        isPause = false
+        requestAnimationFrameFun()
+      }
+      const pauseVisualDraw = () => {
+        isPause = true
+        //audioCtx?.suspend()  // 暂停   加了暂停和恢复音频音质发生了变化  所以这里取消了
+        // source?.disconnect()
+        // analyser?.disconnect()
+      }
+      return {
+        playVisualDraw,
+        pauseVisualDraw
+      }
+    }
+    function handlerLandscapeScreen(event:any){
+      event.stopPropagation()
+      router.push({
+        path:"/playCreation",
+        query:{
+          resourceUrl:encodeURIComponent(state.musicDetail?.videoUrl),
+          musicSheetName:encodeURIComponent(state.musicDetail?.musicSheetName),
+          username:encodeURIComponent(state.musicDetail?.username),
+          musicSheetId:encodeURIComponent(state.musicDetail?.musicSheetId)
+        }
+      })
+    }
+    // 初始化五线谱
+    function initStaff(){
+      //const src = `${vaildMusicScoreUrl()}/instrument/#/simple-detail?id=${state.musicDetail.musicSheetId}&musicRenderType=staff`;
+      const src = `http://192.168.3.68:3000/instrument.html#/simple-detail?id=${state.musicDetail.musicSheetId}&musicRenderType=staff`;
+      staffState.staffSrc = src
+      window.addEventListener('message', (event) => {
+        const { api, height } = event.data;
+        if (api === 'api_musicPage') {
+          staffState.isShow = true
+          staffState.height = height + "px"
+          // 如果是播放中自动开始 播放
+          if(state._plrl.playing){
+            playStaff()
+          }
+        }
+      });
+    }
+    function staffMoveInstance(){
+      let isPause = true
+      const requestAnimationFrameFun = () => {
 				requestAnimationFrame(() => {
 				requestAnimationFrame(() => {
-					analyser.getByteFrequencyData(dataArray)
-					draw(dataArray, canvasCtx, {
-						lineGap: 2,
-						canvWidth: width,
-						canvHeight: height,
-						canvFillColor: "transparent",
-						lineColor: "rgba(255, 255, 255, 0.7)"
-					})
+          staffDom.value?.contentWindow?.postMessage(
+            {
+              api: 'api_playProgress',
+              content: {
+                currentTime: state._plrl.currentTime
+              }
+            },
+            "*"
+          )
 					if (!isPause) {
 					if (!isPause) {
 						requestAnimationFrameFun()
 						requestAnimationFrameFun()
 					}
 					}
 				})
 				})
 			}
 			}
-			let isPause = true
-			const playVisualDraw = () => {
+      const playStaff = () => {
+        // 没渲染不执行
+        if(!staffState.isShow) return
 				isPause = false
 				isPause = false
-				audioCtx.resume()
+        staffDom.value?.contentWindow?.postMessage(
+          {
+            api: 'api_play'
+          },
+          "*"
+        )
 				requestAnimationFrameFun()
 				requestAnimationFrameFun()
 			}
 			}
-			const pauseVisualDraw = () => {
+			const pauseStaff = () => {
+        // 没渲染不执行
+        if(!staffState.isShow) return
 				isPause = true
 				isPause = true
-				audioCtx.suspend()
+        staffDom.value?.contentWindow?.postMessage(
+          {
+            api: 'api_paused'
+          },
+          "*"
+        )
 			}
 			}
+      const updateProgressStaff = (currentTime: string) => {
+        // 没渲染不执行
+        if(!staffState.isShow) return
+        staffDom.value?.contentWindow?.postMessage(
+          {
+            api: 'api_updateProgress',
+            content: {
+              currentTime: state._plrl.currentTime
+            }
+          },
+          "*"
+        )
+      }
 			return {
 			return {
-				playVisualDraw,
-				pauseVisualDraw
+				playStaff,
+				pauseStaff,
+        updateProgressStaff
 			}
 			}
-		}
-    function handlerLandscapeScreen(event:any){
-      event.stopPropagation()
-      router.push({
-        path:"/playCreation",
-        query:{
-          resourceUrl:encodeURIComponent(state.musicDetail?.videoUrl),
-          musicSheetName:encodeURIComponent(state.musicDetail?.musicSheetName),
-          username:encodeURIComponent(state.musicDetail?.username),
-        }
-      })
     }
     }
     onMounted(async () => {
     onMounted(async () => {
       setStatusBarTextColor(true)
       setStatusBarTextColor(true)
@@ -353,6 +451,8 @@ export default defineComponent({
           return;
           return;
         }
         }
         state.musicDetail = res.data || {};
         state.musicDetail = res.data || {};
+        // 五线谱
+        initStaff()
         getStarList();
         getStarList();
         // 判断是视频还是音频
         // 判断是视频还是音频
         if (res.data.videoUrl.lastIndexOf('mp4') !== -1) {
         if (res.data.videoUrl.lastIndexOf('mp4') !== -1) {
@@ -441,6 +541,26 @@ export default defineComponent({
               </div>
               </div>
             </div>
             </div>
             <div class={styles.landscapeScreen} onClick={handlerLandscapeScreen}></div>
             <div class={styles.landscapeScreen} onClick={handlerLandscapeScreen}></div>
+            {/* 谱面 */}
+            {
+              staffState.staffSrc &&
+              <div
+                class={[styles.staffBox, staffState.isShow && styles.staffBoxShow]}
+                style={
+                  {
+                    '--staffBoxHeight':staffState.height
+                  }
+                }
+              >
+                <div class={styles.mask}></div>
+                <iframe
+                  ref={staffDom}
+                  class={styles.staff}
+                  frameborder="0"
+                  src={staffState.staffSrc}>
+                </iframe>
+              </div>
+            }
           </div>
           </div>
         </Sticky>
         </Sticky>
         <div class={styles.musicSection}>
         <div class={styles.musicSection}>

+ 37 - 10
src/views/creation/playCreation/index.module.less

@@ -27,18 +27,9 @@
       .plyr {
       .plyr {
           width: 100%;
           width: 100%;
           height: 100%;
           height: 100%;
-          .plyr__control.plyr__control--overlaid{
-              width: 48px;
-              height: 48px;
-              background: url("../images/midPlay.png") no-repeat;
-              background-size: 100% 100%;
-              .plyr__sr-only, svg{
-                  display: none;
-              }
-          }
           .plyr__controls{
           .plyr__controls{
               background: initial;
               background: initial;
-              padding: 0 20px 13px;
+              padding: 0 20px 20px;
               .plyr__controls__item.plyr__control{
               .plyr__controls__item.plyr__control{
                   padding: 0;
                   padding: 0;
                   width: 18px;
                   width: 18px;
@@ -134,6 +125,21 @@
           height: 66px;
           height: 66px;
       }
       }
   }
   }
+  .playLarge{
+    position: absolute;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+    width: 48px;
+    height: 48px;
+    background: url("../images/midPlay.png") no-repeat;
+    background-size: 100% 100%;
+    z-index: 12;
+    display: none;
+    &.playIngShow{
+      display: initial;
+    }
+  }
   .backBox{
   .backBox{
     position: absolute;
     position: absolute;
     left: 30px;
     left: 30px;
@@ -162,4 +168,25 @@
       }
       }
     }
     }
   }
   }
+  .staffBox{
+    width: 100%;
+    height: var(--staffBoxHeight);
+    position: absolute;
+    bottom: 44px;
+    visibility: hidden;
+    &.staffBoxShow{
+      visibility: initial;
+    }
+    .staff{
+      width: 100%;
+      height: 100%;
+      padding-left: 10px;
+    }
+    .mask{
+      position: absolute;
+      z-index: 6;
+      width: 100%;
+      height: 100%;
+    }
+  }
 }
 }

+ 155 - 15
src/views/creation/playCreation/index.tsx

@@ -1,6 +1,6 @@
 import { defineComponent, onMounted, reactive, ref, onUnmounted, watch } from 'vue';
 import { defineComponent, onMounted, reactive, ref, onUnmounted, watch } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
 import { useRoute, useRouter } from 'vue-router';
-import { browser } from "@/helpers/utils"
+import { browser, vaildMusicScoreUrl } from "@/helpers/utils"
 import styles from './index.module.less';
 import styles from './index.module.less';
 import "plyr/dist/plyr.css";
 import "plyr/dist/plyr.css";
 import Plyr from "plyr";
 import Plyr from "plyr";
@@ -22,10 +22,12 @@ export default defineComponent({
     const resourceUrl = decodeURIComponent(route.query.resourceUrl as string || '');
     const resourceUrl = decodeURIComponent(route.query.resourceUrl as string || '');
     const musicSheetName = decodeURIComponent(route.query.musicSheetName as string || '');
     const musicSheetName = decodeURIComponent(route.query.musicSheetName as string || '');
     const username = decodeURIComponent(route.query.username as string || '');
     const username = decodeURIComponent(route.query.username as string || '');
+    const musicSheetId = decodeURIComponent(route.query.musicSheetId as string || '');
     const playType = resourceUrl.lastIndexOf('mp4') !== -1 ? 'Video' : 'Audio'
     const playType = resourceUrl.lastIndexOf('mp4') !== -1 ? 'Video' : 'Audio'
     const lottieDom = ref()
     const lottieDom = ref()
     const landscapeScreen = ref(false)
     const landscapeScreen = ref(false)
     let _plrl:any
     let _plrl:any
+    const playIngShow = ref(true)
     const loaded = ref(false)
     const loaded = ref(false)
     const { registerDrag, unRegisterDrag } = landscapeScreenDrag()
     const { registerDrag, unRegisterDrag } = landscapeScreenDrag()
     watch(landscapeScreen, ()=>{
     watch(landscapeScreen, ()=>{
@@ -35,10 +37,18 @@ export default defineComponent({
         unRegisterDrag()
         unRegisterDrag()
       }
       }
     })
     })
+    // 谱面
+    const staffState = reactive({
+      staffSrc: "",
+      isShow: false,
+      height:"initial"
+    })
+    const staffDom= ref<HTMLIFrameElement>()
+    const {playStaff, pauseStaff, updateProgressStaff} = staffMoveInstance()
     function initPlay(){
     function initPlay(){
       const id = playType === "Audio" ? "#audioMediaSrc" : "#videoMediaSrc";
       const id = playType === "Audio" ? "#audioMediaSrc" : "#videoMediaSrc";
       _plrl = new Plyr(id, {
       _plrl = new Plyr(id, {
-        controls: ["play-large", "play", "progress", "current-time", "duration"]
+        controls: ["play", "progress", "current-time", "duration"]
       });
       });
       // 在微信中运行的时候,微信没有开放自动加载资源的权限,所以要等播放之后才显示播放控制器
       // 在微信中运行的时候,微信没有开放自动加载资源的权限,所以要等播放之后才显示播放控制器
       _plrl.on('loadedmetadata', () => {
       _plrl.on('loadedmetadata', () => {
@@ -58,6 +68,20 @@ export default defineComponent({
           pauseVisualDraw()
           pauseVisualDraw()
         });
         });
       }
       }
+      _plrl.on('play', () => {
+        playIngShow.value = false
+        playStaff()
+      });
+      _plrl.on('pause', () => {
+        playIngShow.value = true
+        pauseStaff()
+      });
+      _plrl.on('seeked', () => {
+        // 暂停的时候调用
+        if(!_plrl.playing){
+          updateProgressStaff(_plrl.currentTime)
+        }
+      });
     }
     }
     // 注册 横屏时候的自定义拖动事件 解决旋转时候拖动进度条坐标的问题
     // 注册 横屏时候的自定义拖动事件 解决旋转时候拖动进度条坐标的问题
     function landscapeScreenDrag() {
     function landscapeScreenDrag() {
@@ -127,13 +151,10 @@ export default defineComponent({
       canvasDom.width = width
       canvasDom.width = width
       canvasDom.height = height
       canvasDom.height = height
       // audio
       // 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)
+      let audioCtx : AudioContext | null = null
+      let analyser : AnalyserNode | null = null
+      let source : MediaElementAudioSourceNode | null = null
+      const dataArray = new Uint8Array(fftSize / 2)
       const draw = (data: Uint8Array, ctx: CanvasRenderingContext2D, { lineGap, canvWidth, canvHeight, canvFillColor, lineColor }: propsType) => {
       const draw = (data: Uint8Array, ctx: CanvasRenderingContext2D, { lineGap, canvWidth, canvHeight, canvFillColor, lineColor }: propsType) => {
         if (!ctx) return
         if (!ctx) return
         const w = canvWidth
         const w = canvWidth
@@ -180,7 +201,7 @@ export default defineComponent({
       }
       }
       const requestAnimationFrameFun = () => {
       const requestAnimationFrameFun = () => {
         requestAnimationFrame(() => {
         requestAnimationFrame(() => {
-          analyser.getByteFrequencyData(dataArray)
+          analyser?.getByteFrequencyData(dataArray)
           draw(dataArray, canvasCtx, {
           draw(dataArray, canvasCtx, {
             lineGap: 2,
             lineGap: 2,
             canvWidth: width,
             canvWidth: width,
@@ -195,22 +216,41 @@ export default defineComponent({
       }
       }
       let isPause = true
       let isPause = true
       const playVisualDraw = () => {
       const playVisualDraw = () => {
+        if (!audioCtx) {
+          audioCtx = new AudioContext()
+          source = audioCtx.createMediaElementSource(audioDom)
+          analyser = audioCtx.createAnalyser()
+          analyser.fftSize = fftSize
+          source?.connect(analyser)
+          analyser.connect(audioCtx.destination)
+        }
+        //audioCtx.resume()  // 重新更新状态   加了暂停和恢复音频音质发生了变化  所以这里取消了
         isPause = false
         isPause = false
-        audioCtx.resume()
         requestAnimationFrameFun()
         requestAnimationFrameFun()
       }
       }
       const pauseVisualDraw = () => {
       const pauseVisualDraw = () => {
         isPause = true
         isPause = true
-        audioCtx.suspend()
+        //audioCtx?.suspend()  // 暂停   加了暂停和恢复音频音质发生了变化  所以这里取消了
+        // source?.disconnect()
+        // analyser?.disconnect()
       }
       }
       return {
       return {
         playVisualDraw,
         playVisualDraw,
         pauseVisualDraw
         pauseVisualDraw
       }
       }
     }
     }
-    function handlerBack(){
+    function handlerBack(event:MouseEvent){
+      event.stopPropagation()
       router.back()
       router.back()
     }
     }
+    //点击改变播放状态
+    function handlerClickPlay(){
+      if (_plrl.playing) {
+        _plrl.pause();
+      } else {
+        _plrl.play();
+      }
+    }
     function handlerLandscapeScreen(){
     function handlerLandscapeScreen(){
       // app端调用app的横屏
       // app端调用app的横屏
       if(isApp){
       if(isApp){
@@ -242,7 +282,86 @@ export default defineComponent({
         _plrl?.pause();
         _plrl?.pause();
       }
       }
     });
     });
+    // 初始化五线谱
+    function initStaff(){
+      //const src = `${vaildMusicScoreUrl()}/instrument/#/simple-detail?id=${musicSheetId}&musicRenderType=staff`;
+      const src = `http://192.168.3.68:3000/instrument.html#/simple-detail?id=${musicSheetId}&musicRenderType=staff`;
+      staffState.staffSrc = src
+      window.addEventListener('message', (event) => {
+        const { api, height } = event.data;
+        if (api === 'api_musicPage') {
+          staffState.isShow = true
+          staffState.height = height + "px"
+          // 如果是播放中自动开始 播放
+          if(_plrl.playing){
+            playStaff()
+          }
+        }
+      });
+    }
+    function staffMoveInstance(){
+      let isPause = true
+      const requestAnimationFrameFun = () => {
+        requestAnimationFrame(() => {
+          staffDom.value?.contentWindow?.postMessage(
+            {
+              api: 'api_playProgress',
+              content: {
+                currentTime: _plrl.currentTime
+              }
+            },
+            "*"
+          )
+          if (!isPause) {
+            requestAnimationFrameFun()
+          }
+        })
+      }
+      const playStaff = () => {
+        // 没渲染不执行
+        if(!staffState.isShow) return
+        isPause = false
+        staffDom.value?.contentWindow?.postMessage(
+          {
+            api: 'api_play'
+          },
+          "*"
+        )
+        requestAnimationFrameFun()
+      }
+      const pauseStaff = () => {
+        // 没渲染不执行
+        if(!staffState.isShow) return
+        isPause = true
+        staffDom.value?.contentWindow?.postMessage(
+          {
+            api: 'api_paused'
+          },
+          "*"
+        )
+      }
+      const updateProgressStaff = (currentTime: string) => {
+        // 没渲染不执行
+        if(!staffState.isShow) return
+        staffDom.value?.contentWindow?.postMessage(
+          {
+            api: 'api_updateProgress',
+            content: {
+              currentTime
+            }
+          },
+          "*"
+        )
+      }
+      return {
+        playStaff,
+        pauseStaff,
+        updateProgressStaff
+      }
+    }
     onMounted(()=>{
     onMounted(()=>{
+      // 五线谱
+      initStaff()
       initPlay()
       initPlay()
       handlerLandscapeScreen()
       handlerLandscapeScreen()
     })
     })
@@ -259,9 +378,9 @@ export default defineComponent({
       }
       }
     })
     })
     return () =>
     return () =>
-    <div id="landscapeScreenPlay" class={[styles.playCreation,landscapeScreen.value && styles.landscapeScreen,!loaded.value && styles.notLoaded]}>
+    <div id="landscapeScreenPlay" class={[styles.playCreation,landscapeScreen.value && styles.landscapeScreen,!loaded.value && styles.notLoaded]} onClick={handlerClickPlay}>
       <div class={styles.backBox}>
       <div class={styles.backBox}>
-        <img class={styles.backImg} src={backImg}onClick={handlerBack} />
+        <img class={styles.backImg} src={backImg} onClick={handlerBack} />
         <div class={styles.musicDetail}>
         <div class={styles.musicDetail}>
           <div class={styles.musicSheetName}>{musicSheetName}</div>
           <div class={styles.musicSheetName}>{musicSheetName}</div>
           <div class={styles.username}>{username}</div>
           <div class={styles.username}>{username}</div>
@@ -291,6 +410,27 @@ export default defineComponent({
             playsinline
             playsinline
           />
           />
       }
       }
+      <div class={[styles.playLarge, playIngShow.value && styles.playIngShow]}></div>
+      {/* 谱面 */}
+      {
+        staffState.staffSrc &&
+        <div
+          class={[styles.staffBox, staffState.isShow && styles.staffBoxShow]}
+          style={
+            {
+              '--staffBoxHeight':staffState.height
+            }
+          }
+        >
+          <div class={styles.mask}></div>
+          <iframe
+            ref={staffDom}
+            class={styles.staff}
+            frameborder="0"
+            src={staffState.staffSrc}>
+          </iframe>
+        </div>
+      }
     </div>;
     </div>;
   }
   }
 });
 });