only calculate motion data for visible motion segments

This commit is contained in:
Josh Hawkins 2024-12-03 08:57:49 -06:00
parent 3ecf429927
commit a4d91a1e71
2 changed files with 82 additions and 75 deletions

View File

@ -83,52 +83,17 @@ export function MotionReviewTimeline({
motion_events, motion_events,
); );
// Generate segment times for the timeline
const segmentTimes = useMemo(() => { const segmentTimes = useMemo(() => {
const segments = []; const segments = [];
let segmentTime = timelineStartAligned; let segmentTime = timelineStartAligned;
while (segmentTime >= timelineStartAligned - timelineDuration) { for (let i = 0; i < Math.ceil(timelineDuration / segmentDuration); i++) {
const motionStart = segmentTime;
const motionEnd = motionStart + segmentDuration;
const firstHalfMotionValue = getMotionSegmentValue(motionStart);
const secondHalfMotionValue = getMotionSegmentValue(
motionStart + segmentDuration / 2,
);
const segmentMotion =
firstHalfMotionValue > 0 || secondHalfMotionValue > 0;
const overlappingReviewItems = events.some(
(item) =>
(item.start_time >= motionStart && item.start_time < motionEnd) ||
((item.end_time ?? timelineStart) > motionStart &&
(item.end_time ?? timelineStart) <= motionEnd) ||
(item.start_time <= motionStart &&
(item.end_time ?? timelineStart) >= motionEnd),
);
if ((!segmentMotion || overlappingReviewItems) && motionOnly) {
// exclude segment if necessary when in motion only mode
segmentTime -= segmentDuration;
continue;
}
segments.push(segmentTime); segments.push(segmentTime);
segmentTime -= segmentDuration; segmentTime -= segmentDuration;
} }
return segments; return segments;
// we know that these deps are correct }, [timelineStartAligned, segmentDuration, timelineDuration]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
segmentDuration,
timelineStartAligned,
timelineDuration,
events,
getMotionSegmentValue,
motion_events,
motionOnly,
]);
const scrollToSegment = useCallback( const scrollToSegment = useCallback(
(segmentTime: number, ifNeeded?: boolean) => { (segmentTime: number, ifNeeded?: boolean) => {

View File

@ -9,7 +9,7 @@ import React, {
import MotionSegment from "./MotionSegment"; import MotionSegment from "./MotionSegment";
import { ReviewSegment, MotionData } from "@/types/review"; import { ReviewSegment, MotionData } from "@/types/review";
interface VirtualizedMotionSegmentsProps { type VirtualizedMotionSegmentsProps = {
timelineRef: React.RefObject<HTMLDivElement>; timelineRef: React.RefObject<HTMLDivElement>;
segments: number[]; segments: number[];
events: ReviewSegment[]; events: ReviewSegment[];
@ -24,7 +24,7 @@ interface VirtualizedMotionSegmentsProps {
dense: boolean; dense: boolean;
motionOnly: boolean; motionOnly: boolean;
getMotionSegmentValue: (timestamp: number) => number; getMotionSegmentValue: (timestamp: number) => number;
} };
export interface VirtualizedMotionSegmentsRef { export interface VirtualizedMotionSegmentsRef {
scrollToSegment: (segmentTime: number, ifNeeded?: boolean) => void; scrollToSegment: (segmentTime: number, ifNeeded?: boolean) => void;
@ -125,6 +125,75 @@ export const VirtualizedMotionSegments = forwardRef<
scrollToSegment, scrollToSegment,
})); }));
const renderSegment = useCallback(
(segmentTime: number, index: number) => {
const motionStart = segmentTime;
const motionEnd = motionStart + segmentDuration;
const firstHalfMotionValue = getMotionSegmentValue(motionStart);
const secondHalfMotionValue = getMotionSegmentValue(
motionStart + segmentDuration / 2,
);
const segmentMotion =
firstHalfMotionValue > 0 || secondHalfMotionValue > 0;
const overlappingReviewItems = events.some(
(item) =>
(item.start_time >= motionStart && item.start_time < motionEnd) ||
((item.end_time ?? segmentTime) > motionStart &&
(item.end_time ?? segmentTime) <= motionEnd) ||
(item.start_time <= motionStart &&
(item.end_time ?? segmentTime) >= motionEnd),
);
if ((!segmentMotion || overlappingReviewItems) && motionOnly) {
return null; // Skip rendering this segment in motion only mode
}
return (
<div
key={`${segmentTime}_${segmentDuration}`}
style={{
position: "absolute",
top: `${(visibleRange.start + index) * SEGMENT_HEIGHT}px`,
height: `${SEGMENT_HEIGHT}px`,
width: "100%",
}}
>
<MotionSegment
events={events}
firstHalfMotionValue={firstHalfMotionValue}
secondHalfMotionValue={secondHalfMotionValue}
segmentDuration={segmentDuration}
segmentTime={segmentTime}
timestampSpread={timestampSpread}
motionOnly={motionOnly}
showMinimap={showMinimap}
minimapStartTime={minimapStartTime}
minimapEndTime={minimapEndTime}
setHandlebarTime={setHandlebarTime}
scrollToSegment={scrollToSegment}
dense={dense}
/>
</div>
);
},
[
events,
getMotionSegmentValue,
motionOnly,
segmentDuration,
showMinimap,
minimapStartTime,
minimapEndTime,
setHandlebarTime,
scrollToSegment,
dense,
timestampSpread,
visibleRange.start,
],
);
const totalHeight = segments.length * SEGMENT_HEIGHT; const totalHeight = segments.length * SEGMENT_HEIGHT;
const visibleSegments = segments.slice( const visibleSegments = segments.slice(
visibleRange.start, visibleRange.start,
@ -149,46 +218,17 @@ export const VirtualizedMotionSegments = forwardRef<
aria-hidden="true" aria-hidden="true"
/> />
)} )}
{visibleSegments.map((segmentTime, index) => { {visibleSegments.map((segmentTime, index) =>
const firstHalfMotionValue = getMotionSegmentValue(segmentTime); renderSegment(segmentTime, index),
const secondHalfMotionValue = getMotionSegmentValue( )}
segmentTime + segmentDuration / 2,
);
return (
<div
key={`${segmentTime}_${segmentDuration}`}
style={{
position: "absolute",
top: `${(visibleRange.start + index) * SEGMENT_HEIGHT}px`,
height: `${SEGMENT_HEIGHT}px`,
width: "100%",
}}
>
<MotionSegment
events={events}
firstHalfMotionValue={firstHalfMotionValue}
secondHalfMotionValue={secondHalfMotionValue}
segmentDuration={segmentDuration}
segmentTime={segmentTime}
timestampSpread={timestampSpread}
motionOnly={motionOnly}
showMinimap={showMinimap}
minimapStartTime={minimapStartTime}
minimapEndTime={minimapEndTime}
setHandlebarTime={setHandlebarTime}
scrollToSegment={scrollToSegment}
dense={dense}
/>
</div>
);
})}
{visibleRange.end < segments.length && ( {visibleRange.end < segments.length && (
<div <div
style={{ style={{
position: "absolute", position: "absolute",
top: `${visibleRange.end * SEGMENT_HEIGHT}px`, top: `${visibleRange.end * SEGMENT_HEIGHT}px`,
height: `${(segments.length - visibleRange.end) * SEGMENT_HEIGHT}px`, height: `${
(segments.length - visibleRange.end) * SEGMENT_HEIGHT
}px`,
width: "100%", width: "100%",
}} }}
aria-hidden="true" aria-hidden="true"
@ -199,3 +239,5 @@ export const VirtualizedMotionSegments = forwardRef<
); );
}, },
); );
export default VirtualizedMotionSegments;