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_MIN: 0.1,
|
||||||
ZOOM_MAX: 100,
|
ZOOM_MAX: 100,
|
||||||
ZOOM_BUTTON_FACTOR: 1.7,
|
ZOOM_BUTTON_FACTOR: 1.7,
|
||||||
|
ZOOM_ANCHOR_PLAYHEAD_THRESHOLD: 0.15,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const DEFAULT_TIMELINE_VIEW_STATE: TTimelineViewState = {
|
export const DEFAULT_TIMELINE_VIEW_STATE: TTimelineViewState = {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import {
|
||||||
} from "react";
|
} from "react";
|
||||||
import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants";
|
import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants";
|
||||||
import { useEditor } from "@/hooks/use-editor";
|
import { useEditor } from "@/hooks/use-editor";
|
||||||
|
import { zoomToSlider } from "@/lib/timeline/zoom-utils";
|
||||||
|
|
||||||
interface UseTimelineZoomProps {
|
interface UseTimelineZoomProps {
|
||||||
containerRef: RefObject<HTMLDivElement | null>;
|
containerRef: RefObject<HTMLDivElement | null>;
|
||||||
|
|
@ -122,18 +123,47 @@ export function useTimelineZoom({
|
||||||
if (previousZoom === zoomLevel) return;
|
if (previousZoom === zoomLevel) return;
|
||||||
|
|
||||||
const scrollElement = tracksScrollRef.current;
|
const scrollElement = tracksScrollRef.current;
|
||||||
if (scrollElement) {
|
if (!scrollElement) {
|
||||||
editor.project.setTimelineViewState({
|
previousZoomRef.current = zoomLevel;
|
||||||
viewState: {
|
return;
|
||||||
zoomLevel,
|
}
|
||||||
scrollLeft: scrollElement.scrollLeft,
|
|
||||||
playheadTime: editor.playback.getCurrentTime(),
|
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;
|
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
|
// biome-ignore lint/correctness/useExhaustiveDependencies: tracksScrollRef is a stable ref
|
||||||
const saveScrollPosition = useCallback(() => {
|
const saveScrollPosition = useCallback(() => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue