keep handlebar centered when zooming

This commit is contained in:
Josh Hawkins 2024-12-03 10:54:02 -06:00
parent a9a8db784c
commit 63e21763ec
4 changed files with 67 additions and 11 deletions

View File

@ -116,14 +116,31 @@ export function EventReviewTimeline({
]); ]);
const scrollToSegment = useCallback( const scrollToSegment = useCallback(
(segmentTime: number, ifNeeded?: boolean) => { (segmentTime: number, ifNeeded?: boolean, behavior?: ScrollBehavior) => {
if (virtualizedSegmentsRef.current) { if (virtualizedSegmentsRef.current) {
virtualizedSegmentsRef.current.scrollToSegment(segmentTime, ifNeeded); virtualizedSegmentsRef.current.scrollToSegment(
segmentTime,
ifNeeded,
behavior,
);
} }
}, },
[], [],
); );
// keep handlebar centered when zooming
useEffect(() => {
setTimeout(() => {
scrollToSegment(
alignStartDateToTimeline(handlebarTime ?? timelineStart),
true,
"auto",
);
}, 0);
// we only want to scroll when zooming level changes
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [segmentDuration]);
return ( return (
<ReviewTimeline <ReviewTimeline
timelineRef={selectedTimelineRef} timelineRef={selectedTimelineRef}

View File

@ -1,4 +1,10 @@
import React, { useCallback, useMemo, useRef, RefObject } from "react"; import React, {
useCallback,
useMemo,
useRef,
RefObject,
useEffect,
} from "react";
import { useTimelineUtils } from "@/hooks/use-timeline-utils"; import { useTimelineUtils } from "@/hooks/use-timeline-utils";
import { MotionData, ReviewSegment } from "@/types/review"; import { MotionData, ReviewSegment } from "@/types/review";
import ReviewTimeline from "./ReviewTimeline"; import ReviewTimeline from "./ReviewTimeline";
@ -120,14 +126,31 @@ export function MotionReviewTimeline({
]); ]);
const scrollToSegment = useCallback( const scrollToSegment = useCallback(
(segmentTime: number, ifNeeded?: boolean) => { (segmentTime: number, ifNeeded?: boolean, behavior?: ScrollBehavior) => {
if (virtualizedSegmentsRef.current) { if (virtualizedSegmentsRef.current) {
virtualizedSegmentsRef.current.scrollToSegment(segmentTime, ifNeeded); virtualizedSegmentsRef.current.scrollToSegment(
segmentTime,
ifNeeded,
behavior,
);
} }
}, },
[], [],
); );
// keep handlebar centered when zooming
useEffect(() => {
setTimeout(() => {
scrollToSegment(
alignStartDateToTimeline(handlebarTime ?? timelineStart),
true,
"auto",
);
}, 0);
// we only want to scroll when zooming level changes
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [segmentDuration]);
return ( return (
<ReviewTimeline <ReviewTimeline
timelineRef={selectedTimelineRef} timelineRef={selectedTimelineRef}

View File

@ -26,7 +26,11 @@ interface VirtualizedEventSegmentsProps {
} }
export interface VirtualizedEventSegmentsRef { export interface VirtualizedEventSegmentsRef {
scrollToSegment: (segmentTime: number, ifNeeded?: boolean) => void; scrollToSegment: (
segmentTime: number,
ifNeeded?: boolean,
behavior?: ScrollBehavior,
) => void;
} }
const SEGMENT_HEIGHT = 8; const SEGMENT_HEIGHT = 8;
@ -93,7 +97,11 @@ export const VirtualizedEventSegments = forwardRef<
}, [updateVisibleRange, timelineRef]); }, [updateVisibleRange, timelineRef]);
const scrollToSegment = useCallback( const scrollToSegment = useCallback(
(segmentTime: number, ifNeeded: boolean = true) => { (
segmentTime: number,
ifNeeded: boolean = true,
behavior: ScrollBehavior = "smooth",
) => {
const alignedSegmentTime = alignStartDateToTimeline(segmentTime); const alignedSegmentTime = alignStartDateToTimeline(segmentTime);
const segmentIndex = segments.findIndex( const segmentIndex = segments.findIndex(
(time) => time === alignedSegmentTime, (time) => time === alignedSegmentTime,
@ -115,7 +123,7 @@ export const VirtualizedEventSegments = forwardRef<
if (!ifNeeded || !isVisible) { if (!ifNeeded || !isVisible) {
timelineRef.current.scrollTo({ timelineRef.current.scrollTo({
top: Math.max(0, centeredScrollTop), top: Math.max(0, centeredScrollTop),
behavior: "smooth", behavior: behavior,
}); });
} }
updateVisibleRange(); updateVisibleRange();

View File

@ -27,7 +27,11 @@ type VirtualizedMotionSegmentsProps = {
}; };
export interface VirtualizedMotionSegmentsRef { export interface VirtualizedMotionSegmentsRef {
scrollToSegment: (segmentTime: number, ifNeeded?: boolean) => void; scrollToSegment: (
segmentTime: number,
ifNeeded?: boolean,
behavior?: ScrollBehavior,
) => void;
} }
const SEGMENT_HEIGHT = 8; const SEGMENT_HEIGHT = 8;
@ -93,7 +97,11 @@ export const VirtualizedMotionSegments = forwardRef<
}, [updateVisibleRange, timelineRef]); }, [updateVisibleRange, timelineRef]);
const scrollToSegment = useCallback( const scrollToSegment = useCallback(
(segmentTime: number, ifNeeded: boolean = true) => { (
segmentTime: number,
ifNeeded: boolean = true,
behavior: ScrollBehavior = "smooth",
) => {
const segmentIndex = segments.findIndex((time) => time === segmentTime); const segmentIndex = segments.findIndex((time) => time === segmentTime);
if ( if (
segmentIndex !== -1 && segmentIndex !== -1 &&
@ -112,7 +120,7 @@ export const VirtualizedMotionSegments = forwardRef<
if (!ifNeeded || !isVisible) { if (!ifNeeded || !isVisible) {
timelineRef.current.scrollTo({ timelineRef.current.scrollTo({
top: Math.max(0, centeredScrollTop), top: Math.max(0, centeredScrollTop),
behavior: "smooth", behavior: behavior,
}); });
} }
updateVisibleRange(); updateVisibleRange();