diff --git a/index.tsx b/index.tsx index c59e454..9846e01 100644 --- a/index.tsx +++ b/index.tsx @@ -23,11 +23,21 @@ import { GestureStateManagerType, } from 'react-native-gesture-handler/lib/typescript/handlers/gestures/gestureStateManager' -export default function Zoom(props: PropsWithChildren): React.ReactNode { +interface UseZoomGestureProps { + animationFunction?: (toValue: number, config?: object) => any; + animationConfig?: object; +} + +export function useZoomGesture(props: UseZoomGestureProps = {}): { + zoomGesture: typeof Gesture; + contentContainerAnimatedStyle: any; + onLayout: (event: LayoutChangeEvent) => void; + onLayoutContent: (event: LayoutChangeEvent) => void; + zoomOut: () => void; + isZoomedIn: SharedValue; + zoomGestureLastTime: SharedValue; +} { const { - style, - contentContainerStyle, - children, animationFunction = withTiming, animationConfig, } = props @@ -36,6 +46,7 @@ export default function Zoom(props: PropsWithChildren): React.ReactNo const pinchScale = useSharedValue(1) const lastScale = useSharedValue(1) const isZoomedIn = useSharedValue(false) + const zoomGestureLastTime = useSharedValue(0) const containerDimensions = useSharedValue({ width: 0, height: 0 }) const contentDimensions = useSharedValue({ width: 1, height: 1 }) @@ -222,33 +233,38 @@ export default function Zoom(props: PropsWithChildren): React.ReactNo isZoomedIn, ]) - const panOffsetsBeforeGestureStart: SharedValue<{ x: number | null; y: number | null }> = useSharedValue({ - x: null, - y: null, - }) + const updateZoomGestureLastTime = useCallback((): void => { + 'worklet' - const zoomGestures = useMemo(() => { + zoomGestureLastTime.value = Date.now() + }, [zoomGestureLastTime]) + + const zoomGesture = useMemo(() => { const tapGesture = Gesture.Tap() .numberOfTaps(2) + .onStart(() => { + updateZoomGestureLastTime() + }) .onEnd(() => { + updateZoomGestureLastTime() + runOnJS(onDoubleTap)() }) const panGesture = Gesture.Pan() .onStart((event: GestureUpdateEvent): void => { - panStartOffsetX.value = event.translationX - panStartOffsetY.value = event.translationY + updateZoomGestureLastTime() + + const { translationX, translationY } = event + + panStartOffsetX.value = translationX + panStartOffsetY.value = translationY }) .onUpdate((event: GestureUpdateEvent): void => { - let { translationX, translationY } = event + updateZoomGestureLastTime() - if (panOffsetsBeforeGestureStart.value.x === null || panOffsetsBeforeGestureStart.value.y === null) { - panOffsetsBeforeGestureStart.value.x = translationX - panOffsetsBeforeGestureStart.value.y = translationY - } + let { translationX, translationY } = event - translationX -= panOffsetsBeforeGestureStart.value.x - translationY -= panOffsetsBeforeGestureStart.value.y translationX -= panStartOffsetX.value translationY -= panStartOffsetY.value @@ -256,12 +272,10 @@ export default function Zoom(props: PropsWithChildren): React.ReactNo translateY.value = lastOffsetY.value + translationY / lastScale.value }) .onEnd((event: GestureStateChangeEvent): void => { + updateZoomGestureLastTime() + let { translationX, translationY } = event - if (panOffsetsBeforeGestureStart.value.x !== null && panOffsetsBeforeGestureStart.value.y !== null) { - translationX -= panOffsetsBeforeGestureStart.value.x - translationY -= panOffsetsBeforeGestureStart.value.y - } translationX -= panStartOffsetX.value translationY -= panStartOffsetY.value @@ -269,9 +283,6 @@ export default function Zoom(props: PropsWithChildren): React.ReactNo lastOffsetX.value = lastOffsetX.value + translationX / lastScale.value lastOffsetY.value = lastOffsetY.value + translationY / lastScale.value - panOffsetsBeforeGestureStart.value.x = null - panOffsetsBeforeGestureStart.value.y = null - runOnJS(handlePanOutside)() }) .onTouchesMove((e: GestureTouchEvent, state: GestureStateManagerType): void => { @@ -287,16 +298,23 @@ export default function Zoom(props: PropsWithChildren): React.ReactNo .maxPointers(2) const pinchGesture = Gesture.Pinch() + .onStart(() => { + updateZoomGestureLastTime() + }) .onUpdate(({ scale }: GestureUpdateEvent): void => { + updateZoomGestureLastTime() + pinchScale.value = scale }) .onEnd(({ scale }: GestureUpdateEvent): void => { + updateZoomGestureLastTime() + pinchScale.value = scale runOnJS(onPinchEnd)(scale) }) - return Gesture.Race( + return Gesture.Exclusive( Gesture.Simultaneous(pinchGesture, panGesture), tapGesture ) @@ -311,10 +329,9 @@ export default function Zoom(props: PropsWithChildren): React.ReactNo translateY, lastScale, isZoomedIn, - panOffsetsBeforeGestureStart, ]) - const animContentContainerStyle = useAnimatedStyle(() => ({ + const contentContainerAnimatedStyle = useAnimatedStyle(() => ({ transform: [ { scale: baseScale.value * pinchScale.value }, { translateX: translateX.value }, @@ -322,15 +339,43 @@ export default function Zoom(props: PropsWithChildren): React.ReactNo ], })) + return ({ + zoomGesture, + contentContainerAnimatedStyle, + onLayout, + onLayoutContent, + zoomOut, + isZoomedIn, + zoomGestureLastTime, + }) +} + +export default function Zoom(props: PropsWithChildren): React.ReactNode { + const { + style, + contentContainerStyle, + children, + ...rest + } = props + + const { + zoomGesture, + onLayout, + onLayoutContent, + contentContainerAnimatedStyle, + } = useZoomGesture({ + ...rest + }) + return ( - + {children} diff --git a/package.json b/package.json index d71a49d..ca135b6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-zoom-reanimated", - "version": "1.3.3", + "version": "1.4.0", "description": "Zoom component on react-native + react-native-reanimated + react-native-gesture-handler", "keywords": [ "zoom",