fix: velocity flicker - grid canvas useLayoutEffect sync
This commit is contained in:
@@ -4767,7 +4767,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
||||
};
|
||||
}, [notes, st.currentTime, pixelsPerBeat, st.id, totalBeats, bpm]);
|
||||
|
||||
React.useEffect(() => {
|
||||
React.useLayoutEffect(() => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
@@ -137,7 +137,7 @@ const undoStackRef=React.useRef([]);const redoStackRef=React.useRef([]);const no
|
||||
React.useEffect(()=>{const handleWheelRaw=e=>{if(e.ctrlKey){e.preventDefault();const zoomFactor=e.deltaY<0?1.15:0.85;setRollZoom(prev=>Math.max(15,Math.min(250,prev*zoomFactor)));}};const container=gridScrollRef.current;if(container){container.addEventListener('wheel',handleWheelRaw,{passive:false});}return()=>{if(container){container.removeEventListener('wheel',handleWheelRaw);}};},[]);// Alt + Scroll event listener: fast‑forward playhead + play notes
|
||||
React.useEffect(()=>{const handleCanvasWheel=e=>{const canvas=canvasRef.current;if(!canvas)return;const rect=canvas.getBoundingClientRect();const mx=e.clientX-rect.left;const my=e.clientY-rect.top;const pitch=127-Math.floor(my/NoteHeight);if(e.shiftKey){e.preventDefault();// Shift+scroll on note → change velocity
|
||||
const clickedNote=notes.find(n=>pitch===n.pitch&&mx>=n.start_beat*pixelsPerBeat&&mx<(n.start_beat+n.duration_beats)*pixelsPerBeat);if(clickedNote){const delta=e.deltaY<0?0.05:-0.05;setNotes(prev=>prev.map(n=>n.id===clickedNote.id?{...n,velocity:Math.max(0.05,Math.min(1,(n.velocity||0.8)+delta))}:n));}else{// Shift+scroll on empty space → horizontal scroll
|
||||
const container=gridScrollRef.current;if(container)container.scrollLeft+=e.deltaY;}return;}if(e.altKey){e.preventDefault();const scrollDelta=e.deltaY;const beatSec=60.0/(parseInt(bpm)||120);const step=scrollDelta<0?-0.25:0.25;const currentBeat=(st.currentTime||0)/beatSec;const maxBeats=totalBeats;const newBeat=Math.max(0,Math.min(maxBeats,currentBeat+step));const newTime=newBeat*beatSec;setSubTabs(prev=>prev.map(s=>s.id===st.id?{...s,currentTime:newTime}:s));if(window.SonicSF){const ctx=getAudioContext();const playing=notes.filter(n=>currentBeat<n.start_beat&&newBeat>=n.start_beat);playing.forEach(n=>{window.SonicSF.playNote(n.pitch,(n.velocity||0.8)*127,200,ctx.currentTime,st.instrumentProgram,null);});}}};const canvas=canvasRef.current;if(canvas){canvas.addEventListener('wheel',handleCanvasWheel,{passive:false});}return()=>{if(canvas){canvas.removeEventListener('wheel',handleCanvasWheel);}};},[notes,st.currentTime,pixelsPerBeat,st.id,totalBeats,bpm]);React.useEffect(()=>{const canvas=canvasRef.current;if(!canvas)return;const ctx=canvas.getContext('2d');const dpr=window.devicePixelRatio||1;const h=128*NoteHeight;canvas.width=viewWidth*dpr;canvas.height=h*dpr;ctx.scale(dpr,dpr);// Draw background rows
|
||||
const container=gridScrollRef.current;if(container)container.scrollLeft+=e.deltaY;}return;}if(e.altKey){e.preventDefault();const scrollDelta=e.deltaY;const beatSec=60.0/(parseInt(bpm)||120);const step=scrollDelta<0?-0.25:0.25;const currentBeat=(st.currentTime||0)/beatSec;const maxBeats=totalBeats;const newBeat=Math.max(0,Math.min(maxBeats,currentBeat+step));const newTime=newBeat*beatSec;setSubTabs(prev=>prev.map(s=>s.id===st.id?{...s,currentTime:newTime}:s));if(window.SonicSF){const ctx=getAudioContext();const playing=notes.filter(n=>currentBeat<n.start_beat&&newBeat>=n.start_beat);playing.forEach(n=>{window.SonicSF.playNote(n.pitch,(n.velocity||0.8)*127,200,ctx.currentTime,st.instrumentProgram,null);});}}};const canvas=canvasRef.current;if(canvas){canvas.addEventListener('wheel',handleCanvasWheel,{passive:false});}return()=>{if(canvas){canvas.removeEventListener('wheel',handleCanvasWheel);}};},[notes,st.currentTime,pixelsPerBeat,st.id,totalBeats,bpm]);React.useLayoutEffect(()=>{const canvas=canvasRef.current;if(!canvas)return;const ctx=canvas.getContext('2d');const dpr=window.devicePixelRatio||1;const h=128*NoteHeight;canvas.width=viewWidth*dpr;canvas.height=h*dpr;ctx.scale(dpr,dpr);// Draw background rows
|
||||
for(let pitch=0;pitch<128;pitch++){const y=(127-pitch)*NoteHeight;const isBlack=[1,3,6,8,10].includes(pitch%12);ctx.fillStyle=isBlack?'#1a1a1e':'#25252a';ctx.fillRect(0,y,viewWidth,NoteHeight);ctx.strokeStyle='#2d2d35';ctx.lineWidth=0.5;ctx.beginPath();ctx.moveTo(0,y+NoteHeight);ctx.lineTo(viewWidth,y+NoteHeight);ctx.stroke();}// Draw snap lines
|
||||
let snapBeats=0.25;if(snapVal==='1')snapBeats=4.0;else if(snapVal==='1/2')snapBeats=2.0;else if(snapVal==='1/4')snapBeats=1.0;else if(snapVal==='1/8')snapBeats=0.5;else if(snapVal==='1/16')snapBeats=0.25;else if(snapVal==='4')snapBeats=4.0;else if(snapVal==='1/32')snapBeats=0.125;for(let beat=0;beat<=viewBeats;beat+=snapBeats){const x=beat*pixelsPerBeat;if(x>drawWidth)break;const isBar=beat%timeSigNum===0;ctx.strokeStyle=isBar?'#444450':'#2d2d35';ctx.lineWidth=isBar?1.2:0.6;ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,h);ctx.stroke();}// Draw notes with velocity layer representation
|
||||
notes.forEach(note=>{const x=note.start_beat*pixelsPerBeat;const y=(127-note.pitch)*NoteHeight;const w=note.duration_beats*pixelsPerBeat;const isSelected=selectedNoteIds.includes(note.id);// Draw background of note
|
||||
|
||||
Reference in New Issue
Block a user