/ components / PageTransition.tsx
PageTransition.tsx
  1  import React, { useRef, useEffect } from 'react';
  2  import { Animated, ViewStyle } from 'react-native';
  3  
  4  interface PageTransitionProps {
  5    children: React.ReactNode;
  6    style?: ViewStyle;
  7    transitionType?: 'fade' | 'slide' | 'scale';
  8    duration?: number;
  9    delay?: number;
 10  }
 11  
 12  export const PageTransition: React.FC<PageTransitionProps> = ({
 13    children,
 14    style,
 15    transitionType = 'fade',
 16    duration = 300,
 17    delay = 0,
 18  }) => {
 19    const opacity = useRef(new Animated.Value(0)).current;
 20    const translateY = useRef(new Animated.Value(50)).current;
 21    const scale = useRef(new Animated.Value(0.9)).current;
 22  
 23    useEffect(() => {
 24      const animations: Animated.CompositeAnimation[] = [];
 25  
 26      if (
 27        transitionType === 'fade' ||
 28        transitionType === 'slide' ||
 29        transitionType === 'scale'
 30      ) {
 31        animations.push(
 32          Animated.timing(opacity, {
 33            toValue: 1,
 34            duration,
 35            useNativeDriver: true,
 36          })
 37        );
 38      }
 39  
 40      if (transitionType === 'slide') {
 41        animations.push(
 42          Animated.timing(translateY, {
 43            toValue: 0,
 44            duration,
 45            useNativeDriver: true,
 46          })
 47        );
 48      }
 49  
 50      if (transitionType === 'scale') {
 51        animations.push(
 52          Animated.timing(scale, {
 53            toValue: 1,
 54            duration,
 55            useNativeDriver: true,
 56          })
 57        );
 58      }
 59  
 60      const animation = Animated.parallel(animations);
 61  
 62      const timer = setTimeout(() => {
 63        animation.start();
 64      }, delay);
 65  
 66      return () => {
 67        clearTimeout(timer);
 68        animation.stop();
 69      };
 70    }, [transitionType, duration, delay, opacity, translateY, scale]);
 71  
 72    const getAnimatedStyle = () => {
 73      const baseStyle = { opacity };
 74  
 75      switch (transitionType) {
 76        case 'slide':
 77          return {
 78            ...baseStyle,
 79            transform: [{ translateY }],
 80          };
 81        case 'scale':
 82          return {
 83            ...baseStyle,
 84            transform: [{ scale }],
 85          };
 86        default:
 87          return baseStyle;
 88      }
 89    };
 90  
 91    return (
 92      <Animated.View style={[getAnimatedStyle(), style]}>
 93        {children}
 94      </Animated.View>
 95    );
 96  };
 97  
 98  // Hook for page transitions
 99  export const usePageTransition = (
100    transitionType: 'fade' | 'slide' | 'scale' = 'fade',
101    duration: number = 300
102  ) => {
103    const opacity = useRef(new Animated.Value(0)).current;
104    const translateY = useRef(new Animated.Value(50)).current;
105    const scale = useRef(new Animated.Value(0.9)).current;
106  
107    const animateIn = () => {
108      const animations: Animated.CompositeAnimation[] = [
109        Animated.timing(opacity, {
110          toValue: 1,
111          duration,
112          useNativeDriver: true,
113        }),
114      ];
115  
116      if (transitionType === 'slide') {
117        animations.push(
118          Animated.timing(translateY, {
119            toValue: 0,
120            duration,
121            useNativeDriver: true,
122          })
123        );
124      }
125  
126      if (transitionType === 'scale') {
127        animations.push(
128          Animated.timing(scale, {
129            toValue: 1,
130            duration,
131            useNativeDriver: true,
132          })
133        );
134      }
135  
136      Animated.parallel(animations).start();
137    };
138  
139    const animateOut = () => {
140      const animations: Animated.CompositeAnimation[] = [
141        Animated.timing(opacity, {
142          toValue: 0,
143          duration: duration / 2,
144          useNativeDriver: true,
145        }),
146      ];
147  
148      if (transitionType === 'slide') {
149        animations.push(
150          Animated.timing(translateY, {
151            toValue: -50,
152            duration: duration / 2,
153            useNativeDriver: true,
154          })
155        );
156      }
157  
158      if (transitionType === 'scale') {
159        animations.push(
160          Animated.timing(scale, {
161            toValue: 0.8,
162            duration: duration / 2,
163            useNativeDriver: true,
164          })
165        );
166      }
167  
168      return Animated.parallel(animations);
169    };
170  
171    const getAnimatedStyle = () => {
172      const baseStyle = { opacity };
173  
174      switch (transitionType) {
175        case 'slide':
176          return {
177            ...baseStyle,
178            transform: [{ translateY }],
179          };
180        case 'scale':
181          return {
182            ...baseStyle,
183            transform: [{ scale }],
184          };
185        default:
186          return baseStyle;
187      }
188    };
189  
190    return {
191      animateIn,
192      animateOut,
193      animatedStyle: getAnimatedStyle(),
194    };
195  };