Преглед изворни кода

Merge branch 'feature-tianyong' into gym-dev

TIANYONG пре 6 месеци
родитељ
комит
dd7553b1bf

+ 1 - 1
osmd-extended

@@ -1 +1 @@
-Subproject commit a9750e2d69cdef62c365035e86f131586a25aa69
+Subproject commit 4ebb0ca539cdf162b77078d69830820764752a3d

+ 2 - 0
src/helpers/calcSpeed.ts

@@ -117,7 +117,9 @@ export type GradualItem = {
  */
 export const getGradualLengthByXml = (xml: string) => {
 	const firstPartXml = onlyVisible(xml, 0)
+	console.time('DOMParser 耗时2')
 	const xmlParse = new DOMParser().parseFromString(firstPartXml, "text/xml");
+	console.timeEnd('DOMParser 耗时2')
 	const measures = Array.from(xmlParse.querySelectorAll("measure"));
 	const notes = Array.from(xmlParse.querySelectorAll("note"));
 	const words = Array.from(xmlParse.querySelectorAll("words"));

+ 19 - 5
src/helpers/formateMusic.ts

@@ -15,6 +15,7 @@ import {
 } from "/osmd-extended/src";
 import { GradualChange, speedInfo } from "./calcSpeed";
 import { beatUnitTo, speedBeatTo } from "/src/helpers/beatConfig"
+import { xmlDocRef } from "/src/view/music-score"
 
 const browserInfo = browser();
 dayjs.extend(duration);
@@ -251,8 +252,11 @@ export const getCustomInfo = (xml: string): CustomInfo => {
 		showSpeed: true,
 		parsedXML: xml,
 	};
-	const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
-	const words: any = xmlParse.getElementsByTagName("words");
+	console.time('DOMParser 耗时3')
+	// const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
+	const xmlParse = xmlDocRef.value ? xmlDocRef.value : new DOMParser().parseFromString(xml, "text/xml");
+	console.timeEnd('DOMParser 耗时3')
+	const words: any = xmlParse?.getElementsByTagName("words");
 	for (const word of words) {
 		if (word && word.textContent?.trim() === "隐藏速度") {
 			data.showSpeed = false;
@@ -361,7 +365,10 @@ export const onlyVisible = (xml: string, partIndex: number): string => {
 	if (!xml) return "";
 	// console.log('原始xml')
 	const detailId = state.examSongId + "";
-	const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
+	console.time('DOMParser 耗时4')
+	// const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
+	const xmlParse = xmlDocRef.value ? xmlDocRef.value : new DOMParser().parseFromString(xml, "text/xml");
+	console.timeEnd('DOMParser 耗时4')
 	const partList = xmlParse.getElementsByTagName("part-list")?.[0]?.getElementsByTagName("score-part") || [];
 	const partListNames = Array.from(partList).map((item) => item.getElementsByTagName("part-name")?.[0]?.textContent?.trim() || "");
 	const parts: any = xmlParse.getElementsByTagName("part");
@@ -521,7 +528,9 @@ export const onlyVisible2 = (xml: string): string => {
 	if (!xml) return "";
 	// console.log('原始xml')
 	//const detailId = state.examSongId + "";
+	console.time('DOMParser 耗时5')
 	const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
+	console.timeEnd('DOMParser 耗时5')
 	const partList = xmlParse.getElementsByTagName("part-list")?.[0]?.getElementsByTagName("score-part") || [];
 	//const partListNames = Array.from(partList).map((item) => item.getElementsByTagName("part-name")?.[0]?.textContent?.trim() || "");
 	//state.partListNames = partListNames;
@@ -614,7 +623,9 @@ export const formatZoom = (num = 1) => {
 /** 妙极客多分轨的曲子,可能没有part-name标签,需要手动加上该标签 */
 export const xmlAddPartName = (xml: string) => {
 	if (!xml) return "";
+	console.time('DOMParser 耗时6')
 	const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
+	console.timeEnd('DOMParser 耗时6')
 	const scoreParts = Array.from(xmlParse.getElementsByTagName("score-part"));
 	for (const scorePart of scoreParts) {
 		if (scorePart.getElementsByTagName("part-name").length === 0) {
@@ -628,6 +639,7 @@ export const xmlAddPartName = (xml: string) => {
 			scorePart.getElementsByTagName("part-name")[0].textContent = scorePart.getAttribute("id") || "";
 		}
 	}
+	xmlDocRef.value = xmlParse;
 	return new XMLSerializer().serializeToString(xmlParse);
 }
 
@@ -636,8 +648,10 @@ export const xmlAddPartName = (xml: string) => {
  */
 export const formatXML = (xml: string, xmlUrl?: string): string => {
 	if (!xml) return "";
-	const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
-
+	console.time('DOMParser 耗时7')
+	// const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
+	const xmlParse = xmlDocRef.value ? xmlDocRef.value : new DOMParser().parseFromString(xml, "text/xml");
+	console.timeEnd('DOMParser 耗时7')
 	// 声调
 	const fifths = xmlParse.getElementsByTagName("fifths");
 	if (fifths && fifths.length) {

+ 4 - 2
src/helpers/metronome.ts

@@ -208,7 +208,8 @@ class Metronome {
 			return;
 		}
 		metronomeData.isClick = false;
-		if (currentTime === 0) {
+		// 非选段状态,没播放时,不显示节拍指针
+		if (currentTime === 0 && !state.sectionStatus) {
 			metronomeData.activeMetro = {}
 		}
 	};
@@ -460,7 +461,8 @@ class Metronome {
 						left: left?.indexOf("%") > -1 ? `calc(${left})` : left,
 						measureNumberXML: measure.measureNumberXML,
 						isRestFlag: measure.isRestFlag,
-						stepList: measure.stepList
+						stepList: measure.stepList,
+						isPercent: left?.indexOf("%") > -1, // 是否是根据小节宽度等分
 					});
 				}
 			}

+ 52 - 0
src/page-instrument/App.tsx

@@ -8,6 +8,7 @@ import { studentQueryUserInfo } from "./api";
 import { api_cloudLoading, api_getToken } from "../helpers/communication";
 import { showToast } from "vant";
 import state from "/src/state"
+import { values } from "lodash";
 
 export default defineComponent({
   name: "App",
@@ -125,6 +126,57 @@ export default defineComponent({
       //     event.preventDefault();
       //   }
       // });
+
+      /**
+       * DNS 解析耗时: domainLookupEnd - domainLookupStart
+       * TCP 连接耗时: connectEnd - connectStart
+       * SSL 安全连接耗时: connectEnd - secureConnectionStart
+       * 网络请求耗时 (TTFB): responseStart - requestStart
+       *数据传输耗时: responseEnd - responseStart
+        *DOM 解析耗时: domInteractive - responseEnd
+        *资源加载耗时: loadEventStart - domContentLoadedEventEnd
+        *First Byte时间: responseStart - domainLookupStart
+        *白屏时间: responseEnd - fetchStart
+        *首次可交互时间: domInteractive - fetchStart
+        *DOM Ready 时间: domContentLoadEventEnd - fetchStart
+        *页面完全加载时间: loadEventStart - fetchStart
+        *http 头部大小: transferSize - encodedBodySize
+        *重定向次数:performance.navigation.redirectCount
+        *重定向耗时: redirectEnd - redirectStart
+       */
+      window.onload = function() {
+        console.log('加载完成')
+        let timing: any = performance.getEntriesByType('navigation')[0];
+        const { domainLookupEnd, domainLookupStart, connectEnd, connectStart, responseStart,
+          requestStart, responseEnd, domInteractive, loadEventStart, domContentLoadedEventEnd,
+          fetchStart, secureConnectionStart, transferSize, encodedBodySize,
+          redirectEnd, redirectStart
+        } = timing
+        //console.log(timing.domInteractive);
+        //console.log(timing.fetchStart);
+        let diff = timing.domInteractive - timing.fetchStart;
+        //console.log("TTI: " + diff);
+        const timeInfo = [
+          { '类型': 'TTI', '耗时': diff},
+          { '类型': 'DNS 解析耗时', '耗时': domainLookupEnd - domainLookupStart},
+          { '类型': 'TCP 连接耗时', '耗时': connectEnd - connectStart},
+          { '类型': 'SSL 安全连接耗时', '耗时': connectEnd - secureConnectionStart},
+          { '类型': '网络请求耗时', '耗时': responseStart - requestStart},
+          { '类型': '数据传输耗时', '耗时': responseEnd - responseStart},
+          { '类型': 'DOM 解析耗时', '耗时': domInteractive - responseEnd},
+          { '类型': '资源加载耗时', '耗时': loadEventStart - domContentLoadedEventEnd},
+          { '类型': 'First Byte时间', '耗时': responseStart - domainLookupStart},
+          { '类型': '白屏时间', '耗时': responseEnd - fetchStart},
+          { '类型': '首次可交互时间', '耗时': domInteractive - fetchStart},
+          { '类型': 'DOM Ready 时间', '耗时': domContentLoadedEventEnd - fetchStart},
+          { '类型': '页面完全加载时间', '耗时': loadEventStart - fetchStart},
+          { '类型': 'http 头部大小', '耗时': transferSize - encodedBodySize},
+          { '类型': '重定向次数', '耗时': performance.navigation.redirectCount},
+          { '类型': '重定向耗时', '耗时': redirectEnd - redirectStart},
+        ];
+        console.table(timeInfo);
+        
+      };
     });
 
     onUnmounted(() => {

+ 9 - 3
src/state.ts

@@ -17,7 +17,7 @@ import { followData, skipNotePractice } from "/src/view/follow-practice/index"
 import { changeSongSourceByBeat } from "/src/view/audio-list"
 import { moveSmoothAnimation, smoothAnimationState, moveSmoothAnimationByPlayTime, moveTranslateXNum, destroySmoothAnimation, calcClientWidth } from "/src/page-instrument/view-detail/smoothAnimation"
 import { storeData } from "/src/store";
-import { downloadXmlStr } from "./view/music-score"
+import { downloadXmlStr, xmlDocRef } from "./view/music-score"
 import { musicScoreRef, headerColumnHide } from "/src/page-instrument/view-detail/index"
 import { headTopData } from "/src/page-instrument/header-top/index";
 import { api_lessonTrainingTrainingStudentDetail } from "/src/page-instrument/api"
@@ -1359,6 +1359,7 @@ export const setSection = (start: number, end: number, userSpeed?: number) => {
 export const hanldeDirectSelection = (list: any[]) => {
   if (!Array.isArray(list) || list.length !== 2) return;
   state.sectionStatus = true;
+  metronomeData.activeIndex = null;
   setTimeout(() => {
     state.section = formateSelectMearure(list);
     // 选段完成后,需要根据预报小节的速度,设置右下角显示的速度
@@ -1530,8 +1531,13 @@ const getMusicInfo = async (res: any) => {
 };
 //获取xml中的音轨数据
 function xmlToTracks(xmlString: string) {
-  const xmlParse = new DOMParser().parseFromString(xmlString, "text/xml");
-  const partNames = Array.from(xmlParse.getElementsByTagName('part-name'));
+  //console.time('domparse')
+  console.time('DOMParser 耗时1')
+  // const xmlParse = new DOMParser().parseFromString(xmlString, "text/xml");
+  const xmlParse = xmlDocRef.value;
+  console.timeEnd('DOMParser 耗时1')
+  //console.timeEnd('domparse')
+  const partNames = xmlParse ? Array.from(xmlParse.getElementsByTagName('part-name')) : [];
   return partNames.reduce((arr: string[], item) => {
     const textContent = item?.textContent?.trim()
     if (textContent?.toLocaleLowerCase() === "common") {

+ 3 - 0
src/view/music-score/index.tsx

@@ -35,6 +35,9 @@ export const resetRenderMusicScore = (type?: string) => {
 // 下载后的xml
 export const downloadXmlStr = ref("")
 
+// xml的document对象
+export const xmlDocRef = ref<Document | null>(null);
+
 export default defineComponent({
 	name: "music-score",
 	emits: ["rendered"],

+ 35 - 5
src/view/selection/index.tsx

@@ -28,8 +28,11 @@ export default defineComponent({
 			notes: [] as any[],
 			staves: [] as any[],
 			measureHeight: 0 as number, // 小节高度
+			// beatWidth: '100%' as string, // 节拍指针占用的宽度,带有拍号的小节,节拍指针占用的宽度需要减去拍号左侧的宽度
+			// beatLeft: 0 as number, // 小节有拍号时,拍号左侧宽度
 		});
-		
+		const beatMeasureWidths: any = {}; // 节拍指针需要占用的宽度
+
 		/** 计算点击层数据 */
 		const calcNoteData = () => {
 			const musicContainer = document.getElementById("musicAndSelection")?.getBoundingClientRect() || {
@@ -112,7 +115,9 @@ export default defineComponent({
 						notesList.push(item.noteId);
 					}
 				}
-		
+				// 如果小节里面有拍号,需要减去拍号左侧的宽度
+				let beatWidth = '100%';
+				let beatLeft = 0;
 				if (!MeasureNumberXMLList.includes(item.MeasureNumberXML)) {
 					if (item.stave) {
 						if (item.stave?.attrs?.id) {
@@ -132,6 +137,14 @@ export default defineComponent({
 							} catch (error) {}
 		
 							const staveBbox = staveEle?.getBoundingClientRect?.() || { x: 0, width: 0, y: 0, height: 0 };
+							const timesignatureDom = staveEle?.querySelector('.vf-timesignature') || staveEle?.querySelector('.vf-keysignature')
+							// 休止符才是根据小节宽度等分
+							if (timesignatureDom && item.measures.length == 1) {
+								const timesignatureBbox = timesignatureDom.getBoundingClientRect()
+								const leftWidth = timesignatureBbox.x + timesignatureBbox.width - staveBbox.x
+								beatLeft = leftWidth
+								beatWidth = `calc(100% - ${leftWidth+'px'})`
+							}
 							if (i === 0) {
 								minMeasureHeigt = staveBbox.height
 							}
@@ -159,6 +172,10 @@ export default defineComponent({
 							selectData.staves.push(noteItem);
 						}
 						MeasureNumberXMLList.push(item.MeasureNumberXML);
+						beatMeasureWidths[item.MeasureNumberXML] = {
+							beatLeft,
+							beatWidth
+						}
 					} else {
 						if (item.multipleRestMeasures) {
 							if (state.isCombineRender) {
@@ -184,6 +201,10 @@ export default defineComponent({
 									};
 									selectData.staves.push(noteItem);
 									MeasureNumberXMLList.push(item.MeasureNumberXML);
+									beatMeasureWidths[item.MeasureNumberXML] = {
+										beatLeft,
+										beatWidth
+									}
 								}
 							} else {
 								const preItem = selectData.staves.find(
@@ -198,6 +219,15 @@ export default defineComponent({
 									};
 									selectData.staves.push(noteItem);
 									MeasureNumberXMLList.push(item.MeasureNumberXML);
+									const preBeatMeasure = beatMeasureWidths[item.MeasureNumberXML-1]
+									beatMeasureWidths[item.MeasureNumberXML] = preBeatMeasure ?
+									{
+										beatLeft: preBeatMeasure.beatLeft,
+										beatWidth: preBeatMeasure.beatWidth
+									} : {
+										beatLeft,
+										beatWidth
+									}
 								}
 							}
 		
@@ -209,7 +239,7 @@ export default defineComponent({
 			if (selectData.staves[0]?.staveBox?.top !== selectData.staves[1]?.staveBox?.top) {
 				selectData.staves[0].staveBox.top = selectData.staves[1]?.staveBox?.top || selectData.staves[0]?.staveBox?.top
 			}
-			console.log("🚀 ~ selectData.notes:", selectData.notes, selectData.staves);
+			console.log("🚀 ~ selectData.notes:", selectData.notes, selectData.staves,beatMeasureWidths,MeasureNumberXMLList);
 		};
 		/** 是否可以点击音符 */
 		const disableClickNote = computed(() => {
@@ -304,7 +334,7 @@ export default defineComponent({
 							metronomeData.cursorMode === 2 &&
 							item.MeasureNumberXML === metronomeData.activeMetro?.measureNumberXML &&
 							state.times[state.activeNoteIndex].MeasureNumberXML === item.MeasureNumberXML;
-							//console.log('显示节拍指针',lineShow,state.times[state.activeNoteIndex].MeasureNumberXML,item.MeasureNumberXML,metronomeData.activeMetro?.measureNumberXML)
+							// console.log('显示节拍指针',lineShow,state.times[state.activeNoteIndex].MeasureNumberXML,item.MeasureNumberXML,metronomeData.activeMetro?.measureNumberXML)
 						return (
 							<>
 								{item.staveBox && (
@@ -327,7 +357,7 @@ export default defineComponent({
 										}}
 									>
 										{lineShow && (
-											<div style={{height: selectData.measureHeight + 'px', position: 'relative'}}>
+											<div style={{height: selectData.measureHeight + 'px', position: 'relative', width: metronomeData.activeMetro.isPercent ? beatMeasureWidths[item.MeasureNumberXML].beatWidth : '100%', left: metronomeData.activeMetro.isPercent ? beatMeasureWidths[item.MeasureNumberXML].beatLeft + 'px' : 0}}>
 												<div 
 												class={[
 													styles.line,