Bläddra i källkod

feat: 跟练模式1.1

TIANYONG 1 år sedan
förälder
incheckning
30fae2efb0

+ 1 - 1
src/helpers/metronome.ts

@@ -237,7 +237,7 @@ class Metronome {
 		} else {
 			this.source.muted = false
 		}
-		console.log('音量',this.source,this.source.volume)
+		// console.log('音量',this.source,this.source.volume)
 		this.source.play();
 		metronomeData.followAudioIndex += 1;
 		metronomeData.followAudioIndex = metronomeData.followAudioIndex > metronomeData.totalNumerator ? 1 : metronomeData.followAudioIndex;

+ 4 - 0
src/state.ts

@@ -562,6 +562,10 @@ export const onEnded = () => {
   //     // 销毁播放器
   //     api_cloudDestroy();
   // }
+  if (state.playEnd) {
+    console.log('音频播放结束,无需再次执行')
+    return
+  }  
   // 修改状态为结束
   state.playEnd = true;
   state.playState = "paused";

+ 28 - 1
src/view/follow-practice/index.tsx

@@ -95,6 +95,7 @@ const onClear = () => {
 
 /** 开始跟练 */
 export const handleFollowStart = async () => {
+	checking = false;
 	const res = await api_cloudToggleFollow("start");
 	// 用户没有授权,需要重置状态
 	if (res?.content?.reson) {
@@ -132,9 +133,32 @@ export const handleFollowEnd = () => {
 	console.log("结束");
 };
 
+/**
+ * 2024.6.17 新增自动结束跟练模式功能
+ * 如果跟练模式,唱完了最后一个有频率的音符,需要自动结束掉跟练模式
+ * 需要判断当前音符的后面是否都是休止音符(休止音符的频率都是-1),或者是否是最后一个音符了
+ */
+const autoEndFollow = () => {
+	if (followData.index >= state.times.length) {
+		handleFollowEnd()
+		return
+	}
+	let nextIndex = followData.index + 1;
+	const rightTimes = state.times.slice(followData.index,state.times.length)
+	// 后面的音符是否都是休止音符
+	const isAllRest = !rightTimes.some((item: any) => item.frequency > 1);
+	if (isAllRest && state.times[followData.index].frequency < 1) {
+		handleFollowEnd()
+		return
+	}
+}
+
 // 下一个
 const next = () => {
-	gotoNext(state.times[followData.index]);
+	if (followData.index < state.times.length) {
+		gotoNext(state.times[followData.index]);
+	}
+	autoEndFollow();	
 };
 
 // 获取当前音符
@@ -214,6 +238,9 @@ const setColor = (item: any, state: "follow-up" | "follow-down" | "", isRight =
 	if (_note) {
 		_note.classList.remove("follow-up", "follow-down");
 		state && _note.classList.add(state);
+		if (isRight) {
+			_note.classList.add("follow-success");
+		}
 	}
 };
 

BIN
src/view/selection/imgs/down_icon.png


BIN
src/view/selection/imgs/up_icon.png


+ 85 - 8
src/view/selection/index.module.less

@@ -158,6 +158,57 @@
     }
 }
 
+.followTipUp, .followTipDown {
+    display: flex;
+    align-items: center;
+    background: rgba(0,0,0,0.6);
+    position: relative;
+    padding: 3px 6px;
+    border-radius: 16px;
+    width: fit-content;
+    left: 50%;
+    top: -40px;
+    transform: translate(-50%);
+    img {
+        width: 14px;
+        height: 14px;
+        margin-right: 4px;
+    }
+    span {
+        font-size: 12px;
+        color: #fff;
+        word-break: keep-all;
+        display: flex;
+        width: fit-content;
+    }
+    ::before {
+        content: "";
+        position: absolute;
+        left: 50%;
+        bottom: -7PX;
+        transform: translateX(-50%);
+        width: 0;
+        height: 0;
+        border-top: 8PX solid rgba(0,0,0,0.6);
+        border-right: 8PX solid transparent;
+        border-left: 8PX solid transparent;  
+        z-index: 2;    
+    }
+}
+
+.followTipUp {
+    i {
+        font-style: normal;
+        color: rgba(255, 102, 166, 1);
+    }
+}
+.followTipDown {
+    i {
+        font-style: normal;
+        color: rgba(255, 146, 0, 1);
+    }
+}
+
 :global {
     .follow-error {
         display: block;
@@ -165,28 +216,44 @@
         .van-icon-cross {
             display: block;
         }
-
+        :global {
+            .tip-up, .tip-down {
+                display: none;
+            }
+        }
     }
 
     .follow-down {
         .van-icon-cross {
-            color: #ffca67 !important;
+            color: rgba(255, 146, 0, 1) !important;
         }
 
         .vf-note path {
-            fill: #ffca67 !important;
-            stroke: #ffca67 !important;
+            fill: rgba(255, 146, 0, 1) !important;
+            stroke: rgba(255, 146, 0, 1) !important;
+        }
+
+        :global {
+            .tip-down {
+                display: flex;
+            }
         }
     }
 
     .follow-up {
         .van-icon-cross {
-            color: rgb(255, 0, 0) !important;
+            color: rgba(255, 102, 166, 1) !important;
         }
 
         .vf-note path {
-            fill: rgb(255, 0, 0) !important;
-            stroke: rgb(255, 0, 0) !important;
+            fill: rgba(255, 102, 166, 1) !important;
+            stroke: rgba(255, 102, 166, 1) !important;
+        }
+
+        :global {
+            .tip-up {
+                display: flex;
+            }
         }
     }
 
@@ -195,8 +262,18 @@
 
         .van-icon-success {
             display: block;
-            color: #07c160;
+            color: rgba(0, 255, 148, 1);
         }
+        .vf-note path {
+            fill: rgba(0, 255, 148, 1) !important;
+            stroke: rgba(0, 255, 148, 1) !important;
+        }
+        :global {
+            .tip-up, .tip-down {
+                display: none;
+            }
+        }
+
     }
 }
 

+ 16 - 2
src/view/selection/index.tsx

@@ -8,6 +8,8 @@ import { Icon, showToast } from "vant";
 import MoveMusicScore, { moveData, renderForMoveData } from "../plugins/move-music-score";
 import { useRoute } from "vue-router";
 import { getQuery } from "/src/utils/queryString";
+import IntonationDown from "./imgs/down_icon.png"
+import IntonationUp from "./imgs/up_icon.png"
 
 const selectData = reactive({
 	notes: [] as any[],
@@ -296,10 +298,22 @@ export default defineComponent({
 							style={item.bbox}
 							onClick={() => skipNotePlay(item.index)}
 						>
-							<div class={styles.noteFollow} data-vf={"vf" + item.id}>
+							{/* <div class={styles.noteFollow} data-vf={"vf" + item.id}>
 								<Icon name="success" />
 								<Icon name="cross" />
-							</div>
+							</div> */}
+							<div class={styles.noteFollow} data-vf={"vf" + item.id}>
+								{/* <Icon name="success" />
+								<Icon name="cross" /> */}
+								<div class={[styles.followTipUp, 'tip-up']}>
+									<img src={IntonationUp} />
+									<span>音准<i>高了</i></span>
+								</div>
+								<div class={[styles.followTipDown, 'tip-down']}>
+									<img src={IntonationDown} />
+									<span>音准<i>低了</i></span>
+								</div>
+							</div>							
 							<div class={[styles.noteDot, 'node-dot']}></div>
 						</div>
 					);