Expo中如何实现动画效果?有哪些常用的动画库?
Expo动画是提升用户体验的重要手段。Expo支持多种动画库和API,从简单的过渡效果到复杂的交互动画都有完善的解决方案。动画库选择:React Native Animated APIReact Native内置的动画API,适合大多数动画需求。基础动画:import { 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> );}插值动画:function 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> );}并行和序列动画:function 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更强大的动画库,支持手势和复杂动画。安装:npx expo install react-native-reanimated基础动画:import 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> );}手势动画:import { 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创建的复杂动画。安装:npx expo install lottie-react-native使用Lottie动画:import LottieView from 'lottie-react-native';function LottieComponent() { return ( <LottieView source={require('./assets/animation.json')} autoPlay loop style={{ width: 200, height: 200 }} /> );}控制Lottie动画:function 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> );}常用动画模式:淡入淡出function 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> );}滑动动画function 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> );}缩放动画function 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> );}旋转动画function 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> );}性能优化:使用原生驱动// 使用useNativeDriver提高性能Animated.timing(value, { toValue: 1, duration: 500, useNativeDriver: true, // 在UI线程运行}).start();避免在动画中使用复杂布局// 避免在动画组件中使用flex布局const style = useAnimatedStyle(() => ({ transform: [{ translateX: translateX.value }], // 避免使用flex相关的属性}));使用shouldComponentUpdate优化const 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应用的用户体验和交互质量。