Expo动画是提升用户体验的重要手段。Expo支持多种动画库和API,从简单的过渡效果到复杂的交互动画都有完善的解决方案。
动画库选择:
- React Native Animated API
React Native内置的动画API,适合大多数动画需求。
基础动画:
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> ); }
插值动画:
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> ); }
并行和序列动画:
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
更强大的动画库,支持手势和复杂动画。
安装:
bashnpx expo install react-native-reanimated
基础动画:
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> ); }
手势动画:
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
使用Adobe After Effects创建的复杂动画。
安装:
bashnpx expo install lottie-react-native
使用Lottie动画:
typescriptimport LottieView from 'lottie-react-native'; function LottieComponent() { return ( <LottieView source={require('./assets/animation.json')} autoPlay loop style={{ width: 200, height: 200 }} /> ); }
控制Lottie动画:
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> ); }
常用动画模式:
- 淡入淡出
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> ); }
- 滑动动画
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> ); }
- 缩放动画
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> ); }
- 旋转动画
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> ); }
性能优化:
- 使用原生驱动
typescript// 使用useNativeDriver提高性能 Animated.timing(value, { toValue: 1, duration: 500, useNativeDriver: true, // 在UI线程运行 }).start();
- 避免在动画中使用复杂布局
typescript// 避免在动画组件中使用flex布局 const style = useAnimatedStyle(() => ({ transform: [{ translateX: translateX.value }], // 避免使用flex相关的属性 }));
- 使用shouldComponentUpdate优化
typescriptconst AnimatedComponent = React.memo(({ value }) => { const animatedStyle = useAnimatedStyle(() => ({ opacity: value.value, })); return <Animated.View style={animatedStyle} />; });
最佳实践:
-
选择合适的动画库
- 简单动画:React Native Animated
- 复杂动画和手势:React Native Reanimated
- 设计师创建的动画:Lottie
-
性能优先
- 使用原生驱动
- 避免在动画中使用复杂布局
- 使用useMemo和useCallback优化
-
用户体验
- 提供流畅的过渡效果
- 避免过度动画
- 考虑性能较差的设备
-
可访问性
- 为动画提供替代方案
- 尊重用户的减少动画偏好
- 提供动画控制选项
通过合理使用动画,可以显著提升Expo应用的用户体验和交互质量。