From 48a2af2aab68f68e169fe2995fc2041079e19a7e Mon Sep 17 00:00:00 2001 From: deku-nattsu Date: Thu, 5 Mar 2026 17:39:27 +0000 Subject: [PATCH] feat: add an option to skip measuring --- src/core/types.ts | 1 + src/hooks/useTourMeasurement.ts | 28 ++++++++++++++++++++++------ src/ui/CoachmarkOverlay.tsx | 1 + 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/core/types.ts b/src/core/types.ts index 365b130..25ce7ba 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -47,6 +47,7 @@ export type TourStep = { onExit?: () => void; // Custom tooltip renderer renderTooltip?: TooltipRenderer; + skipMeasurement?: boolean; }; export type Tour = { diff --git a/src/hooks/useTourMeasurement.ts b/src/hooks/useTourMeasurement.ts index 531382c..a114b77 100644 --- a/src/hooks/useTourMeasurement.ts +++ b/src/hooks/useTourMeasurement.ts @@ -5,7 +5,7 @@ import { Dimensions } from 'react-native'; import { withTiming, Easing } from 'react-native-reanimated'; import type { SharedValue } from 'react-native-reanimated'; -import type { TourStep } from '../core/types'; +import type { CoachmarkState, Rect, TourStep } from '../core/types'; import { inset } from '../ui/shapes'; import { isRectVisible } from '../utils/autoScroll'; import { measureInWindowByRef } from '../utils/measure'; @@ -25,6 +25,7 @@ type UseTourMeasurementParams = { holeY: SharedValue; holeWidth: SharedValue; holeHeight: SharedValue; + state: CoachmarkState; }; type MeasurementResult = { @@ -52,6 +53,7 @@ type MeasurementResult = { * @param holeY - Reanimated shared value for the y-coordinate of the highlight hole * @param holeWidth - Reanimated shared value for the width of the highlight hole * @param holeHeight - Reanimated shared value for the height of the highlight hole + * @param state - The current state of the coachmark * * @returns Object containing the measured target rectangle, hole shape configuration, hole radius, and a remeasure function * @@ -86,7 +88,11 @@ export function useTourMeasurement({ holeY, holeWidth, holeHeight, + state, }: UseTourMeasurementParams) { + const preMeasuredRect = activeStep?.skipMeasurement + ? state.measured[activeStep.id] + : undefined; const [result, setResult] = useState({ targetRect: null, holeShape: 'rect', @@ -95,6 +101,16 @@ export function useTourMeasurement({ const { width: W, height: H } = Dimensions.get('window'); + const measureRect = useCallback( + async (ref: any, stepId: string): Promise => { + if (preMeasuredRect) return preMeasuredRect; + const measured = await measureInWindowByRef(ref); + setMeasured(stepId, measured); + return measured; + }, + [preMeasuredRect, setMeasured] + ); + const remeasure = useCallback(async () => { if (!activeStep) return; @@ -132,7 +148,8 @@ export function useTourMeasurement({ const padding = activeStep.scrollPadding ?? 20; const force = autoFocus === 'always'; - const rect = await measureInWindowByRef(ref); + const rect = await measureRect(ref, activeStep.id); + const { isVisible } = isRectVisible(rect, padding); if (!isVisible || force) { @@ -161,9 +178,8 @@ export function useTourMeasurement({ } } - const rect = await measureInWindowByRef(ref); + const rect = await measureRect(ref, activeStep.id); const padded = inset(rect, anchor.padding ?? 10); - setMeasured(activeStep.id, rect); const shape = anchor.shape ?? activeStep.shape ?? 'rect'; const radius = anchor.radius ?? activeStep.radius ?? 12; @@ -186,14 +202,14 @@ export function useTourMeasurement({ }, [ activeStep, getAnchor, - next, - setMeasured, reduceMotion, durationMs, holeX, holeY, holeWidth, holeHeight, + next, + measureRect, H, ]); diff --git a/src/ui/CoachmarkOverlay.tsx b/src/ui/CoachmarkOverlay.tsx index 83b2645..470c24e 100644 --- a/src/ui/CoachmarkOverlay.tsx +++ b/src/ui/CoachmarkOverlay.tsx @@ -93,6 +93,7 @@ export const CoachmarkOverlay: React.FC = () => { holeY, holeWidth, holeHeight, + state, }); const tooltipPos = useTooltipPosition({