UNPKG

@umituz/react-native-animation

Version:

Universal animation system for React Native with react-native-reanimated. Provides declarative animations, gesture handling, and preset configurations.

110 lines (94 loc) 3.16 kB
/** * useModalAnimations Hook * Single Responsibility: Manage modal overlay and container animations * Uses react-native-animation's timing and spring animations */ import { useEffect, useMemo, useRef } from "react"; import { useAnimatedStyle } from "react-native-reanimated"; import { useReanimatedReady } from "./useReanimatedReady"; import { useTimingAnimation } from "./useTimingAnimation"; import { useSpringAnimation } from "./useSpringAnimation"; export interface ModalAnimationConfig { overlayFadeDuration?: number; modalScaleDamping?: number; modalScaleStiffness?: number; modalFadeDuration?: number; } const DEFAULT_MODAL_ANIMATION_CONFIG: Required<ModalAnimationConfig> = { overlayFadeDuration: 300, modalScaleDamping: 7, modalScaleStiffness: 50, modalFadeDuration: 300, } as const; export interface UseModalAnimationsReturn { isReady: boolean; overlayStyle: ReturnType<typeof useAnimatedStyle>; modalStyle: ReturnType<typeof useAnimatedStyle>; } /** * Hook for managing modal overlay and container animations */ export function useModalAnimations( visible: boolean, config?: ModalAnimationConfig, ): UseModalAnimationsReturn { const isReanimatedReady = useReanimatedReady(); const overlayTiming = useTimingAnimation(); const modalTiming = useTimingAnimation(); const spring = useSpringAnimation(); const previousVisibleRef = useRef<boolean>(false); const animationConfig = useMemo( () => ({ ...DEFAULT_MODAL_ANIMATION_CONFIG, ...config, }), [ config?.overlayFadeDuration, config?.modalScaleDamping, config?.modalScaleStiffness, config?.modalFadeDuration, ], ); useEffect(() => { if (!isReanimatedReady) { return; } // Only animate if visible state actually changed if (visible === previousVisibleRef.current) { return; } previousVisibleRef.current = visible; if (visible) { // Reset to initial state before animating overlayTiming.opacity.value = 0; modalTiming.opacity.value = 0; spring.scale.value = 0; // Start animations immediately overlayTiming.fadeIn({ duration: animationConfig.overlayFadeDuration }); modalTiming.fadeIn({ duration: animationConfig.modalFadeDuration }); spring.scaleIn({ damping: animationConfig.modalScaleDamping, stiffness: animationConfig.modalScaleStiffness, }); } else { overlayTiming.fadeOut({ duration: animationConfig.overlayFadeDuration }); modalTiming.fadeOut({ duration: animationConfig.modalFadeDuration }); spring.scaleOut({ damping: animationConfig.modalScaleDamping, stiffness: animationConfig.modalScaleStiffness, }); } }, [visible, isReanimatedReady, animationConfig, overlayTiming, modalTiming, spring]); const overlayStyle = useAnimatedStyle(() => ({ opacity: overlayTiming.opacity.value, })); const modalStyle = useAnimatedStyle(() => ({ opacity: modalTiming.opacity.value, transform: [{ scale: spring.scale.value }], })); return { isReady: isReanimatedReady, overlayStyle, modalStyle, }; }