feat: timeline zoom anchoring
This commit is contained in:
parent
1aec9c5cf5
commit
5120e2efa6
|
|
@ -39,6 +39,7 @@ export const TIMELINE_CONSTANTS = {
|
|||
ZOOM_MIN: 0.1,
|
||||
ZOOM_MAX: 100,
|
||||
ZOOM_BUTTON_FACTOR: 1.7,
|
||||
ZOOM_ANCHOR_PLAYHEAD_THRESHOLD: 0.15,
|
||||
} as const;
|
||||
|
||||
export const DEFAULT_TIMELINE_VIEW_STATE: TTimelineViewState = {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
} from "react";
|
||||
import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants";
|
||||
import { useEditor } from "@/hooks/use-editor";
|
||||
import { zoomToSlider } from "@/lib/timeline/zoom-utils";
|
||||
|
||||
interface UseTimelineZoomProps {
|
||||
containerRef: RefObject<HTMLDivElement | null>;
|
||||
|
|
@ -122,18 +123,47 @@ export function useTimelineZoom({
|
|||
if (previousZoom === zoomLevel) return;
|
||||
|
||||
const scrollElement = tracksScrollRef.current;
|
||||
if (scrollElement) {
|
||||
editor.project.setTimelineViewState({
|
||||
viewState: {
|
||||
zoomLevel,
|
||||
scrollLeft: scrollElement.scrollLeft,
|
||||
playheadTime: editor.playback.getCurrentTime(),
|
||||
},
|
||||
});
|
||||
if (!scrollElement) {
|
||||
previousZoomRef.current = zoomLevel;
|
||||
return;
|
||||
}
|
||||
|
||||
const currentScrollLeft = scrollElement.scrollLeft;
|
||||
const playheadTime = editor.playback.getCurrentTime();
|
||||
const sliderPercent = zoomToSlider({ zoomLevel, minZoom });
|
||||
|
||||
if (sliderPercent >= TIMELINE_CONSTANTS.ZOOM_ANCHOR_PLAYHEAD_THRESHOLD) {
|
||||
const playheadPixelsBefore =
|
||||
playheadTime * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * previousZoom;
|
||||
const playheadPixelsAfter =
|
||||
playheadTime * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel;
|
||||
|
||||
const viewportOffset = playheadPixelsBefore - currentScrollLeft;
|
||||
const newScrollLeft = playheadPixelsAfter - viewportOffset;
|
||||
|
||||
const maxScrollLeft =
|
||||
scrollElement.scrollWidth - scrollElement.clientWidth;
|
||||
const clampedScrollLeft = Math.max(
|
||||
0,
|
||||
Math.min(maxScrollLeft, newScrollLeft),
|
||||
);
|
||||
|
||||
scrollElement.scrollLeft = clampedScrollLeft;
|
||||
if (rulerScrollRef.current) {
|
||||
rulerScrollRef.current.scrollLeft = clampedScrollLeft;
|
||||
}
|
||||
}
|
||||
|
||||
previousZoomRef.current = zoomLevel;
|
||||
}, [zoomLevel, editor, tracksScrollRef]);
|
||||
|
||||
editor.project.setTimelineViewState({
|
||||
viewState: {
|
||||
zoomLevel,
|
||||
scrollLeft: scrollElement.scrollLeft,
|
||||
playheadTime,
|
||||
},
|
||||
});
|
||||
}, [zoomLevel, editor, tracksScrollRef, rulerScrollRef, minZoom]);
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: tracksScrollRef is a stable ref
|
||||
const saveScrollPosition = useCallback(() => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue