1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
import {
type WheelEvent as ReactWheelEvent,
type RefObject,
useEffect,
useLayoutEffect,
useReducer,
useState,
} from "react";
import { useEditor } from "@/editor/use-editor";
import { useCommittedRef } from "@/hooks/use-committed-ref";
import { TIMELINE_ZOOM_MIN } from "@/timeline/scale";
import {
ZoomController,
type ZoomConfig,
} from "@/timeline/controllers/zoom-controller";
import type { MediaTime } from "@/wasm";
interface UseTimelineZoomProps {
containerRef: RefObject<HTMLDivElement | null>;
minZoom?: number;
initialZoom?: number;
initialScrollLeft?: number;
initialPlayheadTime?: MediaTime;
tracksScrollRef: RefObject<HTMLDivElement | null>;
rulerScrollRef: RefObject<HTMLDivElement | null>;
}
interface UseTimelineZoomReturn {
zoomLevel: number;
setZoomLevel: (zoomLevel: number | ((prev: number) => number)) => void;
handleWheel: (event: ReactWheelEvent) => void;
saveScrollPosition: () => void;
}
export function useTimelineZoom({
containerRef,
minZoom = TIMELINE_ZOOM_MIN,
initialZoom,
initialScrollLeft,
initialPlayheadTime,
tracksScrollRef,
rulerScrollRef,
}: UseTimelineZoomProps): UseTimelineZoomReturn {
const editor = useEditor();
const config: ZoomConfig = {
minZoom,
getContainerEl: () => containerRef.current,
getTracksScrollEl: () => tracksScrollRef.current,
getRulerScrollEl: () => rulerScrollRef.current,
getCurrentPlayheadTime: () => editor.playback.getCurrentTime(),
seek: (time) => editor.playback.seek({ time }),
setTimelineViewState: ({ zoomLevel, scrollLeft, playheadTime }) =>
editor.project.setTimelineViewState({
viewState: {
zoomLevel,
scrollLeft,
playheadTime,
},
}),
};
const configRef = useCommittedRef(config);
const [controller] = useState(
() => new ZoomController({ configRef, initialZoom }),
);
const zoomLevel = controller.zoomLevel;
const [, rerender] = useReducer((n: number) => n + 1, 0);
useEffect(() => controller.subscribe(rerender), [controller]);
useEffect(() => {
controller.reconcileInitialAndMinZoom({ minZoom, initialZoom });
}, [controller, minZoom, initialZoom]);
useLayoutEffect(() => {
controller.applyZoomLayout(zoomLevel);
}, [controller, zoomLevel]);
useEffect(() => {
return controller.restoreInitialScrollIfNeeded(initialScrollLeft);
}, [controller, initialScrollLeft]);
useEffect(() => {
controller.restoreInitialPlayheadIfNeeded(initialPlayheadTime);
}, [controller, initialPlayheadTime]);
useEffect(() => controller.bindPreventBrowserZoom(), [controller]);
useEffect(() => () => controller.destroy(), [controller]);
return {
zoomLevel,
setZoomLevel: controller.setZoomLevel,
handleWheel: controller.handleWheel,
saveScrollPosition: controller.saveScrollPosition,
};
}
|