feat: added zoom-in/out feature for piano roll window.
This commit is contained in:
@@ -315,3 +315,31 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.piano-roll-zoom-popup {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
right: 0;
|
||||||
|
background-color: #2d2d2d;
|
||||||
|
border: 1px solid #444;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
z-index: 1500;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||||
|
white-space: nowrap;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.piano-roll-zoom-popup input[type="range"] {
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.piano-roll-zoom-value {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #e0e0e0;
|
||||||
|
min-width: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
@@ -50,6 +50,9 @@ const PianoRoll: React.FC<PianoRollProps> = ({
|
|||||||
const [spectrogramThresholdDb, setSpectrogramThresholdDb] = useState<number>(-25);
|
const [spectrogramThresholdDb, setSpectrogramThresholdDb] = useState<number>(-25);
|
||||||
const [spectrogramPower, setSpectrogramPower] = useState<number>(0.5);
|
const [spectrogramPower, setSpectrogramPower] = useState<number>(0.5);
|
||||||
|
|
||||||
|
// Piano roll zoom (1x–8x); updates --region-grid-beat-width CSS variable
|
||||||
|
const [pianoRollZoom, setPianoRollZoom] = useState<number>(1);
|
||||||
|
|
||||||
// Quantization state
|
// Quantization state
|
||||||
const [quantPosition, setQuantPosition] = useState<string>('1/8');
|
const [quantPosition, setQuantPosition] = useState<string>('1/8');
|
||||||
const [quantLength, setQuantLength] = useState<string>('1/8');
|
const [quantLength, setQuantLength] = useState<string>('1/8');
|
||||||
@@ -686,6 +689,17 @@ const PianoRoll: React.FC<PianoRollProps> = ({
|
|||||||
container.scrollLeft = clampedScrollLeft;
|
container.scrollLeft = clampedScrollLeft;
|
||||||
}, [playheadPosition, isPlaying, autoScrollEnabled]);
|
}, [playheadPosition, isPlaying, autoScrollEnabled]);
|
||||||
|
|
||||||
|
// Update --region-grid-beat-width when zoom changes; reset on unmount
|
||||||
|
useEffect(() => {
|
||||||
|
document.documentElement.style.setProperty('--region-grid-beat-width', `${40 * pianoRollZoom}px`);
|
||||||
|
if (triggerNoteUpdateRef.current) {
|
||||||
|
triggerNoteUpdateRef.current(prev => prev + 1);
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
document.documentElement.style.setProperty('--region-grid-beat-width', '40px');
|
||||||
|
};
|
||||||
|
}, [pianoRollZoom]);
|
||||||
|
|
||||||
// Scroll horizontally to the active region's starting bar
|
// Scroll horizontally to the active region's starting bar
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (pianoRollContentRef.current && activeRegion) {
|
if (pianoRollContentRef.current && activeRegion) {
|
||||||
@@ -927,6 +941,8 @@ const PianoRoll: React.FC<PianoRollProps> = ({
|
|||||||
onThresholdChange={setSpectrogramThresholdDb}
|
onThresholdChange={setSpectrogramThresholdDb}
|
||||||
power={spectrogramPower}
|
power={spectrogramPower}
|
||||||
onPowerChange={setSpectrogramPower}
|
onPowerChange={setSpectrogramPower}
|
||||||
|
zoom={pianoRollZoom}
|
||||||
|
onZoomChange={setPianoRollZoom}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PianoRollContent
|
<PianoRollContent
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ interface PianoRollToolbarProps {
|
|||||||
onThresholdChange?: (db: number) => void;
|
onThresholdChange?: (db: number) => void;
|
||||||
power?: number;
|
power?: number;
|
||||||
onPowerChange?: (power: number) => void;
|
onPowerChange?: (power: number) => void;
|
||||||
|
zoom: number;
|
||||||
|
onZoomChange: (value: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PianoRollToolbar: React.FC<PianoRollToolbarProps> = ({
|
const PianoRollToolbar: React.FC<PianoRollToolbarProps> = ({
|
||||||
@@ -49,11 +51,27 @@ const PianoRollToolbar: React.FC<PianoRollToolbarProps> = ({
|
|||||||
onThresholdChange,
|
onThresholdChange,
|
||||||
power = 0.5,
|
power = 0.5,
|
||||||
onPowerChange,
|
onPowerChange,
|
||||||
|
zoom,
|
||||||
|
onZoomChange,
|
||||||
}) => {
|
}) => {
|
||||||
const isSpectrogram = mode === 'spectrogram';
|
const isSpectrogram = mode === 'spectrogram';
|
||||||
const showMidiControls = mode !== 'spectrogram'; // midi-edit and hybrid
|
const showMidiControls = mode !== 'spectrogram'; // midi-edit and hybrid
|
||||||
const showSpecControls = mode === 'spectrogram' || mode === 'hybrid';
|
const showSpecControls = mode === 'spectrogram' || mode === 'hybrid';
|
||||||
|
|
||||||
|
const [showZoomSlider, setShowZoomSlider] = React.useState(false);
|
||||||
|
const zoomSliderRef = React.useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!showZoomSlider) return;
|
||||||
|
const handleClickOutside = (e: MouseEvent) => {
|
||||||
|
if (zoomSliderRef.current && !zoomSliderRef.current.contains(e.target as Node)) {
|
||||||
|
setShowZoomSlider(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener('mousedown', handleClickOutside);
|
||||||
|
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||||
|
}, [showZoomSlider]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="piano-roll-toolbar">
|
<div className="piano-roll-toolbar">
|
||||||
{showMidiControls && (
|
{showMidiControls && (
|
||||||
@@ -148,6 +166,29 @@ const PianoRollToolbar: React.FC<PianoRollToolbarProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<div className="quant-dropdown-container" ref={zoomSliderRef}>
|
||||||
|
<button
|
||||||
|
className="quant-button"
|
||||||
|
onClick={() => setShowZoomSlider(!showZoomSlider)}
|
||||||
|
title="Zoom"
|
||||||
|
>
|
||||||
|
{zoom}x
|
||||||
|
</button>
|
||||||
|
{showZoomSlider && (
|
||||||
|
<div className="piano-roll-zoom-popup">
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min="1"
|
||||||
|
max="8"
|
||||||
|
step="1"
|
||||||
|
value={zoom}
|
||||||
|
onChange={(e) => onZoomChange(parseInt(e.target.value))}
|
||||||
|
/>
|
||||||
|
<span className="piano-roll-zoom-value">{zoom}x</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user