|
@@ -0,0 +1,178 @@
|
|
|
+import { PropType, computed, defineComponent, onBeforeMount, reactive } from "vue";
|
|
|
+import styles from "./index.module.less";
|
|
|
+import icons from "./image/icons.json";
|
|
|
+import { FIGNER_INSTRUMENT_DATA, IFIGNER_INSTRUMENT_Note } from "/src/view/figner-preview";
|
|
|
+import {
|
|
|
+ ITypeFingering,
|
|
|
+ IVocals,
|
|
|
+ getFingeringConfig,
|
|
|
+ subjectFingering,
|
|
|
+} from "/src/view/fingering/fingering-config";
|
|
|
+import { Howl } from "howler";
|
|
|
+import { storeData } from "/src/store";
|
|
|
+import { api_back } from "/src/helpers/communication";
|
|
|
+
|
|
|
+export default defineComponent({
|
|
|
+ name: "viewFigner",
|
|
|
+ emits: ["close"],
|
|
|
+ props: {
|
|
|
+ isComponent: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false,
|
|
|
+ },
|
|
|
+ subject:{
|
|
|
+ type: String as PropType<IVocals>,
|
|
|
+ default: ""
|
|
|
+ }
|
|
|
+ },
|
|
|
+ setup(props, { emit }) {
|
|
|
+ const subject = props.subject || "pan-flute"
|
|
|
+ const data = reactive({
|
|
|
+ subject: subject,
|
|
|
+ realKey: 0,
|
|
|
+ notes: [] as IFIGNER_INSTRUMENT_Note[],
|
|
|
+ soundFonts: {} as any,
|
|
|
+ });
|
|
|
+ const fingerData = reactive({
|
|
|
+ relationshipIndex: 0,
|
|
|
+ subject: null as unknown as ITypeFingering,
|
|
|
+ fingeringInfo: subjectFingering(data.subject),
|
|
|
+ });
|
|
|
+ const getNotes = () => {
|
|
|
+ const fignerData = FIGNER_INSTRUMENT_DATA[data.subject as keyof typeof FIGNER_INSTRUMENT_DATA];
|
|
|
+ if (fignerData) {
|
|
|
+ data.notes = fignerData.list;
|
|
|
+ }
|
|
|
+ };
|
|
|
+ const getFingeringData = async () => {
|
|
|
+ fingerData.subject = await getFingeringConfig(data.subject);
|
|
|
+ };
|
|
|
+ const getSounFonts = () => {
|
|
|
+ for (let i = 0; i < data.notes.length; i++) {
|
|
|
+ const note = data.notes[i];
|
|
|
+ // console.log("🚀 ~ note:", note)
|
|
|
+ // console.log(`/soundfonts/${data.subject}/${note.name}${note.octave}.mp3`)
|
|
|
+ const noteAudio = new Howl({
|
|
|
+ src: `/soundfonts/${data.subject}/${note.name}${note.octave}.mp3`,
|
|
|
+ loop: true,
|
|
|
+ });
|
|
|
+ data.soundFonts[note.realKey] = noteAudio;
|
|
|
+ }
|
|
|
+ // console.log("🚀 ~ data.soundFonts:", data.soundFonts);
|
|
|
+ };
|
|
|
+ onBeforeMount(() => {
|
|
|
+ getNotes();
|
|
|
+ getFingeringData();
|
|
|
+ getSounFonts();
|
|
|
+ });
|
|
|
+
|
|
|
+ const noteClick = (item: IFIGNER_INSTRUMENT_Note, type: "start" | "stop") => {
|
|
|
+ // console.log("🚀 ~ item:", item);
|
|
|
+ const noteAudio = data.soundFonts[item.realKey];
|
|
|
+ if (type === "start") {
|
|
|
+ data.realKey = item.realKey;
|
|
|
+ if (noteAudio) {
|
|
|
+ noteAudio.play();
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ data.realKey = 0;
|
|
|
+ if (noteAudio) {
|
|
|
+ noteAudio.stop();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ /** 返回 */
|
|
|
+ const handleBack = () => {
|
|
|
+ if (props.isComponent) {
|
|
|
+ console.log("关闭");
|
|
|
+ emit("close");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 不在APP中,
|
|
|
+ if (!storeData.isApp) {
|
|
|
+ window.close();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ api_back();
|
|
|
+ };
|
|
|
+
|
|
|
+ return () => {
|
|
|
+ const relationship = fingerData.subject?.relationship?.[data.realKey] || [];
|
|
|
+ const rs: number[] = Array.isArray(relationship[1])
|
|
|
+ ? relationship[fingerData.relationshipIndex]
|
|
|
+ : relationship;
|
|
|
+ const canTizhi = Array.isArray(relationship[1]);
|
|
|
+ return (
|
|
|
+ <div class={styles.fingerBox}>
|
|
|
+ <div class={styles.head}>
|
|
|
+ <div class={styles.left}>
|
|
|
+ <button class={[styles.backBtn, data.subject === 'pan-flute' && styles.backRight]} onClick={() => handleBack()}>
|
|
|
+ <img src={icons.icon_back} />
|
|
|
+ </button>
|
|
|
+ {data.subject === 'pan-flute' && <div class={styles.baseBtn}>切换视图</div>}
|
|
|
+ </div>
|
|
|
+ <div class={styles.rightBtn}>
|
|
|
+ <div class={styles.item}>
|
|
|
+ <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 class={styles.fingerContent}>
|
|
|
+ <div 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)
|
|
|
+ }
|
|
|
+ >
|
|
|
+ 替指
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class={styles.notes}>
|
|
|
+ <div class={styles.noteBox}>
|
|
|
+ {data.notes.map((note: IFIGNER_INSTRUMENT_Note) => {
|
|
|
+ const steps = new Array(Math.abs(note.step)).fill(1);
|
|
|
+ return (
|
|
|
+ <div
|
|
|
+ class={styles.note}
|
|
|
+ onKeydown={() => noteClick(note, "start")}
|
|
|
+ onKeyup={() => noteClick(note, "start")}
|
|
|
+ onTouchstart={() => noteClick(note, "start")}
|
|
|
+ onTouchend={() => noteClick(note, "stop")}
|
|
|
+ >
|
|
|
+ {data.realKey === note.realKey ? (
|
|
|
+ <img src={icons.icon_btn_ylow} />
|
|
|
+ ) : (
|
|
|
+ <img src={icons.icon_btn_blue} />
|
|
|
+ )}
|
|
|
+
|
|
|
+ <div class={styles.noteKey}>
|
|
|
+ {note.step > 0 ? steps.map((n) => <span class={styles.dot}></span>) : null}
|
|
|
+ <span>{note.key}</span>
|
|
|
+ {note.step < 0 ? steps.map((n) => <span class={styles.dot}></span>) : null}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ })}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ };
|
|
|
+ },
|
|
|
+});
|