import { Icon, Toast } from 'vant' import { defineComponent, watchEffect, TransitionGroup, ref, Ref, reactive } from 'vue' import event from '/src/components/music-score/event' import SettingState from '/src/pages/detail/setting-state' import state from '../state' import runtime, { getFirsrNoteByMeasureListIndex } from '../runtime' import { getActtiveNoteByTimes, getBoundingBoxByverticalNote, getNoteBySlursStart } from '../helpers' import { formatZoom } from '/src/helpers/utils' import styles from './index.module.less' import classNames from 'classnames' import { modelType } from '/src/subpages/colexiu/buttons' import { restPromptData } from '/src/helpers/restPrompt' import { unitTestData } from '/src/subpages/colexiu/unitTest' import { metronomeData } from "/src/helpers/metronome"; const sectionRef: Ref = ref(null) const noteInfoItems = reactive({ duration: false, numerator: false, denominator: false, i: false, time: false, speed: false, }) ;(window as unknown as Window & { setNoteInfoItems: (data: typeof noteInfoItems) => void }).setNoteInfoItems = ( data ) => { for (const key in data) { if (Object.prototype.hasOwnProperty.call(data, key)) { noteInfoItems[key as keyof typeof noteInfoItems] = data[key as keyof typeof noteInfoItems] } } } /** 根据位置去重复 */ const uniqueByPosition = (list: any[]) => { const data: { [key in string]: any } = {} for (const item of list) { if (item && item.start_x) { data[`${item.x}-${item.y}`] = item } } return data } watchEffect(() => { // 监听状态节点选择状态并且开启提示 if (state.sectionStatus) { if (!state.section.length) { state.befireSection = null Toast.clear() Toast({ duration: 0, message: '请选择开始节点', position: 'top' }) } else if (state.section.length === 1) { Toast.clear() Toast({ duration: 0, message: '请选择结束节点', position: 'top' }) } } else { state.section = [] state.sectionBoundingBoxs = [] Toast.clear() } }) export default defineComponent({ name: 'section-box', props: { type: { type: String, default: 'evaluating', }, top: { type: Number, default: 0, }, left: { type: Number, default: 0, }, }, data() { return { sectionTop: 0, sectionLeft: 0, } }, methods: { getBoundingBoxByNote(note: any, extra?: any) { const mBoundingBox = note.sourceMeasure?.verticalMeasureList?.[0]?.boundingBox if (!mBoundingBox) { return null } const boundingBox = { ...mBoundingBox.absolutePosition, ...mBoundingBox.size, ...extra, } boundingBox.x = boundingBox.x * 10 boundingBox.y = boundingBox.y * 10 boundingBox.width = boundingBox.width * 10 boundingBox.height = boundingBox.height * 10 if (note?.sourceMeasure?.verticalMeasureList?.[0]?.stave?.height) { boundingBox.height = note.sourceMeasure?.verticalMeasureList?.[0]?.stave?.height } return boundingBox }, // 计算选择范围内的小节宽度高度 setSettionBackground() { state.sectionBoundingBoxs = [] const [start, end] = state.section.sort((a, b) => a.i - b.i) let startIndex = 0, endIndex = 0 const selectNoteNum = Math.abs(end.i - start.i) + 1 startIndex = 0 endIndex = end.noteLength const seteds: number[] = [] const heights: number[] = [] for (let index = 0; index < selectNoteNum; index++) { const activeIndex = index + start.i const activeTimeNote = state.times[activeIndex] const activeNote = activeTimeNote.noteElement const measureListIndex = activeNote.sourceMeasure.measureListIndex // 如果没有节拍器,默认提前一个小节 if (index === 0 && measureListIndex !== 0 && !state.needTick) { const firstNote = getFirsrNoteByMeasureListIndex(measureListIndex - 1) const fnote = firstNote?.noteElement if (fnote) { // console.log(fnote.sourceMeasure?.measureListIndex, start.noteElement?.sourceMeasure?.measureListIndex) for ( let j = fnote.sourceMeasure?.measureListIndex; j < start.noteElement?.sourceMeasure?.measureListIndex; j++ ) { if (!seteds.includes(j)) { for (const item of state.times) { if (item.noteElement?.sourceMeasure?.measureListIndex === j && !seteds.includes(j)) { const boundingBox = this.getBoundingBoxByNote(item.noteElement, { before: true, }) state.befireSection = item if (!boundingBox) { continue } state.sectionBoundingBoxs.push(boundingBox) heights.push(boundingBox.height) seteds.push(j) } } } } } } if (!seteds.includes(measureListIndex)) { seteds.push(measureListIndex) const boundingBox = this.getBoundingBoxByNote(activeNote) if (boundingBox) { state.sectionBoundingBoxs.push(boundingBox) heights.push(boundingBox.height) } } } state.sectionBoundingBoxs.map((item) => { item.height = Math.max(...heights) return item }) }, setSection(evt: MouseEvent) { const activeNote = getActtiveNoteByTimes(evt) if (activeNote && state.section.length < 2) { const sectionLength = state.section.length if (sectionLength === 0) { const note = getNoteBySlursStart(activeNote, true) state.section.push(state.times[note.i - note.si]) } if (sectionLength === 1) { const note = getNoteBySlursStart(activeNote, true, 'end') state.section.push(state.times[note.i - note.si + note.noteLength - 1]) } } if (state.section.length === 2) { Toast.clear() this.setSettionBackground() } }, sectionClick(evt: MouseEvent): void { metronomeData.isClick = true; if (!state.sectionStatus) { if (state.mode !== 'contact' || runtime.evaluatingStatus) { return } event.emit('section-click', evt) } else { // console.log(state.sectionStatus, evt) this.setSection(evt) } }, /** 重复时仅处理一次 */ filterTimes(times: any[]) { const ids: string[] = [] return times.filter((item) => { const has = ids.includes(item.id) ids.push(item.id) return !has }) }, }, mounted() { Toast.clear() state.section = [] this.sectionTop = sectionRef.value?.getBoundingClientRect().top this.sectionLeft = sectionRef.value?.getBoundingClientRect().left }, beforeUnmount() { Toast.clear() state.sectionStatus = false state.section = [] }, render() { const eyeBorderColor = SettingState.sett.eyeProtection ? 'var(--eye-section-border-color)' : 'var(--section-border-color)' const eyeBackground = (item: any) => { if (SettingState.sett.eyeProtection) { return item.before ? 'var(--section-background-color)' : 'var(--eye-section-background-color)' } else { return item.before ? 'var(--eye-section-background-color)' : 'var(--section-background-color)' } } // console.log(uniqueByPosition(Object.values(state.evaluatings)), 'state.evaluatings') // console.log('state.sectionFlash', state.sectionFlash) const activeNumberXml = state.times[runtime.activeIndex]?.noteElement?.sourceMeasure?.MeasureNumberXML || -2 const restMeasure = restPromptData.list.find((n) => { const m = activeNumberXml - n.measureNumberXML return n.allRests && m >= 0 && m < n.multipleRestMeasures }) const restNumber = restMeasure ? activeNumberXml - restMeasure.measureNumberXML + 1 : 0 const img: HTMLElement = document.querySelector('#cursorImg-0')! if (restMeasure){ img && metronomeData.cursorMode === 2 && img.classList.remove('lineHide') } else { img && metronomeData.cursorMode === 2 && img.classList.add('lineHide') } return (
{/* 为每个音符添加一个遮罩方便点击 */} {this.filterTimes(state.times).map((item) => { if (!item.svgElelent) { return null } let bbox: any try { bbox = item.svgElelent.bbox || item.svgElelent.getBoundingBox?.() if (!bbox && item.svgElelent?.attrs?.el) { bbox = item.svgElelent.attrs.el.getBBox() bbox.w = bbox.width < 15 ? 15 : bbox.width bbox.h = bbox.height < 11 ? 11 : bbox.height } } catch (error) { console.log(error) } if (!bbox) { return null } if (SettingState.sett.type === 'jianpu' && item.svgElelent) { if (item.svgElelent.top_y && item.svgElelent.note_height) { bbox.y = item.svgElelent.top_y - item.svgElelent.note_height } } let { x, y, h, w } = bbox let boundingBox = null // let measureBg = false const activeNumberIndex = (item?.noteElement?.sourceMeasure?.measureNumber + 1) || -2; // console.log(activeNumberIndex,'👀👀',metronomeData.activeMetro?.measureNumberXML) if (item.si === 0) { boundingBox = this.getBoundingBoxByNote(item.noteElement) } return ( <> {item.si === 0 && boundingBox && (
state.section[1]?.noteElement?.sourceMeasure.MeasureNumberXML ? 'rgba(0, 0, 0,.28)' : 'var(--section-background-color)' }` : '', }} onClick={state.sectionStatus ? this.sectionClick : undefined} > {metronomeData.cursorMode === 2 && activeNumberIndex === metronomeData.activeMetro?.measureNumberXML &&
}
)}
v === true) ? 'rgba(255, 255, 255, 0.8)' : '', }} onClick={this.sectionClick} > {noteInfoItems.duration && ( <> {parseInt(item.duration * 100 + '') / 100}
)} {noteInfoItems.time && ( <> {item.time.toFixed(2)}
)} {noteInfoItems.numerator && ( <> {item.noteElement.sourceMeasure.activeTimeSignature.numerator}

)} {noteInfoItems.denominator && ( <> {item.noteElement.sourceMeasure.activeTimeSignature.denominator}

)} {noteInfoItems.i && ( <> {item.i}

)} {noteInfoItems.speed && ( <> {item.speed.toFixed(0)}

)}
) })} {/* 范围的两个括号 */} {state.section.map((item, index) => { const boundingBox = getBoundingBoxByverticalNote(item) let X: number | undefined = undefined try { const bbox = item.svgElelent.bbox || item.svgElelent?.getBoundingBox() X = formatZoom(bbox?.x || (index === 0 ? boundingBox.start_x : boundingBox.end_x)) } catch (error) { console.log(error) } if (!X) { return null } if (index === 0 && boundingBox) { // console.log('左',formatZoom(boundingBox.y) - 5, formatZoom(boundingBox.x),X,SettingState.sett.scoreSize) return (
) } if (index === 1 && boundingBox) { // console.log('右',formatZoom(boundingBox.y - 5), formatZoom(boundingBox.end_x),X) return (
) } return null })} {/* 添加选择背景 */} {state.sectionBoundingBoxs.map((item) => { return (
) })} {/* 分数信息 */} {Object.values(uniqueByPosition(Object.values(state.evaluatings))).map((item: any) => { if (!item) return return (
{this.type === 'evaluating' ? ( {item.score} ) : null}
) })}
{/* 休止符等待 */} {restMeasure && (
{restNumber}
)}
) }, })