123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- import { PropType, computed, defineComponent, nextTick, onBeforeMount, onMounted, 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";
- import Hammer from "hammerjs";
- import { Button, Icon, Popup, Space } from "vant";
- import GuideIndex from "./guide/guide-index";
- 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({
- loading: true,
- subject: subject,
- realKey: 0,
- notes: [] as IFIGNER_INSTRUMENT_Note[],
- tones: [] as IFIGNER_INSTRUMENT_Note[],
- activeTone: {} as IFIGNER_INSTRUMENT_Note,
- activeToneName: "",
- soundFonts: {} as any,
- viewIndex: 0,
- noteAudio: null as unknown as Howl,
- transform: {
- scale: 1,
- x: 0,
- y: 0,
- startScale: 1,
- startX: 0,
- startY: 0,
- transition: "",
- },
- tipShow: false,
- tips: [] as IFIGNER_INSTRUMENT_Note[],
- tnoteShow: false,
- });
- 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.tones = fignerData.tones || [];
- if (data.tones.length) {
- data.activeTone = data.tones[0];
- }
- data.tips = fignerData.tips || [];
- setNotes();
- setTimeout(() => {
- data.loading = false;
- }, 600);
- }
- };
- const setNotes = () => {
- const fignerData = FIGNER_INSTRUMENT_DATA[data.subject as keyof typeof FIGNER_INSTRUMENT_DATA];
- if (fignerData) {
- data.notes = fignerData[`list${data.activeTone.realName || ""}`];
- }
- };
- const getFingeringData = async () => {
- const subject: any = data.subject + (data.viewIndex === 0 ? "" : data.viewIndex);
- // console.log("🚀 ~ subject:", subject);
- fingerData.subject = await getFingeringConfig(subject);
- };
- const getSounFonts = () => {
- const pathname = /(192|localhost)/.test(location.origin) ? "/" : location.pathname;
- for (let i = 0; i < data.notes.length; i++) {
- const note = data.notes[i];
- // console.log("🚀 ~ note:", i)
- let url = `${pathname}soundfonts/${data.subject}/`;
- url += note.realName;
- url += ".mp3";
- const noteAudio = new Howl({
- src: url,
- loop: true,
- });
- data.soundFonts[note.realKey] = noteAudio;
- }
- // console.log("🚀 ~ data.soundFonts:", data.soundFonts);
- };
- onBeforeMount(() => {
- getNotes();
- getFingeringData();
- getSounFonts();
- });
- const noteClick = (item: IFIGNER_INSTRUMENT_Note) => {
- if (data.noteAudio) {
- data.noteAudio.stop();
- if (data.realKey === item.realKey) {
- data.realKey = 0;
- data.noteAudio = null as unknown as Howl;
- return;
- }
- }
- data.realKey = item.realKey;
- data.noteAudio = data.soundFonts[item.realKey];
- data.noteAudio.play();
- };
- /** 返回 */
- const handleBack = () => {
- if (data.noteAudio) {
- data.noteAudio.stop();
- data.realKey = 0;
- data.noteAudio = null as unknown as Howl;
- }
- if (props.isComponent) {
- console.log("关闭");
- emit("close");
- return;
- }
- // 不在APP中,
- if (!storeData.isApp) {
- window.close();
- return;
- }
- api_back();
- };
- onMounted(() => {
- loadElement();
- });
- const loadElement = () => {
- const fingeringContainer = document.getElementById("fingeringContainer");
- // console.log("🚀 ~ fingeringContainer:", fingeringContainer);
- const mc = new Hammer.Manager(fingeringContainer as HTMLElement);
- mc.add(new Hammer.Pan({ threshold: 0, pointers: 0 }));
- mc.add(new Hammer.Pinch({ threshold: 0 })).recognizeWith([mc.get("pan")]);
- // mc.get("pan").set({ direction: Hammer.DIRECTION_ALL });
- // mc.get("pinch").set({ enable: true });
- mc.on("panstart pinchstart", function (ev) {
- data.transform.transition = "";
- });
- mc.on("panmove pinchmove", function (ev) {
- if (ev.type === "pinchmove") {
- // console.log("🚀 ~ ev:", ev.type, ev.scale, ev.deltaX, ev.deltaY);
- data.transform.scale = ev.scale * data.transform.startScale;
- data.transform.x = data.transform.startX + ev.deltaX;
- data.transform.y = data.transform.startY + ev.deltaY;
- }
- if (ev.type === "panmove") {
- // console.log("🚀 ~ ev:", ev.type, ev.deltaX, ev.deltaY);
- data.transform.x = data.transform.startX + ev.deltaX;
- data.transform.y = data.transform.startY + ev.deltaY;
- }
- });
- //
- mc.on("hammer.input", function (ev) {
- // console.log("🚀 ~ ev:", ev.type, ev.isFinal);
- if (ev.isFinal) {
- data.transform.startScale = data.transform.scale;
- data.transform.startX = data.transform.x;
- data.transform.startY = data.transform.y;
- }
- });
- };
- const resetElement = () => {
- data.transform.transition = "all 0.3s";
- nextTick(() => {
- data.transform.scale = 1;
- data.transform.x = 0;
- data.transform.y = 0;
- data.transform.startScale = 1;
- data.transform.startX = 0;
- data.transform.startY = 0;
- });
- };
- 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}
- onClick={() => {
- data.viewIndex++;
- if (data.viewIndex > 2) {
- data.viewIndex = 0;
- }
- getFingeringData();
- }}
- >
- 切换视图
- </div>
- )}
- </div>
- <div class={styles.rightBtn}>
- <div class={[styles.item]} onClick={() => resetElement()}>
- <img src={icons.icon_2_0} />
- <span>还原</span>
- </div>
- <div
- 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
- id="finger-note-2"
- class={[styles.tizhi, canTizhi && styles.canDisplay]}
- onClick={() =>
- (fingerData.relationshipIndex = fingerData.relationshipIndex === 0 ? 1 : 0)
- }
- >
- 替指
- </div>
- </div>
- </div>
- <div class={styles.notes}>
- <Button class={styles.noteBtn}>
- <Icon name="arrow-left" />
- </Button>
- <div class={styles.noteContent}>
- <div class={styles.noteBox}>
- {data.notes.map((note: IFIGNER_INSTRUMENT_Note, index: number) => {
- const steps = new Array(Math.abs(note.step)).fill(1);
- return (
- <div
- id={index == 0 ? "finger-note-0" : ""}
- 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.realKey === note.realKey && styles.keyActive]}
- >
- {note.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 class={styles.noteBtn}>
- <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>
- {!!data.tones.length && (
- <div id="finger-note-1" 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>
- )}
- <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" }}>
- <Space size={0} 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>{tone.mark && (tone.mark === "rise" ? "#" : "b")}</sup>
- {tone.name}
- </div>
- {/* {tone.step < 0 ? steps.map((n) => <span class={styles.dot}></span>) : null} */}
- </Button>
- );
- })}
- </Space>
- </div>
- <Space size={0} class={styles.toneAction}>
- <Button type="primary" round plain onClick={() => (data.tnoteShow = false)}>
- 取消
- </Button>
- <Button type="primary" round onClick={() => (data.tnoteShow = false)}>
- 确定
- </Button>
- </Space>
- </div>
- </Popup>
- {!data.loading && <GuideIndex list={["finger"]} />}
- </div>
- );
- };
- },
- });
|