TIANYONG 11 months ago
parent
commit
4c0bb1265c

BIN
src/page-instrument/evaluat-model/countdown/imgs/step1.png


BIN
src/page-instrument/evaluat-model/countdown/imgs/step2.png


BIN
src/page-instrument/evaluat-model/countdown/imgs/step3.png


+ 25 - 0
src/page-instrument/evaluat-model/countdown/index.module.less

@@ -0,0 +1,25 @@
+.countdown {
+    position: fixed;
+    left: calc(50% - 66px);
+    top: calc(50% - 66px);
+    width: 132px;
+    height: 132px;
+    z-index: 200;
+    background-size: 100% 100%;
+    background-repeat: no-repeat;
+    transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;
+    opacity: 1;
+    &.step1{
+        background-image: url("./imgs/step1.png");
+    }
+    &.step2{
+        background-image: url("./imgs/step2.png");
+    }
+    &.step3{
+        background-image: url("./imgs/step3.png");
+    }
+    &.isAnimating {
+        transform: scale(0.5);
+        opacity: 0;
+    }
+}

+ 57 - 0
src/page-instrument/evaluat-model/countdown/index.tsx

@@ -0,0 +1,57 @@
+import { defineComponent, reactive, onMounted} from "vue"
+import styles from "./index.module.less"
+import soundWav from './timer.mp3';
+
+let soundVIdeo: HTMLAudioElement | undefined;
+
+const countdownData = reactive({
+	isShow: false,
+	step: 3,
+	isAnimating: false
+})
+let _countdownTIme: NodeJS.Timer
+
+export function startCountdown() {
+	Object.assign(countdownData, {
+		isShow: true,
+		step: 3,
+		isAnimating: false
+	})
+	//soundVIdeo?.play()
+	let resolveFun: (value: boolean) => void
+	_countdownTIme = setInterval(() => {
+		if (countdownData.step <= 0) {
+			clearInterval(_countdownTIme)
+			countdownData.isShow = false
+			resolveFun(true)
+			//soundVIdeo?.pause()
+		} else {
+			countdownData.isAnimating = true
+			const _time = setTimeout(() => {
+				clearTimeout(_time)
+				countdownData.isAnimating = false
+				countdownData.step--
+			}, 300)
+		}
+	}, 1000)
+	return new Promise( resolve => {
+		resolveFun = resolve
+	})
+}
+export default defineComponent({
+	name: "countdown",
+	setup() {
+		if(!soundVIdeo) {
+			soundVIdeo = new Audio(soundWav)
+			soundVIdeo.load();
+		}
+		onMounted(()=>{
+			soundVIdeo?.pause();
+		})
+		return () => (
+			<>
+				{countdownData.isShow && <div class={[styles.countdown,countdownData.isAnimating&&styles.isAnimating, styles[`step${countdownData.step}`]]}></div>}
+			</>
+		)
+	}
+})

BIN
src/page-instrument/evaluat-model/countdown/timer.mp3


BIN
src/page-instrument/header-top/image/submit.png


BIN
src/page-instrument/view-detail/images/bg2.png


BIN
src/page-instrument/view-detail/images/bg3.png


BIN
src/page-instrument/view-detail/intonationLine/bird.png


+ 31 - 0
src/page-instrument/view-detail/intonationLine/index.less

@@ -0,0 +1,31 @@
+#musicAndSelection {
+    .intonationLineBox{
+        display: flex;
+        align-items: center;
+        height: 2.4rem;
+    }
+    .intonationLineCon{
+        position: relative;
+        border-bottom: 1px solid rgba(255, 255, 255, 0.12);
+        border-top: 1px solid rgba(255, 255, 255, 0.12);
+        .intonationLineCanvas{
+            display: block;
+        }
+    }
+    .intonationLineBot{
+        position: fixed;
+        background: url("./bird.png") no-repeat;
+        background-size: 100% 100%;
+        width: 24Px;
+        height: 30Px;
+        top: calc(36px + (90px - 80Px)/2 + 80Px);
+        margin-left: calc(-12Px + 10px);
+        margin-top: -30Px;
+    }
+    #osmdCanvasPage1{
+        top: 0;
+    }
+    #cursorImg-0 {
+        display: none;
+    }
+}

+ 343 - 0
src/page-instrument/view-detail/intonationLine/index.tsx

@@ -0,0 +1,343 @@
+import "./index.less"
+import state from "/src/state"
+import { getAudioCurrentTime } from "/src/view/audio-list"
+
+type rectNotesPosType = { x: number; y: number; width: number; MeasureNumberXML: number; frequency: number; noteId: number }[]
+type intonationLineType = {
+   canvasDom: null | HTMLCanvasElement
+   canvasCtx: null | undefined | CanvasRenderingContext2D
+   canvasDomWith: number
+   canvasDomHeight: number
+   intonationLineBoxDom: null | HTMLElement
+   osmdCanvasPageDom: null | HTMLElement
+   intonationLineBotDom: null | HTMLElement
+   musicLineDom: null | HTMLCanvasElement
+   rectNotesPos: rectNotesPosType
+   frequencyAv: number
+}
+
+export const intonationLineState = {
+   canvasDom: null,
+   canvasCtx: null,
+   canvasDomWith: 0,
+   canvasDomHeight: 80,
+   intonationLineBoxDom: null,
+   osmdCanvasPageDom: null,
+   intonationLineBotDom: null,
+   musicLineDom: null,
+   rectNotesPos: [], // 音符 音准线
+   frequencyAv: -1
+} as intonationLineType
+
+const progressMusicData = {
+   frequency: -1,
+   progressMusic: []
+} as {
+   frequency: number
+   progressMusic: { noteId: number; x: number; y: number; width: number; status: "start" | "ing" | "end" }[]
+}
+
+/**
+ * 初始化 音准器
+ */
+export function initIntonationLine() {
+   // 创建dom
+   createIntonationLine()
+   // 根据音符获取坐标
+   intonationLineState.rectNotesPos = getRectNotesPosByBatePos()
+   console.log("rectNotesPos:", intonationLineState.rectNotesPos)
+   const { musicLineDom } = drawMusicLine()
+   intonationLineState.musicLineDom = musicLineDom
+   // 画初始进度
+   drawProgressectMusic()
+   // 鸟的初始位置
+   intonationLineState.intonationLineBotDom && (intonationLineState.intonationLineBotDom.style.left = `${intonationLineState.rectNotesPos[0].x}px`)
+   console.log(intonationLineState, "评测小鸟数据")
+}
+
+//  模拟修改值
+setInterval(() => {
+   progressMusicData.frequency = getRandomNumberBetween(600, 800)
+}, 10)
+function getRandomNumberBetween(min: number, max: number) {
+   return Math.floor(Math.random() * (max - min + 1)) + min
+}
+
+/**
+ * 根据播放时间进度移动处理
+ */
+export function moveInitIntonationLineByPlayTime() {
+   const currentTime = getAudioCurrentTime()
+   if (currentTime <= state.fixtime) return
+   if (currentTime > state.times.last()?.endtime) return
+   // 当休止小节,可能当前音符在谱面上没有实际的音符(没有bbox),所以往后找谱面上有的音符
+   let nextIndex = state.activeNoteIndex + 1
+   let nextBBox = state.times[nextIndex]?.bbox
+   while (!nextBBox && nextIndex < state.times.length) {
+      nextIndex += 1
+      nextBBox = state.times[nextIndex]?.bbox
+   }
+   // 当前的音符和下一个音符之间的时值 (当是最后一个音符的时候,下一个音符的时间取当前音符的endtime)
+   const noteDuration =
+      (nextIndex > state.times.length - 1 ? state.times[state.activeNoteIndex]?.endtime : state.times[nextIndex].time) -
+      state.times[state.activeNoteIndex]?.time
+   // 当前时值在该区间的占比
+   const playProgress = (currentTime - state.times[state.activeNoteIndex]?.time) / noteDuration
+   // 当前的进度的位置信息
+   const noteDistance =
+      nextIndex > state.times.length - 1
+         ? intonationLineState.rectNotesPos[intonationLineState.rectNotesPos.length - 1].width
+         : state.times[nextIndex].bbox.x - state.times[state.activeNoteIndex].bbox.x
+   const affectDistance = noteDistance * playProgress
+   const translateXNum = affectDistance + state.times[state.activeNoteIndex].bbox.x - state.times[0].bbox.x
+   intonationLineState.osmdCanvasPageDom && (intonationLineState.osmdCanvasPageDom.style.transform = `translateX(-${translateXNum}px)`)
+   // 更新音准器小鸟的位置信息
+   updateBotByFrequency()
+   // 更新音准器数据
+   updateProgressMusicData(affectDistance)
+}
+/**
+ * 根据频率更新小鸟的位置信息
+ */
+function updateBotByFrequency() {
+   const frequency = progressMusicData.frequency
+   let translateYNum = 0
+   if (frequency !== -1) {
+      const y =
+         intonationLineState.canvasDomHeight / 2 -
+         (((frequency - intonationLineState.frequencyAv) / intonationLineState.frequencyAv) * intonationLineState.canvasDomHeight) / 2
+      if (y >= 0 && y <= intonationLineState.canvasDomHeight) {
+         translateYNum = intonationLineState.canvasDomHeight - y
+      } else if (y > intonationLineState.canvasDomHeight) {
+         translateYNum = 0
+      } else if (y < 0) {
+         translateYNum = intonationLineState.canvasDomHeight
+      }
+   }
+   intonationLineState.intonationLineBotDom && (intonationLineState.intonationLineBotDom.style.transform = `translateY(-${translateYNum}px)`)
+}
+
+/**
+ * 根据 频率更新音准器数据
+ * @affectDistance 偏移的距离
+ */
+const affectNum = 50
+function updateProgressMusicData(affectDistance: number) {
+   const { frequency, progressMusic } = progressMusicData
+   const { activeNoteIndex, times } = state
+   const activeNote = times[activeNoteIndex]
+   // 最后
+   const lastProgressmusic = progressMusic.length ? progressMusic[progressMusic.length - 1] : null
+   if (frequency + affectNum > activeNote.frequency && frequency - affectNum < activeNote.frequency) {
+      const y =
+         intonationLineState.canvasDomHeight / 2 -
+         (((activeNote.frequency - intonationLineState.frequencyAv) / intonationLineState.frequencyAv) * intonationLineState.canvasDomHeight) / 2
+      if (lastProgressmusic) {
+         // 有值的时候先看是不是当前小节
+         if (lastProgressmusic.noteId === activeNote.noteId) {
+            if (lastProgressmusic.status === "end") {
+               // 当前小节 新增
+               progressMusicData.progressMusic.push({
+                  noteId: activeNote.noteId,
+                  x: activeNote.bbox.x + affectDistance,
+                  y,
+                  width: 0,
+                  status: "start"
+               })
+            } else {
+               // 更新处理
+               lastProgressmusic.status = "ing"
+               lastProgressmusic.width = activeNote.bbox.x + affectDistance - lastProgressmusic.x
+            }
+         } else if (lastProgressmusic.status !== "end") {
+            // 不是当前小节 并且不为end时候
+            lastProgressmusic.status = "end"
+            // 下一个 小节 新增
+            progressMusicData.progressMusic.push({
+               noteId: activeNote.noteId,
+               x: activeNote.bbox.x + affectDistance,
+               y,
+               width: 0,
+               status: "start"
+            })
+         }
+      } else {
+         // 增加新值
+         progressMusicData.progressMusic.push({
+            noteId: activeNote.noteId,
+            x: activeNote.bbox.x + affectDistance,
+            y,
+            width: 0,
+            status: "start"
+         })
+      }
+   } else {
+      // 没有匹配上的时候 关闭最后一个
+      if (lastProgressmusic && lastProgressmusic.status !== "end") {
+         lastProgressmusic.status = "end"
+      }
+   }
+   // 更新音准器
+   drawProgressectMusic()
+}
+
+/**
+ * 画音准线进度
+ */
+function drawProgressectMusic() {
+   const canvasCtx = intonationLineState.canvasCtx!
+   const canvasDom = intonationLineState.canvasDom!
+   canvasCtx.clearRect(0, 0, canvasDom.width, canvasDom.height)
+   canvasCtx.drawImage(intonationLineState.musicLineDom!, 0, 0)
+   console.log(progressMusicData.progressMusic, 888)
+   // 过滤掉宽度为0的数据
+   const filterProgressMusic = progressMusicData.progressMusic.filter(item => {
+      return item.width !== 0
+   })
+   drawPathRectByArr(canvasCtx!, filterProgressMusic, "#FFC121")
+}
+
+/**
+ * 画音准线
+ */
+function drawMusicLine() {
+   const musicLineDom = document.createElement("canvas")
+   musicLineDom.width = intonationLineState.canvasDomWith
+   musicLineDom.height = intonationLineState.canvasDomHeight
+   const musicLineCtx = musicLineDom.getContext("2d")!
+   musicLineCtx.clearRect(0, 0, musicLineDom.width, musicLineDom.height)
+   // 画矩形
+   drawPathRectByArr(musicLineCtx, intonationLineState.rectNotesPos, "rgba(255, 255, 255, 0.3)")
+   return {
+      musicLineDom,
+      musicLineCtx
+   }
+}
+
+/**
+ * 创建dom
+ */
+function createIntonationLine() {
+   // osmdCanvasPage
+   const osmdCanvasPageDom = document.querySelector("#osmdCanvasPage1") as HTMLElement
+   intonationLineState.osmdCanvasPageDom = osmdCanvasPageDom
+   // box
+   const intonationLineBoxDom = document.createElement("div")
+   intonationLineBoxDom.className = "intonationLineBox"
+   intonationLineState.intonationLineBoxDom = intonationLineBoxDom
+   // con
+   const intonationLineConDom = document.createElement("div")
+   intonationLineConDom.className = "intonationLineCon"
+   //canvas
+   const intonationLineCanvasDom = document.createElement("canvas")
+   intonationLineCanvasDom.className = "intonationLineCanvas"
+   intonationLineState.canvasDom = intonationLineCanvasDom
+   intonationLineState.canvasDomWith = osmdCanvasPageDom?.offsetWidth | 0
+   intonationLineCanvasDom.width = intonationLineState.canvasDomWith
+   intonationLineCanvasDom.height = intonationLineState.canvasDomHeight
+   intonationLineState.canvasCtx = intonationLineCanvasDom.getContext("2d")
+   // bot
+   const intonationLineBotDom = document.createElement("div")
+   intonationLineBotDom.className = "intonationLineBot"
+   intonationLineState.intonationLineBotDom = intonationLineBotDom
+   document.querySelector("#musicAndSelection")?.appendChild(intonationLineBotDom)
+   intonationLineConDom.appendChild(intonationLineCanvasDom)
+   intonationLineBoxDom.appendChild(intonationLineConDom)
+   // 添加到 osmdCanvasPage1
+   osmdCanvasPageDom?.insertBefore(intonationLineBoxDom, osmdCanvasPageDom.firstChild)
+}
+
+/**
+ * 根据音符获取坐标
+ */
+function getRectNotesPosByBatePos(): rectNotesPosType {
+   let totalAvInde = 0
+   // 取平均值
+   const totalAv =
+      state.times.reduce((total, item) => {
+         if (item.frequency !== -1) {
+            // -1 为休止符
+            total += item.frequency
+            totalAvInde++
+         }
+         return total
+      }, 0) / totalAvInde
+   intonationLineState.frequencyAv = totalAv
+   const notesPos = state.times.reduce((notesArr: any[], item) => {
+      // 当休止小节,可能当前音符在谱面上没有实际的音符(没有bbox),所以往后找谱面上有的音符
+      if (item.bbox) {
+         notesArr.push({
+            noteId: item.noteId,
+            frequency: item.frequency,
+            MeasureNumberXML: item.MeasureNumberXML,
+            x: item.bbox.x,
+            // 当为休止符的时候 取最下面的位置*0.9,确保能显示完整
+            y:
+               intonationLineState.canvasDomHeight / 2 -
+               ((((item.frequency === -1 ? 2 * totalAv * 0.1 : item.frequency) - totalAv) / totalAv) * intonationLineState.canvasDomHeight) / 2
+            // cavans 高度为160 所以基准为80
+         })
+      }
+      return notesArr
+   }, [])
+   // 最后一个音符延长(这里建立一个虚拟的音符延长)
+   const extendPoint = {
+      ...notesPos[notesPos.length - 1]
+   }
+   extendPoint.MeasureNumberXML++
+   extendPoint.frequency = -1
+   // 当总长度减30小于最后一个音符时候,取最后一个音符加15
+   extendPoint.x = intonationLineState.canvasDomWith - 30 > extendPoint.x ? intonationLineState.canvasDomWith - 30 : extendPoint.x + 15
+   notesPos.push(extendPoint)
+   /* 计算音准线 */
+   return notesPos.reduce(
+      (
+         arr: { MeasureNumberXML: number; frequency: number; x: number; y: number; width: number; noteId: number }[],
+         { frequency, x, y, MeasureNumberXML, noteId },
+         index
+      ) => {
+         const canvasDataLen = notesPos.length
+         if (index < canvasDataLen - 1) {
+            const nextCanvasObj = notesPos[index + 1]
+            arr.push({
+               noteId,
+               MeasureNumberXML,
+               frequency,
+               x,
+               y,
+               width: nextCanvasObj.x - x
+            })
+         }
+         return arr
+      },
+      []
+   )
+}
+
+/**
+ * 根据坐标信息 画矩形路径
+ */
+function drawPathRectByArr(ctx: CanvasRenderingContext2D, arr: { x: number; y: number; width: number }[], color: string) {
+   arr.map(({ x, y, width }) => {
+      drawRect(ctx, x, y, width * 0.92, 7, 4, color)
+   })
+}
+
+/**
+ * 画矩形
+ */
+function drawRect(ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number, radius: number, color: string) {
+   ctx.fillStyle = color
+   ctx.beginPath()
+   ctx.moveTo(x + radius, y)
+   ctx.lineTo(x + width - radius, y)
+   ctx.arcTo(x + width, y, x + width, y + radius, radius)
+   ctx.lineTo(x + width, y + height - radius)
+   ctx.arcTo(x + width, y + height, x + width - radius, y + height, radius)
+   ctx.lineTo(x + radius, y + height)
+   ctx.arcTo(x, y + height, x, y + height - radius, radius)
+   ctx.lineTo(x, y + radius)
+   ctx.arcTo(x, y, x + radius, y, radius)
+   ctx.closePath()
+   ctx.fill()
+}

BIN
src/view/plugins/toggleMusicSheet/choosePartName/imgs/changeName.png


BIN
src/view/plugins/toggleMusicSheet/choosePartName/imgs/okBtn.png