123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- 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, getBoundingBoxByNote, changeSpeed } from '../runtime'
- import { getActtiveNoteByTimes, getBoundingBoxByverticalNote, getNoteBySlursStart, setSettionBackground } 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: {
- 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])
- // 选段状态需要重置播放倍率为1
- runtime.basePlayRate = 1;
- const currentItem: any = state.section[0];
- const currentSpeed = currentItem?.measureSpeed ? currentItem.measureSpeed : state.activeSpeed;
- changeSpeed(currentSpeed)
- }
- }
- if (state.section.length === 2) {
- Toast.clear()
- 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 (
- <div class={styles.section} ref={sectionRef}>
- {/* 为每个音符添加一个遮罩方便点击 */}
- {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 = getBoundingBoxByNote(item.noteElement)
- }
- return (
- <>
- {item.si === 0 && boundingBox && (
- <div
- data-id={item.id}
- data-num={item.noteElement.sourceMeasure.MeasureNumberXML}
- style={{
- position: 'absolute',
- top: formatZoom(boundingBox.y) + 'px',
- left: formatZoom(boundingBox.x) + 'px',
- height: formatZoom(boundingBox.height) + 'px',
- width: formatZoom(boundingBox.width) + 'px',
- background: unitTestData.isSelectMeasureMode && state.sectionStatus
- ? `${
- item?.noteElement?.sourceMeasure?.MeasureNumberXML <
- state.section[0]?.noteElement?.sourceMeasure?.MeasureNumberXML ||
- item?.noteElement?.sourceMeasure?.MeasureNumberXML >
- 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 && <div class={styles.lineTEST} style={{ left: metronomeData.activeMetro.left }}></div>}
- </div>
- )}
- <div
- data-id={item.id}
- data-vf={'vf' + item.id}
- class={styles.noteWrap}
- style={{
- position: 'absolute',
- top: formatZoom(y) + 'px',
- left: formatZoom(x) + 'px',
- height: formatZoom(h) + 'px',
- width: formatZoom(w) + 'px',
- // background: 'rgba(0, 0, 0, 0.5)',
- background: Object.values(noteInfoItems).find((v) => v === true) ? 'rgba(255, 255, 255, 0.8)' : '',
- }}
- onClick={this.sectionClick}
- >
- {noteInfoItems.duration && (
- <>
- {parseInt(item.duration * 100 + '') / 100}
- <br />
- </>
- )}
- {noteInfoItems.time && (
- <>
- {item.time.toFixed(2)}
- <br />
- </>
- )}
- {noteInfoItems.numerator && (
- <>
- {item.noteElement.sourceMeasure.activeTimeSignature.numerator}
- <br />
- <br />
- </>
- )}
- {noteInfoItems.denominator && (
- <>
- {item.noteElement.sourceMeasure.activeTimeSignature.denominator}
- <br />
- <br />
- </>
- )}
- {noteInfoItems.i && (
- <>
- {item.i}
- <br />
- <br />
- </>
- )}
- {noteInfoItems.speed && (
- <>
- {item.speed.toFixed(0)}
- <br />
- <br />
- </>
- )}
- <div class={[styles.noteBase, styles.noteRight]}>
- <Icon name="success" size="16" color="#2DC7AA" />
- </div>
- <div class={[styles.noteBase, styles.noteError]}>
- <Icon name="cross" size="16" color="red" />
- </div>
- </div>
- </>
- )
- })}
- {/* 范围的两个括号 */}
- {state.section.map((item, index) => {
- const boundingBox = getBoundingBoxByverticalNote(item)
- let X: number | undefined = undefined
- try {
- const bbox = item.svgElelent?.bbox || item.svgElelent?.getBoundingBox && 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 (
- <div
- style={{
- position: 'absolute',
- top: formatZoom(boundingBox.y) + 'px',
- left: formatZoom(boundingBox.x) + 'px',
- height: formatZoom(boundingBox.height) + 'px',
- border: `5px solid ${eyeBorderColor}`,
- borderColor: `${eyeBorderColor} transparent ${eyeBorderColor} ${eyeBorderColor}`,
- borderRight: 'none',
- width: '5px',
- }}
- ></div>
- )
- }
- if (index === 1 && boundingBox) {
- // console.log('右',formatZoom(boundingBox.y - 5), formatZoom(boundingBox.end_x),X)
- return (
- <div
- style={{
- position: 'absolute',
- top: formatZoom(boundingBox.y) + 'px',
- left: formatZoom(boundingBox.end_x) + 'px',
- height: formatZoom(boundingBox.height) + 'px',
- border: `5px solid ${eyeBorderColor}`,
- borderColor: `${eyeBorderColor} ${eyeBorderColor} ${eyeBorderColor} transparent`,
- borderLeft: 'none',
- width: '5px',
- }}
- ></div>
- )
- }
- return null
- })}
- {/* 添加选择背景 */}
- {state.sectionBoundingBoxs.map((item) => {
- return (
- <div
- class={{
- [styles.flash]: item.before && state.sectionFlash && runtime.playState === 'play',
- }}
- style={{
- position: 'absolute',
- top: formatZoom(item.y) + 'px',
- left: formatZoom(item.x) + 'px',
- height: formatZoom(getBoundingBoxByverticalNote(state.section[0])?.height) + 'px',
- width: formatZoom(item.width) + 'px',
- backgroundColor: eyeBackground(item),
- }}
- ></div>
- )
- })}
- {/* 分数信息 */}
- <TransitionGroup name="list" duration={800}>
- {Object.values(uniqueByPosition(Object.values(state.evaluatings))).map((item: any) => {
- if (!item) return <span />
- return (
- <div
- key={item.y + item.x + item.text}
- class={classNames(styles[item.text], styles.measure, { [styles.dontTransition]: item.dontTransition })}
- style={{
- position: 'absolute',
- top: formatZoom(item.y) + this.top + 'px',
- left: formatZoom(item.x) + this.left + 'px',
- height: formatZoom(item.height) + 'px',
- width: formatZoom(item.width) + 'px',
- }}
- >
- {this.type === 'evaluating' ? (
- <span class={styles.after}>
- <span class={styles.img}></span>
- <span class={styles.font}>{item.score}</span>
- </span>
- ) : null}
- </div>
- )
- })}
- </TransitionGroup>
- {/* 休止符等待 */}
- {restMeasure && (
- <div class={['dotWrap', styles.restMeasure]} style={restMeasure.staveBox}>
- <div class="dot">{restNumber}</div>
- </div>
- )}
- </div>
- )
- },
- })
|