|
@@ -0,0 +1,793 @@
|
|
|
|
+import { ref } from "vue";
|
|
|
|
+import state, { customData } from "../state"
|
|
|
|
+import { getQuery } from "/src/utils/queryString";
|
|
|
|
+import { setGlobalData } from "/src/utils";
|
|
|
|
+const query: any = getQuery();
|
|
|
|
+
|
|
|
|
+interface IItem {
|
|
|
|
+ id?: string
|
|
|
|
+ y?: number
|
|
|
|
+ isLast?: boolean
|
|
|
|
+ childIndex?: number[]
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+interface IItemList {
|
|
|
|
+ parts: string[],
|
|
|
|
+ tieId?: string[],
|
|
|
|
+ staveSection?: IItem[],
|
|
|
|
+ vfmodifiers?: IItem[],
|
|
|
|
+ voltas?: number
|
|
|
|
+ vfcurve?: IItem[]
|
|
|
|
+ stavenote?: IItem[]
|
|
|
|
+}
|
|
|
|
+interface IMusicList {
|
|
|
|
+ [_key: string]: IItemList[]
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const container = ref();
|
|
|
|
+
|
|
|
|
+/** 曲谱配置: 重叠 */
|
|
|
|
+export const resetGivenFormate = () => {
|
|
|
|
+ interface IItem {
|
|
|
|
+ id?: string
|
|
|
|
+ y?: number
|
|
|
|
+ isLast?: boolean
|
|
|
|
+ childIndex?: number[]
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ interface IItemList {
|
|
|
|
+ parts: string[],
|
|
|
|
+ tieId?: string[],
|
|
|
|
+ staveSection?: IItem[],
|
|
|
|
+ vfmodifiers?: IItem[],
|
|
|
|
+ voltas?: number
|
|
|
|
+ vfcurve?: IItem[]
|
|
|
|
+ stavenote?: IItem[]
|
|
|
|
+ }
|
|
|
|
+ interface IMusicList {
|
|
|
|
+ [_key: string]: IItemList[]
|
|
|
|
+ }
|
|
|
|
+ const musicList: IMusicList = {
|
|
|
|
+ '12200': [
|
|
|
|
+ {parts: ['0', '1'], tieId: ['1483']},
|
|
|
|
+ {parts: ['2'], tieId: ['1463']},
|
|
|
|
+ {parts: ['10'], tieId: ['1246']},
|
|
|
|
+ {parts: ['11'], tieId: ['2455']},
|
|
|
|
+ {parts: ['13'], tieId: ['1488', '1688']},
|
|
|
|
+ {parts: ['14', '15'], tieId: ['1272']},
|
|
|
|
+ {parts: ['16'], tieId: ['1264', '1368'], staveSection: [{id: 'section-0', y: -10}]},
|
|
|
|
+ ],
|
|
|
|
+ '12420': [
|
|
|
|
+ {parts: ['0'], tieId: ['1298', '1405', '1998', '2598', '3229', '2731', '2617']}
|
|
|
|
+ ],
|
|
|
|
+ '7729': [
|
|
|
|
+ {parts: ['3'], tieId: ['1498', '1660']}
|
|
|
|
+ ],
|
|
|
|
+ '7439': [
|
|
|
|
+ {parts: ['23'], vfmodifiers: [{id: 'modifiers-130', y: -18, isLast: true}]}
|
|
|
|
+ ],
|
|
|
|
+ '12711': [
|
|
|
|
+ { parts: ['0'], voltas: -12},
|
|
|
|
+ { parts: ['4'],voltas: -8},
|
|
|
|
+ ],
|
|
|
|
+ '3581': [
|
|
|
|
+ { parts: ['0'], voltas: -8},
|
|
|
|
+ ],
|
|
|
|
+ '6244': [
|
|
|
|
+ { parts: ['15'], stavenote: [{id: 'vf-auto1608', y: -15}]},
|
|
|
|
+ ],
|
|
|
|
+ '7473': [
|
|
|
|
+ { parts: ['0'], voltas: -8},
|
|
|
|
+ ]
|
|
|
|
+ }
|
|
|
|
+ const tieList = musicList[state.cbsExamSongId as string]
|
|
|
|
+ if (tieList) {
|
|
|
|
+ const partIndex = query["part-index"] || '0'
|
|
|
|
+ const tie = tieList.find((item) => item.parts.includes(partIndex))
|
|
|
|
+ if (!tie) return
|
|
|
|
+ // 延音线和连线重叠
|
|
|
|
+ if (tie.tieId && tie.tieId.length) {
|
|
|
|
+ for(let tieIndex = 0; tieIndex < tie.tieId.length; tieIndex++){
|
|
|
|
+ const vftie: any = document.querySelector(`#vf-auto${tie.tieId[tieIndex]}-tie`)
|
|
|
|
+ const vfcurve = vftie?.parentNode?.parentNode?.querySelectorAll('.vf-curve')
|
|
|
|
+ if (vfcurve && vfcurve.length){
|
|
|
|
+ for(let i = 0; i < vfcurve.length; i++){
|
|
|
|
+ const result = collisionDetection(vftie, vfcurve[i])
|
|
|
|
+ if (result.isCollision){
|
|
|
|
+ vfcurve[i].style.transform = `translateY(-8px)`;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 小节数字
|
|
|
|
+ if (tie.staveSection && tie.staveSection.length) {
|
|
|
|
+ const sectionList = document.querySelectorAll('.vf-StaveSection')
|
|
|
|
+ sectionList.forEach((node, index) => {
|
|
|
|
+ node.classList.add(`section-${index}`)
|
|
|
|
+ })
|
|
|
|
+ for(let i = 0; i < tie.staveSection.length; i++){
|
|
|
|
+ const item: any = document.querySelector( '.' + tie.staveSection[i].id)
|
|
|
|
+ if (item){
|
|
|
|
+ item.style.transform = `translateY(${tie.staveSection[i].y}px)`;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // modifiers 里面的符号
|
|
|
|
+ if(tie.vfmodifiers && tie.vfmodifiers.length){
|
|
|
|
+ const modifierList = document.querySelectorAll('.vf-modifiers')
|
|
|
|
+ modifierList.forEach((node, index) => {
|
|
|
|
+ node.classList.add(`modifiers-${index}`)
|
|
|
|
+ })
|
|
|
|
+ for(let i = 0; i < tie.vfmodifiers.length; i++){
|
|
|
|
+ const modifier = tie.vfmodifiers[i]
|
|
|
|
+ const item: SVGAElement = document.querySelector( '.' + modifier.id)!
|
|
|
|
+ if (item){
|
|
|
|
+ if (modifier.isLast){
|
|
|
|
+ const lastEle: any = Array.from(item.childNodes).at(-1)
|
|
|
|
+ if (lastEle){
|
|
|
|
+ lastEle.style.transform = `translateY(${modifier.y}px)`;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 房子
|
|
|
|
+ if (tie.voltas){
|
|
|
|
+ const modifierList = document.querySelectorAll('.vf-Volta') as unknown as HTMLElement[]
|
|
|
|
+ modifierList.forEach((node, index) => {
|
|
|
|
+ node.style.transform = `translateY(${tie.voltas}px)`;
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 单个音符
|
|
|
|
+ if (tie.stavenote && tie.stavenote.length) {
|
|
|
|
+ for(let i = 0; i < tie.stavenote.length; i++){
|
|
|
|
+ const item = tie.stavenote[i]
|
|
|
|
+ const ele = document.querySelector('#' + item.id)! as unknown as HTMLElement
|
|
|
|
+ ele && (ele.style.transform = `translateY(${item.y}px)`)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// 谱面优化
|
|
|
|
+export const resetFormate = () => {
|
|
|
|
+ container.value = document.getElementById('scrollContainer')
|
|
|
|
+ if (state.extStyleConfigJson || !container.value) return;
|
|
|
|
+ const stafflines: SVGAElement[] = Array.from((container.value as HTMLElement).querySelectorAll(".staffline"));
|
|
|
|
+ const baseStep = 4; // 两个元素相间,的间距
|
|
|
|
+ const musicalDistance = 28; // 音阶与第一条线谱的间距,默认设置为28
|
|
|
|
+ for (let i = 0, len = stafflines.length; i < len; i++) {
|
|
|
|
+ const staffline = stafflines[i];
|
|
|
|
+ const stafflineBox = staffline.getBBox();
|
|
|
|
+ const stafflineCenter = stafflineBox.y + stafflineBox.height / 2;
|
|
|
|
+ const vfmeasures: SVGAElement[] = Array.from(staffline.querySelectorAll(".vf-measure"));
|
|
|
|
+ const vfcurve: SVGAElement[] = Array.from(staffline.querySelectorAll(".vf-curve"));
|
|
|
|
+ const vfvoices: SVGAElement[] = Array.from(staffline.querySelectorAll(".vf-measure > .vf-voices"));
|
|
|
|
+ const vfbeams: SVGAElement[] = Array.from(staffline.querySelectorAll(".vf-measure > .vf-beams"));
|
|
|
|
+ const vfties: SVGAElement[] = Array.from(staffline.querySelectorAll(".vf-ties"));
|
|
|
|
+ const vflines: SVGAElement[] = Array.from(staffline.querySelectorAll(".vf-line"));
|
|
|
|
+ const texts: SVGAElement[] = Array.from(staffline.querySelectorAll(".vf-measure > .vf-stave text"));
|
|
|
|
+ const rects: SVGAElement[] = Array.from(staffline.querySelectorAll(".vf-measure > .vf-stave rect[fill=none]"));
|
|
|
|
+ const staveSection: SVGAElement[] = Array.from(staffline.querySelectorAll(".vf-measure .vf-staveSection"));
|
|
|
|
+ const paths: SVGAElement[] = Array.from(staffline.querySelectorAll(".vf-measure > .vf-stave path"));
|
|
|
|
+ // 获取第一个线谱的y轴坐标
|
|
|
|
+ const firstLinePathY = paths[0]?.getBBox().y || 0
|
|
|
|
+ // 反复标记 和 小节碰撞
|
|
|
|
+ const repetWord = ["To Coda", "D.S. al Coda", "Coda"];
|
|
|
|
+ texts
|
|
|
|
+ .filter((n) => repetWord.includes(n.textContent || ""))
|
|
|
|
+ .forEach((t) => {
|
|
|
|
+ vfbeams.forEach((curve) => {
|
|
|
|
+ const result = collisionDetection(t, curve);
|
|
|
|
+ const prePath: SVGAElement = t?.previousSibling as unknown as SVGAElement;
|
|
|
|
+ if (result.isCollision) {
|
|
|
|
+ const shift_y = Number(t.getAttribute("y")) - (result.b1 - result.t2) - baseStep + "";
|
|
|
|
+ t.setAttribute("y", shift_y);
|
|
|
|
+ // console.log('音阶间距',shift_y)
|
|
|
|
+ if (prePath && prePath.getAttribute("stroke-width") === "0.3" && prePath.getAttribute("stroke") === "none" && (prePath.getAttribute("d")?.length || 0) > 3000) {
|
|
|
|
+ prePath.style.transform = `translateY(${-(result.b1 - result.t2 + baseStep)}px)`;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ vfvoices.forEach((curve) => {
|
|
|
|
+ const result = collisionDetection(t, curve);
|
|
|
|
+ const prePath: SVGAElement = t?.previousSibling as unknown as SVGAElement;
|
|
|
|
+ if (result.isCollision) {
|
|
|
|
+ const shift_y = Number(t.getAttribute("y")) - (result.b1 - result.t2) - baseStep + "";
|
|
|
|
+ t.setAttribute("y", shift_y);
|
|
|
|
+ // console.log('音阶间距',shift_y)
|
|
|
|
+ if (prePath && prePath.getAttribute("stroke-width") === "0.3" && prePath.getAttribute("stroke") === "none" && (prePath.getAttribute("d")?.length || 0) > 3000) {
|
|
|
|
+ prePath.style.transform = `translateY(${-(result.b1 - result.t2 + baseStep)}px)`;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ });
|
|
|
|
+ // 文字方框和飞线碰撞
|
|
|
|
+ staveSection.forEach((t) => {
|
|
|
|
+ let shift_y = 0;
|
|
|
|
+ [...vfcurve, ...vfties, ...vfvoices].forEach((curve) => {
|
|
|
|
+ const result = collisionDetection(t, curve);
|
|
|
|
+ if (result.isCollision) {
|
|
|
|
+ shift_y = Math.min(shift_y, result.t2 - result.b1 - baseStep);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ t.style.transform = `translateY(${shift_y}px)`;
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 文字和小节碰撞
|
|
|
|
+ let vftexts = Array.from(staffline.querySelectorAll(".vf-text > text")).filter((n: any) => n.getBBox().y < stafflineCenter);
|
|
|
|
+ for (let i = 0; i < vftexts.length; i++) {
|
|
|
|
+ const _text = vftexts[i];
|
|
|
|
+ for (let j = 0; j < vftexts.length; j++) {
|
|
|
|
+ if (_text.parentNode === vftexts[j].parentNode) continue;
|
|
|
|
+ const result = collisionDetection(_text as SVGAElement, vftexts[j] as SVGAElement);
|
|
|
|
+ if (result.isCollision) {
|
|
|
|
+ if (_text.textContent === vftexts[j].textContent) {
|
|
|
|
+ vftexts[j].parentNode?.removeChild(vftexts[j]);
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ vftexts = Array.from(staffline.querySelectorAll(".vf-text > text")).filter((n: any) => n.getBBox().y < stafflineCenter);
|
|
|
|
+ let maxY = 0;
|
|
|
|
+ let _vftexts: SVGAElement[] = [];
|
|
|
|
+
|
|
|
|
+ vftexts.forEach((vftext: any) => {
|
|
|
|
+ const textBox = vftext.getBBox();
|
|
|
|
+ if (textBox.y < stafflineCenter) {
|
|
|
|
+ maxY = Math.max(maxY, textBox.y + textBox.height);
|
|
|
|
+ //console.log('音阶间距',textBox.y, textBox.height)
|
|
|
|
+ _vftexts.push(vftext as SVGAElement);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ if (maxY !== 0 && _vftexts.length > 1) {
|
|
|
|
+ _vftexts.forEach((vftext) => {
|
|
|
|
+ vftext.setAttribute("y", maxY + "");
|
|
|
|
+ //console.log('音阶间距',maxY)
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ vftexts.forEach((vftext) => {
|
|
|
|
+ [...vfcurve, ...vfmeasures, ...vflines].forEach((vfmeasure) => {
|
|
|
|
+ let result = collisionDetection(vftext as SVGAElement, vfmeasure);
|
|
|
|
+ if (result.isCollision && result.b1 < result.b2 && result.t1 < result.b2 - (result.b2 - result.t2) / 2) {
|
|
|
|
+ const shift_y = Number(vftext.getAttribute("y")) - (result.b1 - result.t2) - baseStep + "";
|
|
|
|
+ vftext.setAttribute("y", shift_y);
|
|
|
|
+ //console.log('音阶间距',shift_y)
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ vftexts.forEach((vftext) => {
|
|
|
|
+ vftexts.forEach((text) => {
|
|
|
|
+ if (vftext.parentNode !== text.parentNode && !["marcato", "legato"].includes(vftext.textContent as string)) {
|
|
|
|
+ if (["marcato", "legato"].includes(text.textContent as string)) {
|
|
|
|
+ const result = collisionDetection(vftext as SVGAElement, text as SVGAElement, 30, 30);
|
|
|
|
+ if (result.isCollision) {
|
|
|
|
+ const textBBox = (vftext as SVGAElement).getBBox();
|
|
|
|
+ text.setAttribute("x", textBBox.x + textBBox.width + 5 + "");
|
|
|
|
+ text.setAttribute("y", textBBox.y + textBBox.height - 5 + "");
|
|
|
|
+ //console.log('音阶间距',textBBox.y + textBBox.height - 5 + "")
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ const result = collisionDetection(vftext as SVGAElement, text as SVGAElement);
|
|
|
|
+ if (result.isCollision) {
|
|
|
|
+ const _y = Number(vftext.getAttribute("y"));
|
|
|
|
+ const shift_y = result.b2 - result.t2 < 24 ? 24 : result.b2 - result.t2;
|
|
|
|
+ text.setAttribute("y", _y - shift_y - 0.5 + "");
|
|
|
|
+ //console.log('音阶间距',_y - shift_y - 0.5 + "")
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ });
|
|
|
|
+ // 修改音阶和线谱的间距
|
|
|
|
+ const clefList = ['C', 'G', 'D', 'A', 'E', 'B', 'F#', 'C#', 'G#', 'F', 'Bb', 'Eb', 'Ab', 'Db', 'Gb', 'Cb', 'Fb', 'D#', 'A#', 'E#']
|
|
|
|
+ const btransList = ['Bb', 'Eb', 'Ab', 'Db', 'Gb', 'Cb', 'Fb']
|
|
|
|
+ const jtrsnsList = ['F#', 'C#', 'G#', 'D#', 'A#', 'E#', 'B#']
|
|
|
|
+ vftexts.forEach((label: any) => {
|
|
|
|
+ const labelText = label.textContent as string
|
|
|
|
+ if (clefList.includes(labelText)){
|
|
|
|
+ const _y = Number(label.getAttribute("y"))
|
|
|
|
+ const endY = firstLinePathY ? firstLinePathY - musicalDistance : _y
|
|
|
|
+ label.setAttribute("y", endY)
|
|
|
|
+ }
|
|
|
|
+ if (btransList.includes(labelText)) {
|
|
|
|
+ label.textContent = labelText.replace('b','♭')
|
|
|
|
+ }
|
|
|
|
+ if (jtrsnsList.includes(labelText)) {
|
|
|
|
+ label.textContent = labelText.replace('#','♯')
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ const vftextBottom = Array.from(staffline.querySelectorAll(".vf-text > text")).filter((n: any) => n.getBBox().y > stafflineCenter);
|
|
|
|
+ const vflineBottom = Array.from(staffline.querySelectorAll(".vf-line")).filter((n: any) => n.getBBox().y > stafflineCenter);
|
|
|
|
+ // 去重
|
|
|
|
+ for (let i = 0; i < vftextBottom.length; i++) {
|
|
|
|
+ const _text = vftextBottom[i];
|
|
|
|
+ for (let j = 0; j < vftextBottom.length; j++) {
|
|
|
|
+ if (_text.parentNode === vftextBottom[j].parentNode) continue;
|
|
|
|
+ const result = collisionDetection(_text as SVGAElement, vftextBottom[j] as SVGAElement);
|
|
|
|
+ if (result.isCollision) {
|
|
|
|
+ if (_text.textContent === vftextBottom[j].textContent) {
|
|
|
|
+ vftextBottom[j].parentNode?.removeChild(vftextBottom[j]);
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ // 1,2线谱底部文字重叠问题
|
|
|
|
+ vftextBottom.forEach((vftext) => {
|
|
|
|
+ [...vfmeasures].forEach((n) => {
|
|
|
|
+ let result = collisionDetection(vftext as SVGAElement, n);
|
|
|
|
+ if (result.isCollision) {
|
|
|
|
+ vftext.setAttribute("y", result.b2 + Math.abs(result.t1 - Number(vftext.getAttribute("y"))) + "");
|
|
|
|
+ //console.log('音阶间距', result.b2 + Math.abs(result.t1 - Number(vftext.getAttribute("y"))) + "")
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ });
|
|
|
|
+ // 如果渐弱渐强有平行的文字
|
|
|
|
+ vflineBottom.forEach((line) => {
|
|
|
|
+ const texts: any[] = [];
|
|
|
|
+ if (line.nextElementSibling?.classList.contains("vf-line")) {
|
|
|
|
+ vftextBottom.forEach((text) => {
|
|
|
|
+ let result = collisionDetection(line as SVGAElement, text as SVGAElement, 20, 20);
|
|
|
|
+ if (result.isCollision) {
|
|
|
|
+ texts.push({
|
|
|
|
+ text: text as SVGAElement,
|
|
|
|
+ result,
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ if (texts.length === 1) {
|
|
|
|
+ const result = texts[0].result;
|
|
|
|
+ const text = texts[0].text;
|
|
|
|
+ if (result.x2 + result.w2 < result.x1) {
|
|
|
|
+ // 左
|
|
|
|
+ if (Math.abs(result.y2 - result.y1) > 10) {
|
|
|
|
+ text.setAttribute("y", result.y1 + result.h2 / 2 + "");
|
|
|
|
+ //console.log('音阶间距', result.y1 + result.h2 / 2 + "")
|
|
|
|
+ }
|
|
|
|
+ } else if (result.x2 > result.x1 + result.w1) {
|
|
|
|
+ // 右
|
|
|
|
+ if (Math.abs(result.y2 - result.y1) > 10) {
|
|
|
|
+ text.setAttribute("y", result.y1 + result.h2 / 2 + "");
|
|
|
|
+ //console.log('音阶间距', result.y1 + result.h2 / 2 + "")
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ if (Math.abs(result.x2 - result.x1) < Math.abs(result.x2 + result.w2 - result.x1 - result.w1)) {
|
|
|
|
+ // console.log(text, '有交集', '靠左')
|
|
|
|
+ text.setAttribute("x", result.x1 - result.w2 - 5 + "");
|
|
|
|
+ if (Math.abs(result.y2 - result.y1) > 10) {
|
|
|
|
+ text.setAttribute("y", result.y1 + result.h2 / 2 + "");
|
|
|
|
+ //console.log('音阶间距', result.y1 + result.h2 / 2 + "")
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ // console.log(text, '有交集', '靠右')
|
|
|
|
+ text.setAttribute("x", result.x1 + result.w1 + 5 + "");
|
|
|
|
+ if (Math.abs(result.y2 - result.y1) > 10) {
|
|
|
|
+ text.setAttribute("y", result.y1 + result.h2 / 2 + "");
|
|
|
|
+ //console.log('音阶间距', result.y1 + result.h2 / 2 + "")
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } else if (texts.length === 2) {
|
|
|
|
+ const result1 = texts[0].result;
|
|
|
|
+ const text1 = texts[0].text;
|
|
|
|
+ const result2 = texts[1].result;
|
|
|
|
+ const text2 = texts[1].text;
|
|
|
|
+ text1.setAttribute("x", result1.x1 - result1.w2 - 5 + "");
|
|
|
|
+ if (Math.abs(result1.y2 - result1.y1) > 10) {
|
|
|
|
+ text1.setAttribute("y", result1.y1 + result1.h2 / 2 + "");
|
|
|
|
+ //console.log('音阶间距', result1.y1 + result1.h2 / 2 + "")
|
|
|
|
+ }
|
|
|
|
+ text2.setAttribute("x", result2.x1 + result2.w1 + 5 + "");
|
|
|
|
+ if (Math.abs(result2.y2 - result2.y1) > 10) {
|
|
|
|
+ text2.setAttribute("y", result2.y1 + result2.h2 / 2 + "");
|
|
|
|
+ //console.log('音阶间距', result2.y1 + result2.h2 / 2 + "")
|
|
|
|
+ }
|
|
|
|
+ } else if (texts.length === 3) {
|
|
|
|
+ // console.log(texts)
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ vftextBottom.forEach((vftext) => {
|
|
|
|
+ vftextBottom.forEach((text) => {
|
|
|
|
+ if (vftext.parentNode !== text.parentNode && !["marcato", "legato", "cresc.", "Cantabile"].includes(vftext.textContent as string)) {
|
|
|
|
+ if (["marcato", "legato", "cresc.", "Cantabile"].includes(text.textContent as string)) {
|
|
|
|
+ const result = collisionDetection(vftext as SVGAElement, text as SVGAElement, 30, 30);
|
|
|
|
+ if (result.isCollision) {
|
|
|
|
+ const textBBox = (vftext as SVGAElement).getBBox();
|
|
|
|
+ text.setAttribute("x", textBBox.x + textBBox.width + 5 + "");
|
|
|
|
+ text.setAttribute("y", textBBox.y + textBBox.height - 5 + "");
|
|
|
|
+ //console.log('音阶间距', textBBox.y + textBBox.height - 5 + "")
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ const result = collisionDetection(vftext as SVGAElement, text as SVGAElement);
|
|
|
|
+ if (result.isCollision) {
|
|
|
|
+ text.setAttribute("y", result.y1 + result.h1 + result.h2 + "");
|
|
|
|
+ //console.log('音阶间距', result.y1 + result.h1 + result.h2 + "")
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // setTimeout(() => this.resetGlobalText());
|
|
|
|
+};
|
|
|
|
+// 技巧文本
|
|
|
|
+const resetGlobalText = () => {
|
|
|
|
+ const svg = container.value.querySelector("svg");
|
|
|
|
+ if (!svg) return;
|
|
|
|
+ const svgBBox = svg.getBBox();
|
|
|
|
+ let vfstavetempo: SVGAElement[] = Array.from(container.value.querySelectorAll(".vf-stavetempo")).reduce((eles: SVGAElement[], value: any) => {
|
|
|
|
+ if (eles.find((n) => n.outerHTML === value.outerHTML)) value?.parentNode?.removeChild(value);
|
|
|
|
+ else eles.push(value);
|
|
|
|
+ return eles;
|
|
|
|
+ }, []);
|
|
|
|
+ const staffline: SVGAElement[] = Array.from(container.value.querySelectorAll(".staffline"));
|
|
|
|
+ const vfmeasures: SVGAElement[] = Array.from(container.value.querySelectorAll(".staffline > .vf-measure"));
|
|
|
|
+ const vftexts: SVGAElement[] = Array.from(container.value.querySelectorAll(".staffline > .vf-text"));
|
|
|
|
+ const vfcurves: SVGAElement[] = Array.from(container.value.querySelectorAll(".staffline > .vf-curve"));
|
|
|
|
+
|
|
|
|
+ vfstavetempo.forEach((child: SVGAElement) => {
|
|
|
|
+ let _y = 0;
|
|
|
|
+ [...vfmeasures, ...vftexts, ...vfcurves].forEach((ele) => {
|
|
|
|
+ const result = collisionDetection(child as SVGAElement, ele);
|
|
|
|
+ if (result.isCollision && (result.b1 < result.b2 || result.r1 > result.l2 || result.l1 < result.r2)) {
|
|
|
|
+ _y = Math.min(_y, result.t2 - result.b1);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ if (_y !== 0) {
|
|
|
|
+ child.style.transform = `translateY(${_y}px)`;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const childBBox = child.getBBox();
|
|
|
|
+ const rightY = (childBBox.x + childBBox.width) * 0.7 - Number(svg.getAttribute("width"));
|
|
|
|
+ if (rightY > 0) {
|
|
|
|
+ [...staffline, ...vfstavetempo].forEach((tempo) => {
|
|
|
|
+ if (child != tempo) {
|
|
|
|
+ const result = collisionDetection(child as SVGAElement, tempo, Math.abs(rightY), Math.abs(_y));
|
|
|
|
+ if (result.isCollision) {
|
|
|
|
+ _y = result.t2 - result.b1;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ child.style.transform = `translate(-${rightY / 0.7}px,${_y}px)`;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ if (svgBBox.y < 0) {
|
|
|
|
+ svg.setAttribute("height", Number(svg.getAttribute("height")) - svgBBox.y + 10);
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+// 碰撞检测
|
|
|
|
+const collisionDetection = (a: SVGAElement, b: SVGAElement, distance: number = 0, distance_y: number = 0) => {
|
|
|
|
+ const abbox = a.getBBox();
|
|
|
|
+ const bbbox = b.getBBox();
|
|
|
|
+ let t1 = abbox.y - distance_y;
|
|
|
|
+ let l1 = abbox.x - distance;
|
|
|
|
+ let r1 = abbox.x + abbox.width + distance;
|
|
|
|
+ let b1 = abbox.y + abbox.height + distance_y;
|
|
|
|
+
|
|
|
|
+ let t2 = bbbox.y;
|
|
|
|
+ let l2 = bbbox.x;
|
|
|
|
+ let r2 = bbbox.x + bbbox.width;
|
|
|
|
+ let b2 = bbbox.y + bbbox.height;
|
|
|
|
+ if (b1 < t2 || l1 > r2 || t1 > b2 || r1 < l2) {
|
|
|
|
+ // 表示没碰上
|
|
|
|
+ return {
|
|
|
|
+ isCollision: false,
|
|
|
|
+ t1,
|
|
|
|
+ l1,
|
|
|
|
+ r1,
|
|
|
|
+ b1,
|
|
|
|
+ t2,
|
|
|
|
+ l2,
|
|
|
|
+ r2,
|
|
|
|
+ b2,
|
|
|
|
+ x1: abbox.x,
|
|
|
|
+ y1: abbox.y,
|
|
|
|
+ x2: bbbox.x,
|
|
|
|
+ y2: bbbox.y,
|
|
|
|
+ h1: abbox.height,
|
|
|
|
+ h2: bbbox.height,
|
|
|
|
+ w1: abbox.width,
|
|
|
|
+ w2: bbbox.width,
|
|
|
|
+ };
|
|
|
|
+ } else {
|
|
|
|
+ return {
|
|
|
|
+ isCollision: true,
|
|
|
|
+ t1,
|
|
|
|
+ l1,
|
|
|
|
+ r1,
|
|
|
|
+ b1,
|
|
|
|
+ t2,
|
|
|
|
+ l2,
|
|
|
|
+ r2,
|
|
|
|
+ b2,
|
|
|
|
+ x1: abbox.x,
|
|
|
|
+ y1: abbox.y,
|
|
|
|
+ x2: bbbox.x,
|
|
|
|
+ y2: bbbox.y,
|
|
|
|
+ h1: abbox.height,
|
|
|
|
+ h2: bbbox.height,
|
|
|
|
+ w1: abbox.width,
|
|
|
|
+ w2: bbbox.width,
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/** 全局曲谱配置 */
|
|
|
|
+export const setGlobalMusicSheet = () => {
|
|
|
|
+ const partIndex = query["part-index"] || '0'
|
|
|
|
+ /** 延音线方向问题 start */
|
|
|
|
+ const stavetieList = [
|
|
|
|
+ {id: '12644', part_index: '25', direction: 1}
|
|
|
|
+ ]
|
|
|
|
+ const tieItem = stavetieList.find(({id, part_index}) => {
|
|
|
|
+ return id == state.cbsExamSongId && part_index == partIndex
|
|
|
|
+ })
|
|
|
|
+ setGlobalData('tieDirection', tieItem ? tieItem.direction : undefined)
|
|
|
|
+ /** 延音线方向问题 end */
|
|
|
|
+
|
|
|
|
+ const graceList = [
|
|
|
|
+ {id: '3509', part_index: '16', direction: 1}
|
|
|
|
+ ]
|
|
|
|
+ const graceItem = graceList.find(({id, part_index}) => {
|
|
|
|
+ return id == state.cbsExamSongId && part_index == partIndex
|
|
|
|
+ })
|
|
|
|
+ if (graceItem){
|
|
|
|
+ setGlobalData('graceCustom', {direction: graceItem.direction})
|
|
|
|
+ }
|
|
|
|
+ const bassDrumList = [
|
|
|
|
+ {id: '3030', part_index: '17', line: 4},
|
|
|
|
+ {id: '12704', part_index: '23', line: 3}
|
|
|
|
+ ]
|
|
|
|
+ const bassDrumItem = bassDrumList.find(({id, part_index}) => {
|
|
|
|
+ return id == state.cbsExamSongId && part_index == partIndex
|
|
|
|
+ })
|
|
|
|
+ if (bassDrumItem){
|
|
|
|
+ setGlobalData('customBassDrum', bassDrumItem.line)
|
|
|
|
+ }
|
|
|
|
+ /** 打击乐多声部,双声部休止符重叠 end */
|
|
|
|
+
|
|
|
|
+ /** 符杆朝向 */
|
|
|
|
+ const stemDirectionList = [
|
|
|
|
+ {
|
|
|
|
+ id: '11654',
|
|
|
|
+ part_index: '16',
|
|
|
|
+ stemNotes: [
|
|
|
|
+ {id: 124, direction: 0},
|
|
|
|
+ {id: 125, direction: 0},
|
|
|
|
+ {id: 126, direction: 0},
|
|
|
|
+ {id: 127, direction: 0},
|
|
|
|
+ {id: 128, direction: 0}
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ id: '3581',
|
|
|
|
+ part_index: '4',
|
|
|
|
+ stemNotes: [
|
|
|
|
+ {id: 380, direction: 1},
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ id: '3470',
|
|
|
|
+ part_index: '0',
|
|
|
|
+ stemNotes: [
|
|
|
|
+ {id: 36, direction: 1},
|
|
|
|
+ {id: 37, direction: 1},
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ id: '3470',
|
|
|
|
+ part_index: '11',
|
|
|
|
+ stemNotes: [
|
|
|
|
+ {id: 33, direction: 1},
|
|
|
|
+ {id: 56, direction: 1},
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ id: '12644',
|
|
|
|
+ part_index: '22',
|
|
|
|
+ stemNotes: [
|
|
|
|
+ {id: 22, direction: 1},
|
|
|
|
+ {id: 26, direction: 1},
|
|
|
|
+ {id: 135, direction: 1},
|
|
|
|
+ {id: 163, direction: 1},
|
|
|
|
+ {id: 199, direction: 1},
|
|
|
|
+ {id: 204, direction: 1},
|
|
|
|
+ {id: 206, direction: 1},
|
|
|
|
+ {id: 208, direction: 1},
|
|
|
|
+ {id: 210, direction: 1},
|
|
|
|
+ {id: 213, direction: 1},
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ id: '12303',
|
|
|
|
+ part_index: '18',
|
|
|
|
+ stemNotes: [
|
|
|
|
+ {id: 1, direction: 1},
|
|
|
|
+ {id: 4, direction: 1},
|
|
|
|
+ {id: 6, direction: 1},
|
|
|
|
+ {id: 9, direction: 1},
|
|
|
|
+ {id: 12, direction: 1},
|
|
|
|
+ {id: 14, direction: 1},
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ id: '12669',
|
|
|
|
+ part_index: '24',
|
|
|
|
+ stemNotes: [
|
|
|
|
+ {id: 65, direction: 1},
|
|
|
|
+ {id: 296, direction: 1},
|
|
|
|
+ {id: 298, direction: 1},
|
|
|
|
+ {id: 300, direction: 1},
|
|
|
|
+ {id: 338, direction: 1},
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ id: '12420',
|
|
|
|
+ part_index: '21',
|
|
|
|
+ stemNotes: [
|
|
|
|
+ {id: 614, direction: 0},
|
|
|
|
+ {id: 617, direction: 0},
|
|
|
|
+ {id: 619, direction: 0},
|
|
|
|
+ {id: 621, direction: 0},
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ id: '12711',
|
|
|
|
+ part_index: '22',
|
|
|
|
+ stemNotes: []
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ id: '12973',
|
|
|
|
+ part_index: '21',
|
|
|
|
+ stemNotes: [
|
|
|
|
+ {id: 619, direction: 1},
|
|
|
|
+ {id: 622, direction: 1},
|
|
|
|
+ {id: 745, direction: 1},
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+ ]
|
|
|
|
+ const stemDirectionItem = stemDirectionList.find(({id, part_index}) => {
|
|
|
|
+ return id == state.cbsExamSongId && part_index == partIndex
|
|
|
|
+ })
|
|
|
|
+ if (stemDirectionItem) {
|
|
|
|
+ setGlobalData('stemDirectionNote', stemDirectionItem.stemNotes)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /** vfcure */
|
|
|
|
+ const vfcurveList = [
|
|
|
|
+ {
|
|
|
|
+ id: '12711',
|
|
|
|
+ part_index: '4',
|
|
|
|
+ vfcurve: [
|
|
|
|
+ {MeasureNumberXML: 25, index: 1, bezierEndControlPt: {y: -2}},
|
|
|
|
+ {MeasureNumberXML: 33, index: 1, bezierEndControlPt: {y: -2}},
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ id: '12059',
|
|
|
|
+ part_index: '0',
|
|
|
|
+ vfcurve: [
|
|
|
|
+ {MeasureNumberXML: 15, bezierEndControlPt: {y: 2.8}, bezierEndPt:{y: 1.1}},
|
|
|
|
+ {MeasureNumberXML: 16, bezierEndControlPt: {y: -1}},
|
|
|
|
+ {MeasureNumberXML: 19, index: 1, bezierEndControlPt: {y: 2}},
|
|
|
|
+ {MeasureNumberXML: 20, bezierEndControlPt: {y: -1}},
|
|
|
|
+ {MeasureNumberXML: 42, index: 1, bezierEndControlPt: {y: -1.5}, bezierStartControlPt: {y: -1.5}},
|
|
|
|
+ {MeasureNumberXML: 46, index: 3, bezierEndControlPt: {y: -1.5}, bezierStartControlPt: {y: -1.5}},
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ id: '12668',
|
|
|
|
+ part_index: '11',
|
|
|
|
+ vfcurve: [
|
|
|
|
+ {MeasureNumberXML: 8, index: 2, bezierEndControlPt: {y: -3}, bezierStartControlPt:{y: -3}, bezierEndPt:{y: -1}},
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ id: '11976',
|
|
|
|
+ part_index: '0',
|
|
|
|
+ vfcurve: [
|
|
|
|
+ {MeasureNumberXML: 14, index: 4, bezierEndControlPt: {y: -3}},
|
|
|
|
+ {MeasureNumberXML: 14, index: 1, bezierEndPt: {y: 1.5}, bezierEndControlPt: {y: 1}},
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+ ]
|
|
|
|
+ const vfcurveItem = vfcurveList.find(({id, part_index}) => {
|
|
|
|
+ return id == state.cbsExamSongId && part_index == partIndex
|
|
|
|
+ })
|
|
|
|
+ if (vfcurveItem) {
|
|
|
|
+ setGlobalData('vfcurveItem', vfcurveItem.vfcurve)
|
|
|
|
+ }
|
|
|
|
+ /** drum set声部 重音 */
|
|
|
|
+ const customArtPositionList = [
|
|
|
|
+ {id: '12644', part_index: '25'}
|
|
|
|
+ ]
|
|
|
|
+ const customArtPositionItem = customArtPositionList.find(({id, part_index}) => {
|
|
|
|
+ return id == state.cbsExamSongId && part_index == partIndex
|
|
|
|
+ })
|
|
|
|
+ if (customArtPositionItem) {
|
|
|
|
+ setGlobalData('customArtPosition', true)
|
|
|
|
+ }
|
|
|
|
+ /** 全声部声部 - & 全音符 */
|
|
|
|
+ const customTenutoList = [
|
|
|
|
+ {id: '12645', part_index: '5'}
|
|
|
|
+ ]
|
|
|
|
+ const customTenutoItem = customTenutoList.find(({id, part_index}) => {
|
|
|
|
+ return id == state.cbsExamSongId && part_index == partIndex
|
|
|
|
+ })
|
|
|
|
+ if (customTenutoItem) {
|
|
|
|
+ setGlobalData('customTenutoItem', true)
|
|
|
|
+ }
|
|
|
|
+ /** 全声部声部 > */
|
|
|
|
+ const customAccentList = [
|
|
|
|
+ {id: '12711', part_index: '22'},
|
|
|
|
+ {id: '12711', part_index: '25'},
|
|
|
|
+ ]
|
|
|
|
+ const customAccentItem = customAccentList.find(({id, part_index}) => {
|
|
|
|
+ return id == state.cbsExamSongId && part_index == partIndex
|
|
|
|
+ })
|
|
|
|
+ if (customAccentItem) {
|
|
|
|
+ setGlobalData('customAccentItem', true)
|
|
|
|
+ }
|
|
|
|
+ /** 全声部声部 + */
|
|
|
|
+ const customLefthandpizzicatoList = [
|
|
|
|
+ {id: '12711', part_index: '25'},
|
|
|
|
+ {id: '7755', part_index: '10'},
|
|
|
|
+ {id: '6226', part_index: '16'},
|
|
|
|
+ ]
|
|
|
|
+ const customLefthandpizzicatoItem = customLefthandpizzicatoList.find(({id, part_index}) => {
|
|
|
|
+ return id == state.cbsExamSongId && part_index == partIndex
|
|
|
|
+ })
|
|
|
|
+ if (customLefthandpizzicatoItem) {
|
|
|
|
+ setGlobalData('customLefthandpizzicatoItem', true)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/** 设置自定义渐慢 */
|
|
|
|
+export const setCustomGradual = () => {
|
|
|
|
+ if (state.gradualTimes) {
|
|
|
|
+ const detailId = state.cbsExamSongId + "";
|
|
|
|
+ const partIndex = state.partIndex + "";
|
|
|
|
+ if (["12280"].includes(detailId) && ["24"].includes(partIndex)) {
|
|
|
|
+ state.gradualTimes["8"] = "00:26:10";
|
|
|
|
+ state.gradualTimes["66"] = "01:53:35";
|
|
|
|
+ state.gradualTimes["90"] = "02:41:40";
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/** 设置自定义音符数据 */
|
|
|
|
+export const setCustomNoteRealValue = () => {
|
|
|
|
+ const detailId = state.cbsExamSongId + "";
|
|
|
|
+ const partIndex = state.partIndex + "";
|
|
|
|
+ if (["2670"].includes(detailId)) {
|
|
|
|
+ customData.customNoteRealValue = {
|
|
|
|
+ 0: 0.03125,
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+ if (["12673"].includes(detailId) && ['22'].includes(partIndex)) {
|
|
|
|
+ customData.customNoteRealValue = {
|
|
|
|
+ 208: 0.125,
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (["12667", "12673"].includes(detailId)){
|
|
|
|
+ customData.customNoteCurrentTime = true
|
|
|
|
+ }
|
|
|
|
+};
|