From 6822f5ed33d949d91a9e4a4b202425998d0615d8 Mon Sep 17 00:00:00 2001 From: 3dtours Date: Sat, 25 Jul 2026 10:05:40 +0700 Subject: [PATCH] fix: WaveformLane grid now matches TempoTrackLane bar:beat structure, bar:beat always shown on tempo lane --- app/static/js/app.jsx | 53 ++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/app/static/js/app.jsx b/app/static/js/app.jsx index 8430e25..db51476 100644 --- a/app/static/js/app.jsx +++ b/app/static/js/app.jsx @@ -443,26 +443,28 @@ const WaveformLane = ({ const PADDING_LEFT = barDuration; // 1-bar margin on left const tStart = scrollLeftVal / zoom - PADDING_LEFT; const tEnd = (scrollLeftVal + drawWidth) / zoom + PADDING_LEFT; - let gridSpacing = 1.0; + const firstBeat = Math.floor(tStart / beatDuration) * beatDuration; + let snapDivisor = 1; if (snapValue && snapValue !== 'free') { - const beatDuration = 60 / parseFloat(bpm || 120); - let divisor = 1; - if (snapValue === '4') divisor = 4; else if (snapValue === '1') divisor = 1; else if (snapValue === '1/2') divisor = 0.5; else if (snapValue === '1/4') divisor = 0.25; else if (snapValue === '1/8') divisor = 0.125; else if (snapValue === '1/16') divisor = 0.0625; else if (snapValue === '1/32') divisor = 0.03125; - gridSpacing = beatDuration * divisor; - } else { - gridSpacing = 60 / parseFloat(bpm || 120); + if (snapValue === '4') snapDivisor = 4; else if (snapValue === '1') snapDivisor = 1; else if (snapValue === '1/2') snapDivisor = 0.5; else if (snapValue === '1/4') snapDivisor = 0.25; else if (snapValue === '1/8') snapDivisor = 0.125; else if (snapValue === '1/16') snapDivisor = 0.0625; else if (snapValue === '1/32') snapDivisor = 0.03125; } - let drawSpacing = gridSpacing; - while (drawSpacing * zoom < 10) { - drawSpacing *= 2; - } - const firstGridStep = Math.floor(tStart / drawSpacing) * drawSpacing; - for (let s = firstGridStep; s <= tEnd; s += drawSpacing) { - const localX = (s - tStart) * zoom; + for (let t = firstBeat; t <= tEnd; t += beatDuration) { + const beatNum = Math.floor(t / beatDuration) + 1; + const isBar = beatNum % 4 === 1; + const localX = (t - tStart) * zoom; + if (localX < -200 || localX > drawWidth + 200) continue; + ctx.strokeStyle = isBar ? 'rgba(255, 255, 255, 0.12)' : 'rgba(255, 255, 255, 0.04)'; + ctx.lineWidth = isBar ? 1.2 : 0.5; ctx.beginPath(); ctx.moveTo(localX, 0); ctx.lineTo(localX, height); ctx.stroke(); + if (isBar && zoom >= 2) { + ctx.fillStyle = 'rgba(255, 255, 255, 0.15)'; + ctx.font = 'bold 7px Inter, sans-serif'; + ctx.textAlign = 'left'; + ctx.fillText(`${Math.ceil(beatNum / 4)}`, localX + 2, 10); + } } // Draw waveform lane @@ -1237,22 +1239,10 @@ const TempoTrackLane = ({ ctx.fillStyle = 'rgba(255, 255, 255, 0.7)'; ctx.font = 'bold 9px Inter, sans-serif'; ctx.textAlign = 'left'; + ctx.fillStyle = 'rgba(255, 255, 255, 0.7)'; + ctx.font = 'bold 9px Inter, sans-serif'; + ctx.textAlign = 'left'; ctx.fillText(`${Math.ceil(beatNum / 4)}`, localX + 3, 11); - } else if (zoom >= 2) { - ctx.strokeStyle = 'rgba(255, 255, 255, 0.08)'; - ctx.lineWidth = 1; - ctx.beginPath(); - ctx.moveTo(localX, 0); - ctx.lineTo(localX, height); - ctx.stroke(); - if (zoom >= 5) { - ctx.fillStyle = 'rgba(255, 255, 255, 0.4)'; - ctx.font = '8px Inter, sans-serif'; - ctx.textAlign = 'left'; - const bar = Math.ceil(beatNum / 4); - const beat = ((beatNum - 1) % 4) + 1; - ctx.fillText(`${bar}:${beat}`, localX + 2, 9); - } } else { ctx.strokeStyle = 'rgba(255, 255, 255, 0.08)'; ctx.lineWidth = 1; @@ -1261,6 +1251,11 @@ const TempoTrackLane = ({ ctx.lineTo(localX, height); ctx.stroke(); } + ctx.fillStyle = 'rgba(255, 255, 255, 0.35)'; + ctx.font = '7px Inter, sans-serif'; + const bar = Math.ceil(beatNum / 4); + const beat = ((beatNum - 1) % 4) + 1; + ctx.fillText(`${bar}:${beat}`, localX + 2, height - 3); } // Draw snap sub-ticks at the bottom