|
9 | 9 | } from "react"; |
10 | 10 | import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants"; |
11 | 11 | import { useEditor } from "@/hooks/use-editor"; |
| 12 | +import { zoomToSlider } from "@/lib/timeline/zoom-utils"; |
12 | 13 |
|
13 | 14 | interface UseTimelineZoomProps { |
14 | 15 | containerRef: RefObject<HTMLDivElement | null>; |
@@ -122,18 +123,47 @@ export function useTimelineZoom({ |
122 | 123 | if (previousZoom === zoomLevel) return; |
123 | 124 |
|
124 | 125 | const scrollElement = tracksScrollRef.current; |
125 | | - if (scrollElement) { |
126 | | - editor.project.setTimelineViewState({ |
127 | | - viewState: { |
128 | | - zoomLevel, |
129 | | - scrollLeft: scrollElement.scrollLeft, |
130 | | - playheadTime: editor.playback.getCurrentTime(), |
131 | | - }, |
132 | | - }); |
| 126 | + if (!scrollElement) { |
| 127 | + previousZoomRef.current = zoomLevel; |
| 128 | + return; |
| 129 | + } |
| 130 | + |
| 131 | + const currentScrollLeft = scrollElement.scrollLeft; |
| 132 | + const playheadTime = editor.playback.getCurrentTime(); |
| 133 | + const sliderPercent = zoomToSlider({ zoomLevel, minZoom }); |
| 134 | + |
| 135 | + if (sliderPercent >= TIMELINE_CONSTANTS.ZOOM_ANCHOR_PLAYHEAD_THRESHOLD) { |
| 136 | + const playheadPixelsBefore = |
| 137 | + playheadTime * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * previousZoom; |
| 138 | + const playheadPixelsAfter = |
| 139 | + playheadTime * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel; |
| 140 | + |
| 141 | + const viewportOffset = playheadPixelsBefore - currentScrollLeft; |
| 142 | + const newScrollLeft = playheadPixelsAfter - viewportOffset; |
| 143 | + |
| 144 | + const maxScrollLeft = |
| 145 | + scrollElement.scrollWidth - scrollElement.clientWidth; |
| 146 | + const clampedScrollLeft = Math.max( |
| 147 | + 0, |
| 148 | + Math.min(maxScrollLeft, newScrollLeft), |
| 149 | + ); |
| 150 | + |
| 151 | + scrollElement.scrollLeft = clampedScrollLeft; |
| 152 | + if (rulerScrollRef.current) { |
| 153 | + rulerScrollRef.current.scrollLeft = clampedScrollLeft; |
| 154 | + } |
133 | 155 | } |
134 | 156 |
|
135 | 157 | previousZoomRef.current = zoomLevel; |
136 | | - }, [zoomLevel, editor, tracksScrollRef]); |
| 158 | + |
| 159 | + editor.project.setTimelineViewState({ |
| 160 | + viewState: { |
| 161 | + zoomLevel, |
| 162 | + scrollLeft: scrollElement.scrollLeft, |
| 163 | + playheadTime, |
| 164 | + }, |
| 165 | + }); |
| 166 | + }, [zoomLevel, editor, tracksScrollRef, rulerScrollRef, minZoom]); |
137 | 167 |
|
138 | 168 | // biome-ignore lint/correctness/useExhaustiveDependencies: tracksScrollRef is a stable ref |
139 | 169 | const saveScrollPosition = useCallback(() => { |
|
0 commit comments