|
@@ -0,0 +1,439 @@
|
|
|
|
+/**
|
|
|
|
+ * 一行谱动画
|
|
|
|
+ */
|
|
|
|
+import { watch, ref, Ref } from "vue"
|
|
|
|
+import { getAudioCurrentTime } from "/src/view/audio-list"
|
|
|
|
+import state from "/src/state"
|
|
|
|
+import "./index.less"
|
|
|
|
+
|
|
|
|
+type pointsPosType = { x: number; y: number; MeasureNumberXML: number }[]
|
|
|
|
+type smoothAnimationType = {
|
|
|
|
+ isShow: Ref<boolean>
|
|
|
|
+ canvasDom: null | HTMLCanvasElement
|
|
|
|
+ canvasCtx: null | undefined | CanvasRenderingContext2D
|
|
|
|
+ canvasDomWith: number
|
|
|
|
+ canvasDomHeight: number
|
|
|
|
+ smoothAnimationBoxDom: null | HTMLElement
|
|
|
|
+ smoothBotDom: null | HTMLElement
|
|
|
|
+ osmdCanvasPageDom: null | HTMLElement
|
|
|
|
+ osdmScrollDom: null | HTMLElement
|
|
|
|
+ pointsPos: pointsPosType
|
|
|
|
+ translateXNum: number
|
|
|
|
+ aveSpeed: number
|
|
|
|
+ clientWidth: number
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const _numberOfSegments = 58 // 中间切割线的个数
|
|
|
|
+
|
|
|
|
+export const smoothAnimationState = {
|
|
|
|
+ isShow: ref(false), // 是否显示
|
|
|
|
+ canvasDom: null,
|
|
|
|
+ canvasCtx: null,
|
|
|
|
+ canvasDomWith: 0,
|
|
|
|
+ canvasDomHeight: 160,
|
|
|
|
+ smoothAnimationBoxDom: null,
|
|
|
|
+ smoothBotDom: null,
|
|
|
|
+ osmdCanvasPageDom: null,
|
|
|
|
+ osdmScrollDom: null,
|
|
|
|
+ pointsPos: [], // 计算之后的点坐标数组
|
|
|
|
+ translateXNum: 0, // 当前谱面的translateX的距离 谱面的位置信息 由translateX和scrollLeft的偏移一起决定
|
|
|
|
+ aveSpeed: 0, // 谱面的一帧的平均速度
|
|
|
|
+ clientWidth: 0 // 屏幕宽度
|
|
|
|
+} as smoothAnimationType
|
|
|
|
+
|
|
|
|
+// 监听显示与隐藏
|
|
|
|
+watch(smoothAnimationState.isShow, () => {
|
|
|
|
+ if (smoothAnimationState.isShow.value) {
|
|
|
|
+ smoothAnimationState.smoothAnimationBoxDom?.classList.remove("smoothAnimationBoxHide")
|
|
|
|
+ moveSmoothAnimation(moveState.progress, moveState.activeIndex)
|
|
|
|
+ } else {
|
|
|
|
+ smoothAnimationState.smoothAnimationBoxDom?.classList.add("smoothAnimationBoxHide")
|
|
|
|
+ }
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 初始化
|
|
|
|
+ */
|
|
|
|
+export function initSmoothAnimation() {
|
|
|
|
+ // 创建dom
|
|
|
|
+ createSmoothAnimation()
|
|
|
|
+ // 初始化动画数据
|
|
|
|
+ const batePos = getPointsPosByBatePos()
|
|
|
|
+ console.log(batePos)
|
|
|
|
+ smoothAnimationState.pointsPos = createSmoothCurvePoints(batePos, undefined, undefined, _numberOfSegments)
|
|
|
|
+ // 谱面的平均速度(因为可能有反复的情况所以实际距离要加上反复的距离)
|
|
|
|
+ const canvasDomPath = batePos.reduce((path, item, index, arr) => {
|
|
|
|
+ if (index !== 0) {
|
|
|
|
+ if (Math.abs(item.MeasureNumberXML - arr[index - 1].MeasureNumberXML) <= 1) {
|
|
|
|
+ path += item.x - arr[index - 1].x
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return path
|
|
|
|
+ }, 0)
|
|
|
|
+ smoothAnimationState.aveSpeed = (canvasDomPath / (state.times[state.times.length - 1].time - state.times[0].time) / 1000) * 16.67
|
|
|
|
+ // 当前屏幕的宽度
|
|
|
|
+ calcClientWidth()
|
|
|
|
+ document.addEventListener("resize", calcClientWidth)
|
|
|
|
+ smoothAnimationState.isShow.value = true
|
|
|
|
+ console.log(smoothAnimationState, 777777)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 销毁
|
|
|
|
+ */
|
|
|
|
+export function destroySmoothAnimation() {
|
|
|
|
+ document.removeEventListener("resize", calcClientWidth)
|
|
|
|
+ smoothAnimationState.smoothAnimationBoxDom?.remove()
|
|
|
|
+ Object.assign(smoothAnimationState, {
|
|
|
|
+ canvasDom: null,
|
|
|
|
+ canvasCtx: null,
|
|
|
|
+ canvasDomWith: 0,
|
|
|
|
+ canvasDomHeight: 160,
|
|
|
|
+ smoothAnimationBoxDom: null,
|
|
|
|
+ smoothBotDom: null,
|
|
|
|
+ osmdCanvasPageDom: null,
|
|
|
|
+ osdmScrollDom: null,
|
|
|
|
+ pointsPos: [],
|
|
|
|
+ translateXNum: 0,
|
|
|
|
+ aveSpeed: 0,
|
|
|
|
+ clientWidth: 0
|
|
|
|
+ })
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 根据播放时间进度移动处理
|
|
|
|
+ */
|
|
|
|
+export function moveSmoothAnimationByPlayTime() {
|
|
|
|
+ const currentTime = getAudioCurrentTime()
|
|
|
|
+ if (currentTime <= state.fixtime) return
|
|
|
|
+ if (currentTime > state.times.last()?.time) return
|
|
|
|
+ // 当休止小节,可能当前音符在谱面上没有实际的音符(没有bbox),所以往后找谱面上有的音符
|
|
|
|
+ let nextIndex = state.activeNoteIndex + 1
|
|
|
|
+ let nextBBox = state.times[nextIndex]?.bbox
|
|
|
|
+ while (!nextBBox && nextIndex < state.times.length) {
|
|
|
|
+ nextIndex += 1
|
|
|
|
+ nextBBox = state.times[nextIndex]?.bbox
|
|
|
|
+ }
|
|
|
|
+ // 当前的音符和下一个音符之间的时值
|
|
|
|
+ const noteDuration = state.times[nextIndex].time - state.times[state.activeNoteIndex]?.time
|
|
|
|
+ // 当前时值在该区间的占比
|
|
|
|
+ const playProgress = (currentTime - state.times[state.activeNoteIndex]?.time) / noteDuration
|
|
|
|
+ moveSmoothAnimation(playProgress, state.activeNoteIndex)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 移动处理
|
|
|
|
+ * progress 当前音符到下一个音符的距离百分比
|
|
|
|
+ * activeIndex 当前
|
|
|
|
+ */
|
|
|
|
+const moveState = {
|
|
|
|
+ oldIndex: -1, // 上一次index
|
|
|
|
+ progress: 0,
|
|
|
|
+ activeIndex: 0
|
|
|
|
+}
|
|
|
|
+export function moveSmoothAnimation(progress: number, activeIndex: number) {
|
|
|
|
+ moveState.progress = progress
|
|
|
|
+ moveState.activeIndex = activeIndex
|
|
|
|
+ // if (!smoothAnimationState.isShow.value) {
|
|
|
|
+ // return
|
|
|
|
+ // }
|
|
|
|
+ // 计算 下一个音符index 在pointsPos 中的距离
|
|
|
|
+ const nextPointsIndex = (activeIndex + 1) * (_numberOfSegments + 1) - 1
|
|
|
|
+ // 百分比转为当前的index 距离个数
|
|
|
|
+ const progressCalcIndex = Math.round(progress * _numberOfSegments)
|
|
|
|
+ // // 当前的index
|
|
|
|
+ let nowIndex = nextPointsIndex - _numberOfSegments + progressCalcIndex
|
|
|
|
+ // 当前计算的位置和上一次值一样时候不运行
|
|
|
|
+ if (moveState.oldIndex === nowIndex) {
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ moveState.oldIndex = nowIndex
|
|
|
|
+ const nowPointsPos = smoothAnimationState.pointsPos[nowIndex]
|
|
|
|
+ smoothAnimationState.canvasCtx?.clearRect(0, 0, smoothAnimationState.canvasDomWith, smoothAnimationState.canvasDomHeight)
|
|
|
|
+ // 移动
|
|
|
|
+ smoothAnimationMove(
|
|
|
|
+ {
|
|
|
|
+ x: nowPointsPos.x - 20,
|
|
|
|
+ y: nowPointsPos.y - 25
|
|
|
|
+ },
|
|
|
|
+ smoothAnimationState.pointsPos,
|
|
|
|
+ smoothAnimationState.pointsPos.slice(0, nowIndex)
|
|
|
|
+ )
|
|
|
|
+ move_osmd(nowPointsPos)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 谱面移动逻辑
|
|
|
|
+ */
|
|
|
|
+function move_osmd(nowPointsPos: pointsPosType[0]) {
|
|
|
|
+ const speed = smoothAnimationState.aveSpeed * (state.speed / 60)
|
|
|
|
+ const clientWidth = smoothAnimationState.clientWidth
|
|
|
|
+ const clientMidWidth = smoothAnimationState.clientWidth / 2
|
|
|
|
+ const { left, right, width } = smoothAnimationState.smoothBotDom!.getBoundingClientRect()
|
|
|
|
+ const midBotNum = left + width / 2
|
|
|
|
+ // 分阶段移动
|
|
|
|
+ if (right > clientWidth) {
|
|
|
|
+ // 移动超过屏幕时候
|
|
|
|
+ smoothAnimationState.translateXNum = 0
|
|
|
|
+ smoothAnimationState.osdmScrollDom!.scrollLeft = nowPointsPos.x - clientWidth * 0.9
|
|
|
|
+ } else if (left < 0) {
|
|
|
|
+ // 移动小于屏幕时候
|
|
|
|
+ smoothAnimationState.translateXNum = 0
|
|
|
|
+ smoothAnimationState.osdmScrollDom!.scrollLeft = nowPointsPos.x - clientWidth * 0.1
|
|
|
|
+ } else if (midBotNum > clientMidWidth - clientWidth * 0.3 && midBotNum <= clientMidWidth - clientWidth * 0.25) {
|
|
|
|
+ smoothAnimationState.translateXNum += speed * 0.5
|
|
|
|
+ } else if (midBotNum > clientMidWidth - clientWidth * 0.25 && midBotNum <= clientMidWidth - clientWidth * 0.2) {
|
|
|
|
+ smoothAnimationState.translateXNum += speed * 0.6
|
|
|
|
+ } else if (midBotNum > clientMidWidth - clientWidth * 0.2 && midBotNum <= clientMidWidth - clientWidth * 0.15) {
|
|
|
|
+ smoothAnimationState.translateXNum += speed * 0.7
|
|
|
|
+ } else if (midBotNum > clientMidWidth - clientWidth * 0.15 && midBotNum <= clientMidWidth - clientWidth * 0.1) {
|
|
|
|
+ smoothAnimationState.translateXNum += speed * 0.8
|
|
|
|
+ } else if (midBotNum > clientMidWidth - clientWidth * 0.1 && midBotNum <= clientMidWidth - clientWidth * 0.05) {
|
|
|
|
+ smoothAnimationState.translateXNum += speed * 0.9
|
|
|
|
+ } else if (midBotNum > clientMidWidth - clientWidth * 0.05 && midBotNum <= clientMidWidth) {
|
|
|
|
+ smoothAnimationState.translateXNum += speed
|
|
|
|
+ } else if (midBotNum > clientMidWidth && midBotNum <= clientMidWidth + clientWidth * 0.05) {
|
|
|
|
+ smoothAnimationState.translateXNum += speed * 1.2
|
|
|
|
+ } else if (midBotNum > clientMidWidth + clientWidth * 0.05 && midBotNum <= clientMidWidth + clientWidth * 0.1) {
|
|
|
|
+ smoothAnimationState.translateXNum += speed * 1.4
|
|
|
|
+ } else if (midBotNum > clientMidWidth + clientWidth * 0.1 && midBotNum <= clientMidWidth + clientWidth * 0.15) {
|
|
|
|
+ smoothAnimationState.translateXNum += speed * 1.7
|
|
|
|
+ } else if (midBotNum > clientMidWidth + clientWidth * 0.15 && midBotNum <= clientMidWidth + clientWidth * 0.2) {
|
|
|
|
+ smoothAnimationState.translateXNum += speed * 2
|
|
|
|
+ } else if (midBotNum > clientMidWidth + clientWidth * 0.2 && midBotNum <= clientMidWidth + clientWidth * 0.25) {
|
|
|
|
+ smoothAnimationState.translateXNum += speed * 2.4
|
|
|
|
+ } else if (midBotNum > clientMidWidth + clientWidth * 0.25 && midBotNum <= clientMidWidth + clientWidth * 0.3) {
|
|
|
|
+ smoothAnimationState.translateXNum += speed * 2.8
|
|
|
|
+ } else if (midBotNum > clientMidWidth + clientWidth * 0.3 && midBotNum <= clientMidWidth + clientWidth * 0.35) {
|
|
|
|
+ smoothAnimationState.translateXNum += speed * 3.3
|
|
|
|
+ } else if (midBotNum > clientMidWidth + clientWidth * 0.35 && midBotNum <= clientMidWidth + clientWidth * 0.4) {
|
|
|
|
+ smoothAnimationState.translateXNum += speed * 3.8
|
|
|
|
+ } else if (midBotNum > clientMidWidth + clientWidth * 0.4 && midBotNum <= clientMidWidth + clientWidth * 0.45) {
|
|
|
|
+ smoothAnimationState.translateXNum += speed * 4.4
|
|
|
|
+ } else if (midBotNum > clientMidWidth + clientWidth * 0.45 && midBotNum <= clientMidWidth + clientWidth * 0.5) {
|
|
|
|
+ smoothAnimationState.translateXNum += speed * 5
|
|
|
|
+ }
|
|
|
|
+ smoothAnimationState.osmdCanvasPageDom!.style.transform = `translateX(-${smoothAnimationState.translateXNum}px)`
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 进度条和块移动方法
|
|
|
|
+ */
|
|
|
|
+function smoothAnimationMove(pos: { x: number; y: number }, pointsPos: pointsPosType, progresspointsPos?: pointsPosType) {
|
|
|
|
+ smoothAnimationState.smoothBotDom && (smoothAnimationState.smoothBotDom.style.transform = `translate(${pos.x}px, ${pos.y}px)`)
|
|
|
|
+ smoothAnimationState.canvasCtx && drawSmoothCurve(smoothAnimationState.canvasCtx, pointsPos, progresspointsPos)
|
|
|
|
+}
|
|
|
|
+/**
|
|
|
|
+ * 计算屏幕宽度
|
|
|
|
+ */
|
|
|
|
+function calcClientWidth() {
|
|
|
|
+ smoothAnimationState.clientWidth = document.body.clientWidth
|
|
|
|
+}
|
|
|
|
+/**
|
|
|
|
+ * 创建dom
|
|
|
|
+ */
|
|
|
|
+function createSmoothAnimation() {
|
|
|
|
+ // osdmScrollDom
|
|
|
|
+ const osdmScrollDom = document.querySelector("#musicAndSelection") as HTMLElement
|
|
|
|
+ smoothAnimationState.osdmScrollDom = osdmScrollDom
|
|
|
|
+ // osmdCanvasPage
|
|
|
|
+ const osmdCanvasPageDom = document.querySelector("#osmdCanvasPage1") as HTMLElement
|
|
|
|
+ smoothAnimationState.osmdCanvasPageDom = osmdCanvasPageDom
|
|
|
|
+ // box
|
|
|
|
+ const smoothAnimationBoxDom = document.createElement("div")
|
|
|
|
+ smoothAnimationBoxDom.className = "smoothAnimationBox"
|
|
|
|
+ smoothAnimationState.smoothAnimationBoxDom = smoothAnimationBoxDom
|
|
|
|
+ //canvas
|
|
|
|
+ const smoothCanvasDom = document.createElement("canvas")
|
|
|
|
+ smoothCanvasDom.className = "smoothCanvas"
|
|
|
|
+ smoothAnimationState.canvasDom = smoothCanvasDom
|
|
|
|
+ smoothAnimationState.canvasDomWith = osmdCanvasPageDom?.offsetWidth | 0
|
|
|
|
+ smoothCanvasDom.width = smoothAnimationState.canvasDomWith
|
|
|
|
+ smoothCanvasDom.height = smoothAnimationState.canvasDomHeight
|
|
|
|
+ smoothAnimationState.canvasCtx = smoothCanvasDom.getContext("2d")
|
|
|
|
+ // bot
|
|
|
|
+ const smoothBotDom = document.createElement("div")
|
|
|
|
+ smoothBotDom.className = "smoothBot"
|
|
|
|
+ smoothAnimationState.smoothBotDom = smoothBotDom
|
|
|
|
+ smoothAnimationBoxDom.appendChild(smoothCanvasDom)
|
|
|
|
+ smoothAnimationBoxDom.appendChild(smoothBotDom)
|
|
|
|
+ // 添加到 osmdCanvasPage1
|
|
|
|
+ osmdCanvasPageDom?.insertBefore(smoothAnimationBoxDom, osmdCanvasPageDom.firstChild)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 根据音符获取坐标
|
|
|
|
+ */
|
|
|
|
+function getPointsPosByBatePos(): pointsPosType {
|
|
|
|
+ let totalAvInde = 0
|
|
|
|
+ // 取平均值
|
|
|
|
+ const totalAv =
|
|
|
|
+ state.times.reduce((total, item) => {
|
|
|
|
+ if (item.frequency !== -1) {
|
|
|
|
+ // -1 为休止符
|
|
|
|
+ total += item.frequency
|
|
|
|
+ totalAvInde++
|
|
|
|
+ }
|
|
|
|
+ return total
|
|
|
|
+ }, 0) / totalAvInde
|
|
|
|
+ const pointsPos = state.times.reduce((posArr: any[], item) => {
|
|
|
|
+ // 当休止小节,可能当前音符在谱面上没有实际的音符(没有bbox),所以往后找谱面上有的音符
|
|
|
|
+ if (item.bbox) {
|
|
|
|
+ posArr.push({
|
|
|
|
+ MeasureNumberXML: item.MeasureNumberXML,
|
|
|
|
+ x: item.bbox.x,
|
|
|
|
+ y:
|
|
|
|
+ ((((item.frequency === -1 ? totalAv : item.frequency) - totalAv) / totalAv) * smoothAnimationState.canvasDomHeight) / 2 +
|
|
|
|
+ smoothAnimationState.canvasDomHeight / 2 // cavans 高度为160 所以基准为80
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ return posArr
|
|
|
|
+ }, [])
|
|
|
|
+ return pointsPos
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 使用传入的曲线的顶点坐标创建平滑曲线的顶点。
|
|
|
|
+ * @param {Array} points 曲线顶点坐标数组,
|
|
|
|
+ * @param {Float} tension 密集程度,默认为 0.5
|
|
|
|
+ * @param {Boolean} closed 是否创建闭合曲线,默认为 false
|
|
|
|
+ * @param {Int} numberOfSegments 平滑曲线 2 个顶点间的线段数,默认为 20
|
|
|
|
+ * @return {Array} 平滑曲线的顶点坐标数组
|
|
|
|
+ */
|
|
|
|
+function createSmoothCurvePoints(pointsPos: pointsPosType, tension?: number, closed?: boolean, numberOfSegments?: number) {
|
|
|
|
+ if (pointsPos.length <= 2) {
|
|
|
|
+ return pointsPos
|
|
|
|
+ }
|
|
|
|
+ tension = tension ? tension : 0.5
|
|
|
|
+ closed = closed ? true : false
|
|
|
|
+ numberOfSegments = numberOfSegments ? numberOfSegments : 20
|
|
|
|
+ let ps = pointsPos.slice(0),
|
|
|
|
+ result = [],
|
|
|
|
+ x,
|
|
|
|
+ y,
|
|
|
|
+ t1x,
|
|
|
|
+ t2x,
|
|
|
|
+ t1y,
|
|
|
|
+ t2y,
|
|
|
|
+ c1,
|
|
|
|
+ c2,
|
|
|
|
+ c3,
|
|
|
|
+ c4,
|
|
|
|
+ st,
|
|
|
|
+ t,
|
|
|
|
+ i
|
|
|
|
+ if (closed) {
|
|
|
|
+ ps.unshift(pointsPos[pointsPos.length - 1])
|
|
|
|
+ ps.unshift(pointsPos[pointsPos.length - 1])
|
|
|
|
+ ps.push(pointsPos[0])
|
|
|
|
+ } else {
|
|
|
|
+ ps.unshift(pointsPos[0])
|
|
|
|
+ ps.push(pointsPos[pointsPos.length - 1])
|
|
|
|
+ }
|
|
|
|
+ for (i = 1; i < ps.length - 2; i += 1) {
|
|
|
|
+ //console.log(ps[i + 1].MeasureNumberXML, ps[i - 1].MeasureNumberXML, ps[i + 2].MeasureNumberXML, ps[i].MeasureNumberXML)
|
|
|
|
+ t1x = (ps[i + 1].x - ps[i - 1].x) * tension
|
|
|
|
+ t2x = (ps[i + 2].x - ps[i].x) * tension
|
|
|
|
+ t1y = (ps[i + 1].y - ps[i - 1].y) * tension
|
|
|
|
+ t2y = (ps[i + 2].y - ps[i].y) * tension
|
|
|
|
+ // 当中途出现反复 刚开始反复时候 53 52 22 52 (22)中途值会变小 这里强行拉大 防止算法平均值出现很大偏差
|
|
|
|
+ if (ps[i + 1].MeasureNumberXML - ps[i + 2].MeasureNumberXML > 1) {
|
|
|
|
+ const nowNumberXML = ps[i + 1].MeasureNumberXML + 1
|
|
|
|
+ //在当前值的情况下 向前一位
|
|
|
|
+ let index = ps.findIndex(item => {
|
|
|
|
+ return nowNumberXML === item.MeasureNumberXML
|
|
|
|
+ })
|
|
|
|
+ // 查询不到index时候取当前值
|
|
|
|
+ index === -1 && (index = i + 1)
|
|
|
|
+ t2x = (ps[index].x - ps[i].x) * tension
|
|
|
|
+ }
|
|
|
|
+ // 当中途出现反复 结束反复时候 22 53 22 22 (53)中途值会变大 这里强行缩小 防止算法平均值出现很大偏差
|
|
|
|
+ if (ps[i - 1].MeasureNumberXML - ps[i].MeasureNumberXML > 1) {
|
|
|
|
+ //在当前值的情况下 向后一位
|
|
|
|
+ const nowNumberXML = ps[i].MeasureNumberXML - 1
|
|
|
|
+ let index = ps.findIndex((item, index) => {
|
|
|
|
+ return nowNumberXML === item.MeasureNumberXML && nowNumberXML !== ps[index + 1]?.MeasureNumberXML
|
|
|
|
+ })
|
|
|
|
+ // 查询不到index时候取当前值
|
|
|
|
+ index === -1 && (index = i)
|
|
|
|
+ t1x = (ps[i + 1].x - ps[index].x) * tension
|
|
|
|
+ }
|
|
|
|
+ // 当中途出现跳房子 刚开始跳房子时候 35 35 54 35 (54)中途值会变大 这里强行缩小 防止算法平均值出现很大偏差
|
|
|
|
+ if (ps[i + 1].MeasureNumberXML - ps[i + 2].MeasureNumberXML < -1) {
|
|
|
|
+ const nowNumberXML = ps[i + 1].MeasureNumberXML + 1
|
|
|
|
+ //在当前值的情况下 向前一位
|
|
|
|
+ let index = ps.findIndex(item => {
|
|
|
|
+ return nowNumberXML === item.MeasureNumberXML
|
|
|
|
+ })
|
|
|
|
+ // 查询不到index时候取当前值
|
|
|
|
+ index === -1 && (index = i + 1)
|
|
|
|
+ t2x = (ps[index].x - ps[i].x) * tension
|
|
|
|
+ }
|
|
|
|
+ // 当中途出现跳房子 结束跳房子时候 54 35 54 54 (35)中途值会变小 这里强行拉大 防止算法平均值出现很大偏差
|
|
|
|
+ if (ps[i - 1].MeasureNumberXML - ps[i].MeasureNumberXML < -1) {
|
|
|
|
+ const nowNumberXML = ps[i].MeasureNumberXML - 1
|
|
|
|
+ let index = ps.findIndex((item, index) => {
|
|
|
|
+ return nowNumberXML === item.MeasureNumberXML && nowNumberXML !== ps[index + 1]?.MeasureNumberXML
|
|
|
|
+ })
|
|
|
|
+ // 查询不到index时候取当前值
|
|
|
|
+ index === -1 && (index = i)
|
|
|
|
+ t1x = (ps[i + 1].x - ps[index].x) * tension
|
|
|
|
+ }
|
|
|
|
+ const nowMeasureNumberXML = pointsPos[i - 1].MeasureNumberXML
|
|
|
|
+ const nextMeasureNumberXML = pointsPos[i].MeasureNumberXML
|
|
|
|
+ for (t = 0; t <= numberOfSegments; t++) {
|
|
|
|
+ // 小于1时候是反复 大于1是跳房子 不画曲线 停留
|
|
|
|
+ if (nextMeasureNumberXML - nowMeasureNumberXML < 0 || nextMeasureNumberXML - nowMeasureNumberXML > 1) {
|
|
|
|
+ //console.log(x, y)
|
|
|
|
+ result.push({
|
|
|
|
+ x: x as number,
|
|
|
|
+ y: y as number,
|
|
|
|
+ MeasureNumberXML: nowMeasureNumberXML
|
|
|
|
+ })
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+ st = t / numberOfSegments
|
|
|
|
+ c1 = 2 * Math.pow(st, 3) - 3 * Math.pow(st, 2) + 1
|
|
|
|
+ c2 = -(2 * Math.pow(st, 3)) + 3 * Math.pow(st, 2)
|
|
|
|
+ c3 = Math.pow(st, 3) - 2 * Math.pow(st, 2) + st
|
|
|
|
+ c4 = Math.pow(st, 3) - Math.pow(st, 2)
|
|
|
|
+ x = c1 * ps[i].x + c2 * ps[i + 1].x + c3 * t1x + c4 * t2x
|
|
|
|
+ y = c1 * ps[i].y + c2 * ps[i + 1].y + c3 * t1y + c4 * t2y
|
|
|
|
+ //console.log(x, y)
|
|
|
|
+ result.push({
|
|
|
|
+ x,
|
|
|
|
+ y,
|
|
|
|
+ MeasureNumberXML: t === numberOfSegments ? nextMeasureNumberXML : nowMeasureNumberXML
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return result
|
|
|
|
+}
|
|
|
|
+/**
|
|
|
|
+ * 根据坐标划线
|
|
|
|
+ */
|
|
|
|
+function drawSmoothCurve(context: CanvasRenderingContext2D, pointsPos: pointsPosType, progresspointsPos?: pointsPosType) {
|
|
|
|
+ context.lineWidth = 4
|
|
|
|
+ context.lineJoin = "round" // 优化锯齿
|
|
|
|
+ context.strokeStyle = "rgba(255,255,255,0.6)"
|
|
|
|
+ drawLines(context, pointsPos)
|
|
|
|
+ if (progresspointsPos?.length) {
|
|
|
|
+ context.strokeStyle = "#FFC121"
|
|
|
|
+ drawLines(context, progresspointsPos)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+function drawLines(context: CanvasRenderingContext2D, points: pointsPosType) {
|
|
|
|
+ context.beginPath()
|
|
|
|
+ context.moveTo(points[0].x, points[0].y)
|
|
|
|
+ for (let i = 1; i < points.length - 1; i++) {
|
|
|
|
+ if (Math.abs(points[i].MeasureNumberXML - points[i - 1].MeasureNumberXML) > 1) {
|
|
|
|
+ // 取消反复和跳房子连线
|
|
|
|
+ context.stroke()
|
|
|
|
+ context.beginPath()
|
|
|
|
+ context.moveTo(points[i + 1].x, points[i + 1].y)
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+ context.lineTo(points[i].x, points[i].y)
|
|
|
|
+ }
|
|
|
|
+ context.stroke()
|
|
|
|
+}
|