Browse Source

使用说明,移调

liushengqiang 2 years ago
parent
commit
cde17ba571

BIN
src/page-instrument/view-figner/image/icon_bg.png


BIN
src/page-instrument/view-figner/image/icon_bg_t.png


BIN
src/page-instrument/view-figner/image/icon_bg_v.png


File diff suppressed because it is too large
+ 0 - 0
src/page-instrument/view-figner/image/icons.json


+ 300 - 21
src/page-instrument/view-figner/index.module.less

@@ -11,9 +11,7 @@
     flex-direction: column;
     width: 100vw;
     height: 100vh;
-    background-color: antiquewhite;
-    background-image: url('./image/icon_bg.png');
-    background-size: 100% auto;
+    background: linear-gradient(180deg, #6BC6E1 0%, #E1FBEB 35%, #9DE7E2 100%);
     user-select: none;
 }
 
@@ -126,7 +124,140 @@
 .fingerContent {
     flex: 1;
     overflow: hidden;
-    padding-top: 50px;
+    display: flex;
+
+    .wrapFinger {
+        flex: 1;
+        overflow: hidden;
+        display: flex;
+        flex-direction: column;
+    }
+
+    .boxFinger {
+        flex: 1;
+        padding-top: 50px;
+        overflow: hidden;
+    }
+
+    &.fingerRight {
+        flex-direction: row;
+        background: url('./image/icon_bg_t.png') no-repeat;
+        background-size: cover;
+
+        .tips {
+            width: 43%;
+            border-radius: 18px 0px 0px 18px;
+
+            &.tipHidden {
+                margin-right: -43%;
+            }
+        }
+    }
+
+    &.fingerBottom {
+        flex-direction: column;
+        background: url('./image/icon_bg_v.png') no-repeat;
+        background-size: cover;
+
+        .tips {
+            height: 238px;
+            border-radius: 18px 18px 0 0;
+
+            &.tipHidden {
+                margin-bottom: -238px;
+            }
+        }
+    }
+}
+
+.tips {
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+    position: relative;
+    z-index: 11;
+    flex-shrink: 0;
+    transition: all .3s;
+    background: #EEFDFF;
+    box-shadow: 0px 1px 12px 0px rgba(0, 0, 0, 0.12);
+    padding: 0 7px 13px 7px;
+    overflow: hidden;
+
+    .tipTitle {
+        position: relative;
+        height: 45px;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        flex-shrink: 0;
+    }
+
+    .tipTitleName {
+        position: relative;
+        font-weight: 500;
+        font-size: 15px;
+        z-index: 1;
+
+        &::after {
+            content: '';
+            position: absolute;
+            left: 0;
+            bottom: -2px;
+            width: 100%;
+            height: 8px;
+            background: #8AEDFA;
+            border-radius: 4px;
+            opacity: 0.5;
+            z-index: -1;
+        }
+    }
+
+    .tipClose {
+        position: absolute;
+        right: -7px;
+        top: 0;
+        height: 100%;
+        border: none;
+        background: transparent;
+        border-radius: 0;
+    }
+
+    .tipContent {
+        flex: 1;
+        background: #FFFFFF;
+        border-radius: 15px;
+        padding: 16px;
+        color: #415E6C;
+        font-size: 12px;
+        overflow: hidden;
+        overflow-y: auto;
+    }
+}
+
+.tipItem {
+    display: flex;
+    line-height: 18px;
+    margin-bottom: 8px;
+
+    .iconWrap {
+        display: flex;
+        align-items: center;
+        height: 18px;
+        margin-right: 6px;
+    }
+
+    .tipItemIcon {
+        width: 16px;
+        height: 16px;
+        background: linear-gradient(180deg, #70C6FF 0%, #1898F9 100%);
+        font-weight: 600;
+        line-height: 16px;
+        color: #FFFFFF;
+        text-shadow: 0px 1px 1px #319FF0;
+        border-radius: 50%;
+        text-align: center;
+
+    }
 }
 
 .notes {
@@ -135,6 +266,7 @@
     justify-content: center;
     align-items: flex-start;
     height: 65px;
+    flex-shrink: 0;
 
     .noteContent {
         position: relative;
@@ -182,24 +314,7 @@
 
 
 }
-.tones{
-    position: absolute;
-    left: 50px;
-    top: 50%;
-    transform: translateY(-50%);
-    max-height: calc(100% - 200px);
-    border-radius: 25px;
-    background: rgba(255, 255, 255, 0.53);
-    border: 1px solid rgba(255, 255, 255, 0.81);
-    overflow: auto;
-    overflow-x: hidden;
-    z-index: 10;
 
-    &::-webkit-scrollbar {
-        width: 0;
-        display: none;
-    }
-}
 
 .note {
     position: relative;
@@ -316,4 +431,168 @@
 .disabled {
     opacity: .5;
     pointer-events: none;
+}
+
+.toggleBtn {
+    position: fixed;
+    right: 0;
+    top: 50%;
+    transform: translateY(-50%);
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    width: 33px;
+    height: 55px;
+    background: linear-gradient(180deg, #FEF0AF 0%, #F7D656 100%);
+    box-shadow: 0px 2px 1px 0px rgba(168, 121, 0, 0.24), inset 0px 1px 3px 0px rgba(255, 255, 255, 0.5), inset 0px -2px 1px 0px rgba(255, 255, 255, 0.5);
+    border-radius: 48px 0px 0px 48px;
+    font-size: 13px;
+    color: #A14927;
+    line-height: 14px;
+    font-weight: 600;
+    padding-left: 10px;
+    text-align: center;
+    cursor: pointer;
+    z-index: 12;
+
+    &:active {
+        opacity: .8;
+    }
+
+    img {
+        width: 8px;
+        height: 5px;
+        margin-top: 3px;
+    }
+}
+
+.tones {
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+    position: relative;
+    z-index: 12;
+    transition: all .3s;
+    overflow: hidden;
+
+    .toneTitle {
+        position: relative;
+        height: 45px;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        flex-shrink: 0;
+    }
+
+    .tipTitleName {
+        position: relative;
+        font-weight: 500;
+        font-size: 15px;
+        z-index: 1;
+    }
+
+    .tipClose {
+        position: absolute;
+        right: 0;
+        top: 0;
+        height: 100%;
+        border: none;
+        background: transparent;
+        border-radius: 0;
+    }
+
+    .toneAction {
+        height: 82px;
+        border-top: 1px solid #EBEBEB;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+
+        :global {
+            .van-button {
+                font-size: 15px;
+                width: 40%;
+                height: 0;
+                margin: 0 7px;
+                padding: 0;
+                padding-bottom: 10.8%;
+
+                &::before {
+                    display: none;
+                }
+
+                .van-button__content {
+                    position: absolute;
+                    left: 0;
+                    top: 0;
+                    width: 100%;
+                }
+
+                &:active {
+                    opacity: .8;
+                }
+            }
+        }
+    }
+
+    .toneContent {
+        padding: 0 14px;
+        display: flex;;
+        flex-wrap: wrap;
+
+        :global {
+            .van-button {
+                font-size: 13px;
+                width: calc(100% / 4.5);
+                height: 0;
+                padding: 0;
+                margin: 0 1%;
+                margin-bottom: 4%;
+                padding-bottom: 8%;
+                flex-shrink: 0;
+                background-color: transparent;
+
+                &::before {
+                    display: none;
+                }
+
+                .van-button__content {
+                    position: absolute;
+                    left: 0;
+                    top: 0;
+                    width: 100%;
+                }
+
+                &:active {
+                    opacity: .8;
+                }
+            }
+        }
+    }
+}
+
+:global(.van-popup--right.tonePopup) {
+    width: 43%;
+    height: 100%;
+    border-radius: 18px 0px 0px 18px;
+    background: #EEFDFF;
+    .toneContent{
+        padding-bottom: 30px;
+        padding-top: 20px;
+    }
+}
+
+:global(.van-popup--bottom.tonePopup) {
+    display: flex;
+    flex-direction: column;
+    min-height: 238px;
+    border-radius: 18px 18px 0 0;
+    background: #EEFDFF;
+    .tones{
+        flex: 1;
+    }
+    .toneContent{
+        margin-top: auto;
+    }
 }

+ 152 - 82
src/page-instrument/view-figner/index.tsx

@@ -12,6 +12,7 @@ import { Howl } from "howler";
 import { storeData } from "/src/store";
 import { api_back } from "/src/helpers/communication";
 import Hammer from "hammerjs";
+import { Button, Icon, Popup, Space } from "vant";
 
 export default defineComponent({
 	name: "viewFigner",
@@ -33,7 +34,8 @@ export default defineComponent({
 			realKey: 0,
 			notes: [] as IFIGNER_INSTRUMENT_Note[],
 			tones: [] as IFIGNER_INSTRUMENT_Note[],
-			activeTone: "",
+			activeTone: {} as IFIGNER_INSTRUMENT_Note,
+			activeToneName: "",
 			soundFonts: {} as any,
 			viewIndex: 0,
 			noteAudio: null as unknown as Howl,
@@ -46,6 +48,10 @@ export default defineComponent({
 				startY: 0,
 				transition: "",
 			},
+			tipShow: false,
+			tips: [] as IFIGNER_INSTRUMENT_Note[],
+
+			tnoteShow: false,
 		});
 		const fingerData = reactive({
 			relationshipIndex: 0,
@@ -57,15 +63,16 @@ export default defineComponent({
 			if (fignerData) {
 				data.tones = fignerData.tones || [];
 				if (data.tones.length) {
-					data.activeTone = data.tones[0].realName;
+					data.activeTone = data.tones[0];
 				}
+				data.tips = fignerData.tips || [];
 				setNotes();
 			}
 		};
 		const setNotes = () => {
 			const fignerData = FIGNER_INSTRUMENT_DATA[data.subject as keyof typeof FIGNER_INSTRUMENT_DATA];
 			if (fignerData) {
-				data.notes = fignerData[`list${data.activeTone}`];
+				data.notes = fignerData[`list${data.activeTone.realName || ""}`];
 			}
 		};
 		const getFingeringData = async () => {
@@ -213,104 +220,167 @@ export default defineComponent({
 								<img src={icons.icon_2_0} />
 								<span>还原</span>
 							</div>
-							<div class={[styles.item]}>
-								<img src={icons.icon_2_1} />
-								<span>小技巧</span>
-							</div>
-						</div>
-					</div>
-					<div id="fingeringContainer" class={styles.fingerContent}>
-						<div
-							style={{
-								transform: `translate3d(${data.transform.x}px,${data.transform.y}px,0px) scale(${data.transform.scale})`,
-								transition: data.transform.transition,
-							}}
-							class={[styles.fingeringContainer]}
-						>
-							<div class={styles.imgs}>
-								<img src={fingerData.subject?.json?.full} />
-								{rs.map((key: number | string, index: number) => {
-									const nk: string = typeof key === "string" ? key.replace("active-", "") : String(key);
-									return <img data-index={nk} src={fingerData.subject?.json?.[nk]} />;
-								})}
-							</div>
-
 							<div
-								class={[styles.tizhi, canTizhi && styles.canDisplay]}
-								onClick={() =>
-									(fingerData.relationshipIndex = fingerData.relationshipIndex === 0 ? 1 : 0)
-								}
+								class={[styles.item]}
+								onClick={() => {
+									resetElement();
+									data.tipShow = !data.tipShow;
+								}}
 							>
-								替指
+								<img src={icons.icon_2_1} />
+								<span>使用说明</span>
 							</div>
 						</div>
+					</div>
+					<div
+						class={[
+							styles.fingerContent,
+							fingerData.fingeringInfo.orientation === 1 ? styles.fingerBottom : styles.fingerRight,
+						]}
+					>
+						<div class={styles.wrapFinger}>
+							<div id="fingeringContainer" class={styles.boxFinger}>
+								<div
+									style={{
+										transform: `translate3d(${data.transform.x}px,${data.transform.y}px,0px) scale(${data.transform.scale})`,
+										transition: data.transform.transition,
+									}}
+									class={[styles.fingeringContainer]}
+								>
+									<div class={styles.imgs}>
+										<img src={fingerData.subject?.json?.full} />
+										{rs.map((key: number | string, index: number) => {
+											const nk: string =
+												typeof key === "string" ? key.replace("active-", "") : String(key);
+											return <img data-index={nk} src={fingerData.subject?.json?.[nk]} />;
+										})}
+									</div>
 
-						<div class={styles.tones} style={{display: data.tones.length ? '' : 'none'}}>
-							{data.tones.map((tone: IFIGNER_INSTRUMENT_Note) => {
-								const steps = new Array(Math.abs(tone.step)).fill(1);
-								return (
 									<div
-										draggable={false}
-										class={styles.note}
-										onClick={() => {
-											data.activeTone = tone.realName;
-											setNotes();
-										}}
+										class={[styles.tizhi, canTizhi && styles.canDisplay]}
+										onClick={() =>
+											(fingerData.relationshipIndex = fingerData.relationshipIndex === 0 ? 1 : 0)
+										}
 									>
-										{data.activeTone === tone.realName ? (
-											<img draggable={false} src={icons.icon_btn_ylow} />
-										) : (
-											<img draggable={false} src={icons.icon_btn_blue} />
-										)}
+										替指
+									</div>
+								</div>
+							</div>
+							<div class={styles.notes}>
+								{/* <Button>
+							<Icon name="arrow-left" />
+						</Button> */}
+								<div class={styles.noteContent}>
+									<div class={styles.noteBox}>
+										{data.notes.map((note: IFIGNER_INSTRUMENT_Note) => {
+											const steps = new Array(Math.abs(note.step)).fill(1);
+											return (
+												<div draggable={false} class={styles.note} onClick={() => noteClick(note)}>
+													{data.realKey === note.realKey ? (
+														<img draggable={false} src={icons.icon_btn_ylow} />
+													) : (
+														<img draggable={false} src={icons.icon_btn_blue} />
+													)}
 
-										<div class={[styles.noteKey, data.activeTone === tone.realName && styles.keyActive]}>
-											{tone.step > 0 ? steps.map((n) => <span class={styles.dot}></span>) : null}
+													<div
+														class={[styles.noteKey, data.realKey === note.realKey && styles.keyActive]}
+													>
+														{note.step > 0 ? steps.map((n) => <span class={styles.dot}></span>) : null}
 
-											<div class={styles.noteName}>
-												<sup>{tone.mark && (tone.mark === "rise" ? "#" : "b")}</sup>
-												{tone.key}
-											</div>
-											{tone.step < 0 ? steps.map((n) => <span class={styles.dot}></span>) : null}
+														<div class={styles.noteName}>
+															<sup>{note.mark && (note.mark === "rise" ? "#" : "b")}</sup>
+															{note.key}
+														</div>
+														{note.step < 0 ? steps.map((n) => <span class={styles.dot}></span>) : null}
+													</div>
+												</div>
+											);
+										})}
+									</div>
+								</div>
+								{/* <Button size="small" >
+							<Icon name="arrow" />
+						</Button> */}
+							</div>
+						</div>
+						<div class={[styles.tips, data.tipShow ? "" : styles.tipHidden]}>
+							<div class={styles.tipTitle}>
+								<div class={styles.tipTitleName}>{fingerData.fingeringInfo.code}使用说明</div>
+								<Button class={styles.tipClose} onClick={() => (data.tipShow = false)}>
+									<Icon name="cross" color="#999" />
+								</Button>
+							</div>
+							<div class={styles.tipContent}>
+								{data.tips.map((tip, tipIndex) => (
+									<div class={styles.tipItem}>
+										<div class={styles.iconWrap}>
+											<div class={styles.tipItemIcon}>{tipIndex + 1}</div>
+										</div>
+										<div>
+											{tip.name}: {tip.realName}
 										</div>
 									</div>
-								);
-							})}
+								))}
+							</div>
 						</div>
 					</div>
-					<div class={styles.notes}>
-						{/* <Button>
-							<Icon name="arrow-left" />
-						</Button> */}
-						<div class={styles.noteContent}>
-							<div class={styles.noteBox}>
-								{data.notes.map((note: IFIGNER_INSTRUMENT_Note) => {
-									const steps = new Array(Math.abs(note.step)).fill(1);
-									return (
-										<div draggable={false} class={styles.note} onClick={() => noteClick(note)}>
-											{data.realKey === note.realKey ? (
-												<img draggable={false} src={icons.icon_btn_ylow} />
-											) : (
-												<img draggable={false} src={icons.icon_btn_blue} />
-											)}
+					<div class={styles.toggleBtn} onClick={() => (data.tnoteShow = true)}>
+						<div>
+							<sup>{data.activeTone.mark && (data.activeTone.mark === "rise" ? "#" : "b")}</sup>
+							{data.activeTone.name}
+						</div>
+						调
+						<img src={icons.icon_arrow} />
+					</div>
 
-											<div class={[styles.noteKey, data.realKey === note.realKey && styles.keyActive]}>
-												{note.step > 0 ? steps.map((n) => <span class={styles.dot}></span>) : null}
+					<Popup
+						class="tonePopup"
+						v-model:show={data.tnoteShow}
+						position={fingerData.fingeringInfo.orientation === 1 ? "bottom" : "right"}
+					>
+						<div class={styles.tones}>
+							<div class={styles.toneTitle}>
+								<div class={styles.tipTitleName}>移调</div>
+								<Button class={styles.tipClose} onClick={() => (data.tnoteShow = false)}>
+									<Icon name="cross" color="#999" />
+								</Button>
+							</div>
+							<div style={{ flex: 1, overflow: "hidden" }}>
+								<div class={styles.toneContent}>
+									{data.tones.map((tone: IFIGNER_INSTRUMENT_Note) => {
+										const steps = new Array(Math.abs(tone.step)).fill(1);
+										return (
+											<Button
+												round
+												plain
+												type={data.activeTone.realName === tone.realName ? "primary" : "default"}
+												onClick={() => {
+													data.activeTone = tone;
+													setNotes();
+												}}
+											>
+												{/* {tone.step > 0 ? steps.map((n) => <span class={styles.dot}></span>) : null} */}
 
 												<div class={styles.noteName}>
-													<sup>{note.mark && (note.mark === "rise" ? "#" : "b")}</sup>
-													{note.key}
+													<sup>{tone.mark && (tone.mark === "rise" ? "#" : "b")}</sup>
+													{tone.name}
 												</div>
-												{note.step < 0 ? steps.map((n) => <span class={styles.dot}></span>) : null}
-											</div>
-										</div>
-									);
-								})}
+												{/* {tone.step < 0 ? steps.map((n) => <span class={styles.dot}></span>) : null} */}
+											</Button>
+										);
+									})}
+								</div>
+							</div>
+							<div class={styles.toneAction}>
+								<Button type="primary" round plain onClick={() => (data.tnoteShow = false)}>
+									取消
+								</Button>
+								<Button type="primary" round onClick={() => (data.tnoteShow = false)}>
+									确定
+								</Button>
 							</div>
 						</div>
-						{/* <Button size="small" >
-							<Icon name="arrow" />
-						</Button> */}
-					</div>
+					</Popup>
 				</div>
 			);
 		};

+ 215 - 0
src/view/figner-preview/index.ts

@@ -185,8 +185,66 @@ export const FIGNER_INSTRUMENT_DATA: { [_: string]: IFIGNER_INSTRUMENT_DATA } =
 				realName: "F7",
 			},
 		],
+		tips: [
+			{
+				key: 0,
+				name: "呼吸",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName:
+					"使用腹式呼吸法。吸气时,最大限度地向外扩张腹部,胸部保持不动。呼气时,最大限度地向内收缩腹部,胸部保持不动。",
+			},
+			{
+				key: 0,
+				name: "吐音",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName:
+					"首先要形成一个正确的口风:微笑——嘴闭紧——嘴角向两边抻——下颌微微往前伸,不要露出牙齿。舌尖伸出—双唇压紧—迅速收回—同时吹气。反复练习这个动作,而且每吹一个音都要用吐音,只有掌握了正确的吐音方法以后,才可以进行演奏。建议对着镜子练习。",
+			},
+			{
+				key: 0,
+				name: "移动",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName:
+					"排箫一管一音,移动的准确性就成为重中之重,移动时,保持排箫的水平和垂直以及下唇和吹口的位置关系,而且每次移动都要对正管子。建议练习找管位时先从二度——三度——四度——五度——多度,总之每一次练习都应该是重复正确和纠正错误,开始时注意力很难兼顾三项,慢慢才能配合默契,形成一种下意识动作,直至“手到—嘴到—气到”。",
+			},
+		],
 	},
 	"hulusi-flute": {
+		tips: [
+			{
+				key: 0,
+				name: "单吐",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName:
+					"利用舌尖部顶住上腭前半部(即“吐”字发音前状态)截断气流,然后迅速地将舌放开,气息随之吹出。通过一顶一放的连续动作,使气流断续地进入吹口,便可以获得断续分奏的单吐效果,完成单吐的过程。单吐一般在音符上方用“T”标示。根据音乐表现的需要,单吐又可以分为断吐和连吐两种。",
+			},
+			{
+				key: 0,
+				name: "双吐",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName:
+					"双吐是用来完成连续快速分奏的技巧。首先用舌尖部顶住前上腭,然后将其放开,发出“吐”字。简言之,在“吐”字发出后,立即加发一个“苦”字,将“吐苦”二字连接起来便是双吐。双吐的符号是“TK”。",
+			},
+			{
+				key: 0,
+				name: "三吐",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName:
+					"三吐实际上是单吐和双吐在某种节奏型上的综合运用,符号为“TTK”或者“TKT”,即“吐吐苦”或者“吐苦吐”",
+			},
+		],
 		tones: [
 			{
 				key: 5,
@@ -1321,6 +1379,53 @@ export const FIGNER_INSTRUMENT_DATA: { [_: string]: IFIGNER_INSTRUMENT_DATA } =
 		],
 	},
 	piccolo: {
+		tips: [
+			{
+				key: 0,
+				name: "拿竖笛的方法",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName:
+					"把竖笛拿起来,然后放在嘴边,把它轻轻地放在你的唇间,并用你的手指拿稳。记住要把你的左手放在上边,有一个孔的背面应该正对着你,不要咬吹口或是让它碰到牙。",
+			},
+			{
+				key: 0,
+				name: "吹竖笛的力度",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName:
+					"向竖笛吹气,轻一点吹想象你在吹泡泡一样,控制气流的稳定并轻柔地吹奏,尝试用膈肌呼吸并确保你吹得均匀,这可以使声音稳定持续。",
+			},
+			{
+				key: 0,
+				name: "学习呼吸方法",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName:
+					"吹竖笛时,气息的控制是很关键的,气息分为缓吹法和急吹法,吸气要从鼻子和嘴角吸气,吸到胸部和腰部,小腹微微向里收,以使演奏有气息支持。",
+			},
+			{
+				key: 0,
+				name: "勤练习指法",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName:
+					"指法图是用来表示竖笛上的单音的,后背的孔叫0孔,从上到下依次为一孔、二孔、三孔、四孔、五孔、六孔、七孔,要吹奏简单的练习曲,必须要牢记单音的指法。",
+			},
+			{
+				key: 0,
+				name: "学习一些演奏技法",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName:
+					"单吐是用舌尖顶住上牙的牙根,用气息轻轻地把舌尖冲开,待声音发出后舌尖有弹性地返回到原来的位置,这时舌尖就像一个通气阀门,如果我们让它发出声音就像发“嘟”音的感觉。在有连线的地方只有第一个音采用单吐的技法,后面的音不再做吐音,只接前面“嘟”音的尾音发出“呜”音,整个连线里面的音就像“嘟呜”的。",
+			},
+		],
 		tones: [],
 		list: [
 			{
@@ -1553,6 +1658,62 @@ export const FIGNER_INSTRUMENT_DATA: { [_: string]: IFIGNER_INSTRUMENT_DATA } =
 		],
 	},
 	ocarina: {
+		tips: [
+			{
+				key: 0,
+				name: "嘴型",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName:
+					"以嘴唇轻轻含住吹口,不可含太深,以免盖住出气孔。\n吹气方式宜以丹田之力(腹腔)稳定送出,一可避免太多口水,二可产生振音效果,让笛韵更为悦耳。",
+			},
+			{
+				key: 0,
+				name: "长音",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName:
+					"长音的吹奏,要求音量平稳,不可忽大忽小,音与音之间,要流畅的连接,不可断断续续。\n短音与重音需运用“踢舌”的技巧,将舌尖抵住上腭牙齿与牙龈的相接处,于吹气时瞬间将舌头缩回,这样的声音干净有力,听起来有活泼欢乐的感觉。",
+			},
+			{
+				key: 0,
+				name: "滑音",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName:
+					"开合气孔的方式,一般是直接提高手指,音阶瞬间转换,另一种方式是将手指向陶笛外侧慢慢滑开,如此可以产生优美的滑音。",
+			},
+			{
+				key: 0,
+				name: "圆滑音",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName:
+					"在两个以上不同高的音符之间加上“”记号,此记号成为圆滑线,它的吹法是用一口气连接的吹完整串音符,中间气不间断,注意第一个音仍要踢舌!",
+			},
+			{
+				key: 0,
+				name: "气震音",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName:
+					"气震音是指在吹奏中用气流的变化来让发出的音发生波动,产生颤抖的感觉,所以又称气颤音,靠腹部控制,又叫腹震音。",
+			},
+			{
+				key: 0,
+				name: "呼吸方法",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName:
+					"使用胸腹式呼吸法是一种比较科学的呼吸方法,大致分为两个步骤,吸气速度要快,胸腹要做到联合动作,让胸腹快速吸到更多的气,也可以用口协助吸气。",
+			},
+		],
 		tones: [
 			{
 				key: 1,
@@ -3802,6 +3963,60 @@ export const FIGNER_INSTRUMENT_DATA: { [_: string]: IFIGNER_INSTRUMENT_DATA } =
 		],
 	},
 	melodica: {
+		tips: [
+			{
+				key: 0,
+				name: "演奏姿势",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName: "口风琴的基本演奏姿势有两种,即坐奏式和立奏式两种。",
+			},
+			{
+				key: 0,
+				name: "坐奏式",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName: "将口风琴平放在桌上,左手持长吹管前面的吹嘴,有手在琴键上演奎。要求身体端正。",
+			},
+			{
+				key: 0,
+				name: "立奏式",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName:
+					"口含短吹管,左手持琴,右手在琴键上演奏。琴身倾斜约45度。\n注:无论是坐奏式还是立奏式,都要注意口含吹嘴少些。",
+			},
+			{
+				key: 0,
+				name: "呼吸方法",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName:
+					"呼吸吹奏口风琴时的呼吸与唱歌和演奏其他吹管乐器时的呼吸方法基本一致,即采用口鼻并用的胸腹式呼吸法。请记住下面这一句:深吸气,要保持,均匀平稳呼出去。",
+			},
+			{
+				key: 0,
+				name: "吐舌法",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName:
+					"与演奏其他管乐器一样,演奏口风琴时,吐舌法是十分重要的。其基本方法是舌尖轻触上牙床,并靠拢前肠,气流通过。",
+			},
+			{
+				key: 0,
+				name: "基本指法",
+				octave: 0,
+				step: 0,
+				realKey: 0,
+				realName:
+					"口风琴的指法与钢琴、风琴电子琴、手风琴等键盘乐器基本上相同,其区别在于使用它作同音重复演奏时,手指不动,而是用吐舌法来完成。\n演奏口风琴时,右手手指编了号:拇指为1号指,食指为2号指,中指为3号指,无名指为4号指,小指为五号指。与演奏其他键盘乐器一样,演奏口风琴时要求手指自然弯曲,以指尖触键,手腕平放,手心如提球状。",
+			},
+		],
 		tones: [],
 		list: [
 			{

+ 6 - 0
src/view/fingering/fingering-config.ts

@@ -18,6 +18,7 @@ export type IFingering = {
 	disabledFinger?: boolean;
 	/** 横竖屏 0:横屏 1: 竖屏 */
 	orientation?: number;
+	code?: string;
 };
 
 type ITypeContent = {
@@ -184,6 +185,7 @@ export const subjectFingering = (subjectId: number | string): IFingering => {
 				direction: "vertical",
 				width: "3rem",
 				orientation: 1,
+				code: '竖笛'
 			};
 		case "hulusi-flute": // 葫芦丝
 			return {
@@ -191,6 +193,7 @@ export const subjectFingering = (subjectId: number | string): IFingering => {
 				direction: "vertical",
 				width: "3rem",
 				orientation: 1,
+				code: '葫芦丝'
 			};
 		case "pan-flute": // 排箫
 			return {
@@ -199,6 +202,7 @@ export const subjectFingering = (subjectId: number | string): IFingering => {
 				height: "2.6rem",
 				disabledFinger: true,
 				orientation: 0,
+				code: '排箫'
 			};
 		case "ocarina": // 陶笛
 			return {
@@ -207,6 +211,7 @@ export const subjectFingering = (subjectId: number | string): IFingering => {
 				width: "3rem",
 				disabledFinger: true,
 				orientation: 0,
+				code: '陶笛'
 			};
 		case "melodica": // 口风琴
 			return {
@@ -214,6 +219,7 @@ export const subjectFingering = (subjectId: number | string): IFingering => {
 				direction: "transverse",
 				height: "1.8rem",
 				orientation: 0,
+				code: '口风琴'
 			};
 		default:
 			return {};

Some files were not shown because too many files changed in this diff