Expo animations are an important means to enhance user experience. Expo supports multiple animation libraries and APIs, with comprehensive solutions from simple transition effects to complex interactive animations.
Animation Library Selection:
- React Native Animated API
React Native's built-in animation API, suitable for most animation needs.
Basic Animation:
typescriptimport { Animated, Easing } from 'react-native'; function FadeInComponent() { const fadeAnim = useRef(new Animated.Value(0)).current; useEffect(() => { Animated.timing(fadeAnim, { toValue: 1, duration: 1000, easing: Easing.ease, useNativeDriver: true, }).start(); }, []); return ( <Animated.View style={{ opacity: fadeAnim }}> <Text>Fade In</Text> </Animated.View> ); }
Interpolation Animation:
typescriptfunction InterpolationComponent() { const translateX = useRef(new Animated.Value(0)).current; const rotate = translateX.interpolate({ inputRange: [0, 100], outputRange: ['0deg', '180deg'], }); const scale = translateX.interpolate({ inputRange: [0, 100], outputRange: [1, 2], }); return ( <Animated.View style={{ transform: [ { translateX }, { rotate }, { scale }, ], }} > <Text>Animated Box</Text> </Animated.View> ); }
Parallel and Sequence Animations:
typescriptfunction ComplexAnimation() { const fadeAnim = useRef(new Animated.Value(0)).current; const scaleAnim = useRef(new Animated.Value(1)).current; const startAnimation = () => { Animated.parallel([ Animated.timing(fadeAnim, { toValue: 1, duration: 500, useNativeDriver: true, }), Animated.spring(scaleAnim, { toValue: 1.5, friction: 5, useNativeDriver: true, }), ]).start(); }; return ( <Animated.View style={{ opacity: fadeAnim, transform: [{ scale: scaleAnim }], }} > <Button title="Animate" onPress={startAnimation} /> </Animated.View> ); }
- React Native Reanimated
More powerful animation library, supports gestures and complex animations.
Installation:
bashnpx expo install react-native-reanimated
Basic Animation:
typescriptimport Animated, { useSharedValue, useAnimatedStyle, withTiming, withSpring, withSequence, } from 'react-native-reanimated'; function ReanimatedComponent() { const opacity = useSharedValue(0); const scale = useSharedValue(1); const animatedStyle = useAnimatedStyle(() => { return { opacity: opacity.value, transform: [{ scale: scale.value }], }; }); const startAnimation = () => { opacity.value = withTiming(1, { duration: 500 }); scale.value = withSpring(1.5); }; return ( <Animated.View style={animatedStyle}> <Button title="Animate" onPress={startAnimation} /> </Animated.View> ); }
Gesture Animation:
typescriptimport { Gesture, GestureDetector } from 'react-native-gesture-handler'; function GestureComponent() { const translateX = useSharedValue(0); const translateY = useSharedValue(0); const pan = Gesture.Pan() .onUpdate((event) => { translateX.value = event.translationX; translateY.value = event.translationY; }) .onEnd(() => { translateX.value = withSpring(0); translateY.value = withSpring(0); }); const animatedStyle = useAnimatedStyle(() => { return { transform: [ { translateX: translateX.value }, { translateY: translateY.value }, ], }; }); return ( <GestureDetector gesture={pan}> <Animated.View style={animatedStyle}> <Text>Drag me</Text> </Animated.View> </GestureDetector> ); }
- Lottie
Complex animations created with Adobe After Effects.
Installation:
bashnpx expo install lottie-react-native
Using Lottie Animations:
typescriptimport LottieView from 'lottie-react-native'; function LottieComponent() { return ( <LottieView source={require('./assets/animation.json')} autoPlay loop style={{ width: 200, height: 200 }} /> ); }
Controlling Lottie Animations:
typescriptfunction ControlledLottie() { const animationRef = useRef<LottieView>(null); const playAnimation = () => { animationRef.current?.play(); }; const pauseAnimation = () => { animationRef.current?.pause(); }; const resetAnimation = () => { animationRef.current?.reset(); }; return ( <View> <LottieView ref={animationRef} source={require('./assets/animation.json')} style={{ width: 200, height: 200 }} /> <Button title="Play" onPress={playAnimation} /> <Button title="Pause" onPress={pauseAnimation} /> <Button title="Reset" onPress={resetAnimation} /> </View> ); }
Common Animation Patterns:
- Fade In/Out
typescriptfunction FadeInOut() { const opacity = useSharedValue(0); const fadeIn = () => { opacity.value = withTiming(1, { duration: 500 }); }; const fadeOut = () => { opacity.value = withTiming(0, { duration: 500 }); }; const style = useAnimatedStyle(() => ({ opacity: opacity.value, })); return ( <Animated.View style={style}> <Button title="Fade In" onPress={fadeIn} /> <Button title="Fade Out" onPress={fadeOut} /> </Animated.View> ); }
- Slide Animation
typescriptfunction SlideAnimation() { const translateX = useSharedValue(-300); const slideIn = () => { translateX.value = withSpring(0); }; const slideOut = () => { translateX.value = withSpring(-300); }; const style = useAnimatedStyle(() => ({ transform: [{ translateX: translateX.value }], })); return ( <Animated.View style={style}> <Text>Slide Content</Text> </Animated.View> ); }
- Scale Animation
typescriptfunction ScaleAnimation() { const scale = useSharedValue(1); const scaleUp = () => { scale.value = withSpring(1.5); }; const scaleDown = () => { scale.value = withSpring(1); }; const style = useAnimatedStyle(() => ({ transform: [{ scale: scale.value }], })); return ( <Animated.View style={style}> <Text>Scale Content</Text> </Animated.View> ); }
- Rotate Animation
typescriptfunction RotateAnimation() { const rotation = useSharedValue(0); const rotate = () => { rotation.value = withTiming(rotation.value + 360, { duration: 1000, easing: Easing.linear, }); }; const style = useAnimatedStyle(() => ({ transform: [{ rotate: `${rotation.value}deg` }], })); return ( <Animated.View style={style}> <Text>Rotate Content</Text> </Animated.View> ); }
Performance Optimization:
- Use Native Driver
typescript// Use useNativeDriver to improve performance Animated.timing(value, { toValue: 1, duration: 500, useNativeDriver: true, // Run on UI thread }).start();
- Avoid Complex Layouts in Animations
typescript// Avoid using flex layout in animated components const style = useAnimatedStyle(() => ({ transform: [{ translateX: translateX.value }], // Avoid using flex-related properties }));
- Optimize with shouldComponentUpdate
typescriptconst AnimatedComponent = React.memo(({ value }) => { const animatedStyle = useAnimatedStyle(() => ({ opacity: value.value, })); return <Animated.View style={animatedStyle} />; });
Best Practices:
-
Choose Appropriate Animation Library
- Simple animations: React Native Animated
- Complex animations and gestures: React Native Reanimated
- Designer-created animations: Lottie
-
Performance First
- Use native driver
- Avoid complex layouts in animations
- Optimize with useMemo and useCallback
-
User Experience
- Provide smooth transition effects
- Avoid over-animating
- Consider lower-performance devices
-
Accessibility
- Provide alternatives for animations
- Respect user's reduced motion preference
- Provide animation control options
By using animations appropriately, you can significantly improve user experience and interaction quality of Expo apps.