index.tsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. import { PropType, computed, defineComponent, onBeforeMount, reactive } from "vue";
  2. import styles from "./index.module.less";
  3. import icons from "./image/icons.json";
  4. import { FIGNER_INSTRUMENT_DATA, IFIGNER_INSTRUMENT_Note } from "/src/view/figner-preview";
  5. import {
  6. ITypeFingering,
  7. IVocals,
  8. getFingeringConfig,
  9. subjectFingering,
  10. } from "/src/view/fingering/fingering-config";
  11. import { Howl } from "howler";
  12. import { storeData } from "/src/store";
  13. import { api_back } from "/src/helpers/communication";
  14. import { Button, Icon } from "vant";
  15. export default defineComponent({
  16. name: "viewFigner",
  17. emits: ["close"],
  18. props: {
  19. isComponent: {
  20. type: Boolean,
  21. default: false,
  22. },
  23. subject: {
  24. type: String as PropType<IVocals>,
  25. default: "",
  26. },
  27. },
  28. setup(props, { emit }) {
  29. const subject = props.subject || "pan-flute";
  30. const data = reactive({
  31. subject: subject,
  32. realKey: 0,
  33. notes: [] as IFIGNER_INSTRUMENT_Note[],
  34. soundFonts: {} as any,
  35. viewIndex: 0,
  36. });
  37. const fingerData = reactive({
  38. relationshipIndex: 0,
  39. subject: null as unknown as ITypeFingering,
  40. fingeringInfo: subjectFingering(data.subject),
  41. });
  42. const getNotes = () => {
  43. const fignerData = FIGNER_INSTRUMENT_DATA[data.subject as keyof typeof FIGNER_INSTRUMENT_DATA];
  44. if (fignerData) {
  45. data.notes = fignerData.list;
  46. }
  47. };
  48. const getFingeringData = async () => {
  49. const subject: any = data.subject + (data.viewIndex === 0 ? "" : data.viewIndex);
  50. fingerData.subject = await getFingeringConfig(subject);
  51. };
  52. const getSounFonts = () => {
  53. const pathname = /(192|localhost)/.test(location.origin) ? "/" : location.pathname;
  54. for (let i = 0; i < data.notes.length; i++) {
  55. const note = data.notes[i];
  56. // console.log("🚀 ~ note:", i)
  57. let url = `${pathname}soundfonts/${data.subject}/`;
  58. url += note.realName;
  59. url += ".mp3";
  60. const noteAudio = new Howl({
  61. src: url,
  62. loop: true,
  63. });
  64. data.soundFonts[note.realKey] = noteAudio;
  65. }
  66. // console.log("🚀 ~ data.soundFonts:", data.soundFonts);
  67. };
  68. onBeforeMount(() => {
  69. getNotes();
  70. getFingeringData();
  71. getSounFonts();
  72. });
  73. const noteClick = (item: IFIGNER_INSTRUMENT_Note, type: "start" | "stop") => {
  74. // console.log("🚀 ~ item:", item);
  75. const noteAudio = data.soundFonts[item.realKey];
  76. if (type === "start") {
  77. data.realKey = item.realKey;
  78. if (noteAudio) {
  79. noteAudio.play();
  80. }
  81. } else {
  82. data.realKey = 0;
  83. if (noteAudio) {
  84. noteAudio.stop();
  85. }
  86. }
  87. };
  88. /** 返回 */
  89. const handleBack = () => {
  90. if (props.isComponent) {
  91. console.log("关闭");
  92. emit("close");
  93. return;
  94. }
  95. // 不在APP中,
  96. if (!storeData.isApp) {
  97. window.close();
  98. return;
  99. }
  100. api_back();
  101. };
  102. return () => {
  103. const relationship = fingerData.subject?.relationship?.[data.realKey] || [];
  104. const rs: number[] = Array.isArray(relationship[1])
  105. ? relationship[fingerData.relationshipIndex]
  106. : relationship;
  107. const canTizhi = Array.isArray(relationship[1]);
  108. return (
  109. <div class={styles.fingerBox}>
  110. <div class={styles.head}>
  111. <div class={styles.left}>
  112. <button
  113. class={[styles.backBtn, data.subject === "pan-flute" && styles.backRight]}
  114. onClick={() => handleBack()}
  115. >
  116. <img src={icons.icon_back} />
  117. </button>
  118. {data.subject === "pan-flute" && (
  119. <div
  120. class={styles.baseBtn}
  121. onClick={() => {
  122. data.viewIndex++;
  123. if (data.viewIndex > 1) {
  124. data.viewIndex = 0;
  125. }
  126. getFingeringData();
  127. }}
  128. >
  129. 切换视图
  130. </div>
  131. )}
  132. </div>
  133. <div class={styles.rightBtn}>
  134. <div class={[styles.item, styles.disabled]}>
  135. <img src={icons.icon_2_0} />
  136. <span>还原</span>
  137. </div>
  138. <div class={[styles.item, styles.disabled]}>
  139. <img src={icons.icon_2_1} />
  140. <span>小技巧</span>
  141. </div>
  142. </div>
  143. </div>
  144. <div class={styles.fingerContent}>
  145. <div class={[styles.fingeringContainer]}>
  146. <div class={styles.imgs}>
  147. <img src={fingerData.subject?.json?.full} />
  148. {rs.map((key: number | string, index: number) => {
  149. const nk: string = typeof key === "string" ? key.replace("active-", "") : String(key);
  150. return <img data-index={nk} src={fingerData.subject?.json?.[nk]} />;
  151. })}
  152. </div>
  153. <div
  154. class={[styles.tizhi, canTizhi && styles.canDisplay]}
  155. onClick={() =>
  156. (fingerData.relationshipIndex = fingerData.relationshipIndex === 0 ? 1 : 0)
  157. }
  158. >
  159. 替指
  160. </div>
  161. </div>
  162. </div>
  163. <div class={styles.notes}>
  164. {/* <Button>
  165. <Icon name="arrow-left" />
  166. </Button> */}
  167. <div class={styles.noteBox}>
  168. {data.notes.map((note: IFIGNER_INSTRUMENT_Note) => {
  169. const steps = new Array(Math.abs(note.step)).fill(1);
  170. return (
  171. <div
  172. draggable={false}
  173. class={styles.note}
  174. onKeydown={() => noteClick(note, "start")}
  175. onKeyup={() => noteClick(note, "start")}
  176. onMouseleave={() => noteClick(note, "stop")}
  177. onTouchstart={() => noteClick(note, "start")}
  178. onTouchend={() => noteClick(note, "stop")}
  179. onTouchcancel={() => noteClick(note, "stop")}
  180. >
  181. {data.realKey === note.realKey ? (
  182. <img draggable={false} src={icons.icon_btn_ylow} />
  183. ) : (
  184. <img draggable={false} src={icons.icon_btn_blue} />
  185. )}
  186. <div class={[styles.noteKey, data.realKey === note.realKey && styles.keyActive]}>
  187. {note.step > 0 ? steps.map((n) => <span class={styles.dot}></span>) : null}
  188. <div class={styles.noteName}>
  189. <sup>
  190. {note.mark && (note.mark === "rise" ? "#" : "b")}
  191. </sup>
  192. {note.key}
  193. </div>
  194. {note.step < 0 ? steps.map((n) => <span class={styles.dot}></span>) : null}
  195. </div>
  196. </div>
  197. );
  198. })}
  199. </div>
  200. {/* <Button size="small" >
  201. <Icon name="arrow" />
  202. </Button> */}
  203. </div>
  204. </div>
  205. );
  206. };
  207. },
  208. });