fix: brush scroll speed + CC snap + non-selected flicker
This commit is contained in:
+15
-16
@@ -5250,7 +5250,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
brushAutoScrollRef.current = {
|
brushAutoScrollRef.current = {
|
||||||
direction: 'up',
|
direction: 'up',
|
||||||
id: setInterval(() => {
|
id: setInterval(() => {
|
||||||
if (gridScrollRef.current) gridScrollRef.current.scrollTop = Math.max(0, gridScrollRef.current.scrollTop - NoteHeight * 2);
|
if (gridScrollRef.current) gridScrollRef.current.scrollTop = Math.max(0, gridScrollRef.current.scrollTop - Math.max(1, Math.floor(NoteHeight * 0.5)));
|
||||||
}, 30)
|
}, 30)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -5260,7 +5260,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
brushAutoScrollRef.current = {
|
brushAutoScrollRef.current = {
|
||||||
direction: 'down',
|
direction: 'down',
|
||||||
id: setInterval(() => {
|
id: setInterval(() => {
|
||||||
if (gridScrollRef.current) gridScrollRef.current.scrollTop = Math.min(gridScrollRef.current.scrollHeight - gridScrollRef.current.clientHeight, gridScrollRef.current.scrollTop + NoteHeight * 2);
|
if (gridScrollRef.current) gridScrollRef.current.scrollTop = Math.min(gridScrollRef.current.scrollHeight - gridScrollRef.current.clientHeight, gridScrollRef.current.scrollTop + Math.max(1, Math.floor(NoteHeight * 0.5)));
|
||||||
}, 30)
|
}, 30)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -5376,12 +5376,13 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
const h = rect.height;
|
const h = rect.height;
|
||||||
const beat = x / pixelsPerBeat;
|
const beat = x / pixelsPerBeat;
|
||||||
|
|
||||||
let noteIdx = notes.findIndex(n => beat >= n.start_beat && beat <= n.start_beat + n.duration_beats);
|
const snappedBeat = getSnapBeat(beat, snapVal);
|
||||||
|
let noteIdx = notes.findIndex(n => snappedBeat >= n.start_beat && snappedBeat <= n.start_beat + n.duration_beats);
|
||||||
if (noteIdx === -1) {
|
if (noteIdx === -1) {
|
||||||
let minDistance = Infinity;
|
let minDistance = Infinity;
|
||||||
notes.forEach((n, idx) => {
|
notes.forEach((n, idx) => {
|
||||||
const center = n.start_beat + n.duration_beats / 2;
|
const center = n.start_beat + n.duration_beats / 2;
|
||||||
const dist = Math.abs(center - beat);
|
const dist = Math.abs(center - snappedBeat);
|
||||||
if (dist < minDistance) {
|
if (dist < minDistance) {
|
||||||
minDistance = dist;
|
minDistance = dist;
|
||||||
noteIdx = idx;
|
noteIdx = idx;
|
||||||
@@ -5390,14 +5391,6 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
}
|
}
|
||||||
|
|
||||||
const val = Math.max(0, Math.min(1, (h - y) / h));
|
const val = Math.max(0, Math.min(1, (h - y) / h));
|
||||||
const paintNote = (idx, v) => {
|
|
||||||
if (idx === -1) return;
|
|
||||||
setNotes(prev => prev.map((n, i) => {
|
|
||||||
if (i !== idx) return n;
|
|
||||||
if (ccMode === 'pan') { return { ...n, pan: (v - 0.5) * 2.0 }; }
|
|
||||||
return { ...n, velocity: v };
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
if (e.ctrlKey) {
|
if (e.ctrlKey) {
|
||||||
if (selectedNoteIds.length > 0) {
|
if (selectedNoteIds.length > 0) {
|
||||||
@@ -5421,13 +5414,18 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
ccDragRef.current = { active: true, lastBeat: beat, selectedMode: true, lastPainted: [] };
|
ccDragRef.current = { active: true, lastBeat: beat, selectedMode: true, lastPainted: [] };
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (noteIdx !== -1) paintNote(noteIdx, val);
|
|
||||||
ccDragRef.current = { active: true, lastBeat: beat, lastPainted: noteIdx !== -1 ? [noteIdx] : [] };
|
ccDragRef.current = { active: true, lastBeat: beat, lastPainted: noteIdx !== -1 ? [noteIdx] : [] };
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (noteIdx !== -1) paintNote(noteIdx, val);
|
if (noteIdx !== -1) {
|
||||||
|
setNotes(prev => prev.map((n, i) => {
|
||||||
|
if (i !== noteIdx) return n;
|
||||||
|
if (ccMode === 'pan') return { ...n, pan: (val - 0.5) * 2.0 };
|
||||||
|
return { ...n, velocity: val };
|
||||||
|
}));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCCMouseMove = (e) => {
|
const handleCCMouseMove = (e) => {
|
||||||
@@ -5468,7 +5466,8 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const candidateIdx = notes.findIndex(n => beat >= n.start_beat && beat <= n.start_beat + n.duration_beats);
|
const snappedBeat = getSnapBeat(beat, snapVal);
|
||||||
|
const candidateIdx = notes.findIndex(n => snappedBeat >= n.start_beat && snappedBeat <= n.start_beat + n.duration_beats);
|
||||||
|
|
||||||
if (candidateIdx !== -1 && !painted.includes(candidateIdx)) {
|
if (candidateIdx !== -1 && !painted.includes(candidateIdx)) {
|
||||||
setNotes(prev => prev.map((n, i) => {
|
setNotes(prev => prev.map((n, i) => {
|
||||||
@@ -5482,7 +5481,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
let minDist = Infinity;
|
let minDist = Infinity;
|
||||||
notes.forEach((n, idx) => {
|
notes.forEach((n, idx) => {
|
||||||
const center = n.start_beat + n.duration_beats / 2;
|
const center = n.start_beat + n.duration_beats / 2;
|
||||||
const d = Math.abs(center - beat);
|
const d = Math.abs(center - snappedBeat);
|
||||||
if (d < minDist) { minDist = d; nearest = idx; }
|
if (d < minDist) { minDist = d; nearest = idx; }
|
||||||
});
|
});
|
||||||
if (nearest !== -1 && !painted.includes(nearest)) {
|
if (nearest !== -1 && !painted.includes(nearest)) {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -28,6 +28,11 @@
|
|||||||
- **Ghi chú/Test (nếu có):** `npm run build` pass. Thay `lastPainted` trong selectedMode từ array all indices thành array single-node index.
|
- **Ghi chú/Test (nếu có):** `npm run build` pass. Thay `lastPainted` trong selectedMode từ array all indices thành array single-node index.
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### [2026-07-26 10:20] Task: Fix brush scroll speed + CC snap + non-selected flicker
|
||||||
|
- **Tóm tắt thay đổi:** (1) Giảm auto-scroll step từ `NoteHeight * 2` xuống `max(1, floor(NoteHeight * 0.5))` — cuộn chậm hơn để draw note đúng pitch. (2) CC lane `handleCCMouseDown` và `handleCCMouseMove` dùng `getSnapBeat(beat, snapVal)` khi tìm note index — snap đúng grid. (3) Non-selected CC flicker: xóa `paintNote` helper, inline `setNotes` trực tiếp trong mousedown, không gọi setNotes riêng lẻ gây re-render dư thừa.
|
||||||
|
- **Các file ảnh hưởng:** `app/static/js/app.jsx`
|
||||||
|
- **Ghi chú/Test (nếu có):** `npm run build` pass.
|
||||||
|
|
||||||
### [2026-07-25 07:25] Task: Fix auto-scroll + maxDuration tab isolation + 1-bar margin
|
### [2026-07-25 07:25] Task: Fix auto-scroll + maxDuration tab isolation + 1-bar margin
|
||||||
- **Tóm tắt thay đổi:** (1) `maxDuration` dùng `activeTracks` + 4-bar buffer. (2) Cách ly MAIN vs SECTION-TAB. (3) Clip/section/MIDI drag/stretch/resize clamp 1-bar from right. (4) Clip drag + stretched clip dùng `updateActiveTracks`. (5) Stretched clip handler thêm auto-scroll.
|
- **Tóm tắt thay đổi:** (1) `maxDuration` dùng `activeTracks` + 4-bar buffer. (2) Cách ly MAIN vs SECTION-TAB. (3) Clip/section/MIDI drag/stretch/resize clamp 1-bar from right. (4) Clip drag + stretched clip dùng `updateActiveTracks`. (5) Stretched clip handler thêm auto-scroll.
|
||||||
- **Các file ảnh hưởng:** `app/static/js/app.jsx`
|
- **Các file ảnh hưởng:** `app/static/js/app.jsx`
|
||||||
|
|||||||
Reference in New Issue
Block a user