feat: let playhead automatically centered
This commit is contained in:
@@ -31,6 +31,8 @@ const MainContent: React.FC<MainContentProps> = ({
|
||||
updateTrackProperties,
|
||||
timeSignature,
|
||||
setPlayheadPosition,
|
||||
playheadPosition,
|
||||
isPlaying,
|
||||
clearAllSelections,
|
||||
setSelectedTrack,
|
||||
showPianoRoll,
|
||||
@@ -80,6 +82,12 @@ const MainContent: React.FC<MainContentProps> = ({
|
||||
// Refs to track pending updates for verification
|
||||
const pendingUpdates = useRef<Map<string, { trackId: string, regionId: string, startBeat: number, length: number }>>(new Map());
|
||||
|
||||
// Refs for auto-scroll during playback
|
||||
const mainContentRef = useRef<HTMLDivElement | null>(null);
|
||||
const userManuallyScrolledRef = useRef(false);
|
||||
const expectedScrollLeftRef = useRef<number>(-1);
|
||||
const isPlayingRef = useRef(false);
|
||||
|
||||
// Refs for bar numbers and loop range drag functionality
|
||||
const barNumbersRef = useRef<HTMLDivElement | null>(null);
|
||||
const isLoopDraggingRef = useRef(false);
|
||||
@@ -87,6 +95,56 @@ const MainContent: React.FC<MainContentProps> = ({
|
||||
const loopDragStartXRef = useRef<number | null>(null);
|
||||
const loopDragOriginalSettingsRef = useRef<{ isLooping: boolean; loopingRange: [number, number] } | null>(null);
|
||||
|
||||
// Sync isPlayingRef and reset manual-scroll flag when playback starts
|
||||
useEffect(() => {
|
||||
if (isPlaying && !isPlayingRef.current) {
|
||||
userManuallyScrolledRef.current = false;
|
||||
}
|
||||
isPlayingRef.current = isPlaying;
|
||||
}, [isPlaying]);
|
||||
|
||||
// Detect manual horizontal scroll during playback
|
||||
useEffect(() => {
|
||||
const container = mainContentRef.current;
|
||||
if (!container) return;
|
||||
|
||||
const handleScroll = () => {
|
||||
if (!isPlayingRef.current) return;
|
||||
// If scrollLeft matches what we programmatically set (within 1px), it's our scroll — ignore
|
||||
if (Math.abs(container.scrollLeft - expectedScrollLeftRef.current) < 1) return;
|
||||
userManuallyScrolledRef.current = true;
|
||||
};
|
||||
|
||||
container.addEventListener('scroll', handleScroll);
|
||||
return () => container.removeEventListener('scroll', handleScroll);
|
||||
}, []);
|
||||
|
||||
// Auto-scroll to keep playhead centered during playback
|
||||
useEffect(() => {
|
||||
if (!isPlaying || userManuallyScrolledRef.current) return;
|
||||
|
||||
const container = mainContentRef.current;
|
||||
if (!container) return;
|
||||
|
||||
const beatsPerBar = timeSignature.numerator;
|
||||
const barPosition = playheadPosition / beatsPerBar;
|
||||
const barWidth = parseInt(
|
||||
getComputedStyle(document.documentElement).getPropertyValue('--track-grid-bar-width')
|
||||
) || 40;
|
||||
const playheadPixel = barPosition * barWidth;
|
||||
|
||||
// Center the playhead in the visible grid area (excluding the 200px sticky info panel)
|
||||
const infoWidth = 200;
|
||||
const targetScrollLeft = playheadPixel - (container.clientWidth - infoWidth) / 2;
|
||||
const clampedScrollLeft = Math.max(
|
||||
0,
|
||||
Math.min(targetScrollLeft, container.scrollWidth - container.clientWidth)
|
||||
);
|
||||
|
||||
expectedScrollLeftRef.current = clampedScrollLeft;
|
||||
container.scrollLeft = clampedScrollLeft;
|
||||
}, [playheadPosition, isPlaying, timeSignature]);
|
||||
|
||||
// Effect to verify track updates
|
||||
useEffect(() => {
|
||||
// Check for pending updates
|
||||
@@ -722,7 +780,7 @@ const MainContent: React.FC<MainContentProps> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`main-content${showInstrumentSelection ? ' has-left-instrument' : ''}`}>
|
||||
<div className={`main-content${showInstrumentSelection ? ' has-left-instrument' : ''}`} ref={mainContentRef}>
|
||||
<div className="main-content-wrapper">
|
||||
{/* Top-left spacer */}
|
||||
<div className="top-left-spacer">
|
||||
|
||||
Reference in New Issue
Block a user