React Native - 动画
React Native - 动画
Section titled “React Native - 动画”React Native 提供了强大的 API 用于创建动画。本章节重点介绍 Animated API,用于对动画值进行精细控制,并简要提及用于简单过渡的 LayoutAnimation。我们将使用函数组件和 Hooks。
使用 Animated API
Section titled “使用 Animated API”Animated API 允许你创建基于时间的动画(例如 Animated.timing)、基于弹簧的动画(Animated.spring)或衰减动画(Animated.decay)。通常你会对特殊的 Animated 组件(例如 Animated.View、Animated.Text、Animated.Image)的特定样式属性进行动画处理。
我们将创建一个示例,演示如何通过按下操作改变一个盒子的宽度和高度。
AnimatedBox.js
import React, { useRef, useState } from 'react';import { View, StyleSheet, Animated, TouchableOpacity, Text } from 'react-native';
const AnimatedBox = () => { // 使用 useRef 存储 Animated 值,使其在多次渲染之间保持不变 const animatedWidth = useRef(new Animated.Value(100)).current; const animatedHeight = useRef(new Animated.Value(100)).current; const animatedOpacity = useRef(new Animated.Value(1)).current;
const [isExpanded, setIsExpanded] = useState(false);
const animateBox = () => { const newWidth = isExpanded ? 100 : 200; const newHeight = isExpanded ? 100 : 200; const newOpacity = isExpanded ? 1 : 0.5;
// 并行动画 Animated.parallel([ Animated.timing(animatedWidth, { toValue: newWidth, duration: 500, // 毫秒 useNativeDriver: false, // 对于非布局属性,设置为 'true' 可获得更好的性能 }), Animated.timing(animatedHeight, { toValue: newHeight, duration: 700, useNativeDriver: false, }), Animated.timing(animatedOpacity, { toValue: newOpacity, duration: 700, useNativeDriver: true, // 透明度可以使用原生驱动 }) ]).start(() => { setIsExpanded(!isExpanded); // 动画完成后切换状态 }); };
// 创建一个动画样式对象 const animatedStyle = { width: animatedWidth, height: animatedHeight, opacity: animatedOpacity, };
return ( <View style={styles.container}> <TouchableOpacity onPress={animateBox}> <Animated.View style={[styles.box, animatedStyle]} /> </TouchableOpacity> <Text style={styles.instructionText}> Tap the box to {isExpanded ? 'collapse' : 'expand'} it. </Text> </View> );};
const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#f0f0f0', }, box: { backgroundColor: 'royalblue', // 初始尺寸由 Animated.Value 设置 }, instructionText: { marginTop: 20, fontSize: 16, color: '#333', }});
export default AnimatedBox;useRef和Animated.Value:我们在useRef内部初始化Animated.Value,以确保它们在多次渲染之间保持不变,不会被重新创建。animatedWidth.current提供了对Animated.Value实例的访问。Animated.timing:创建一个动画,在指定的持续时间内过渡一个值。其他类型包括Animated.spring(基于物理的弹簧动画)和Animated.decay(初始速度逐渐减慢直至停止)。Animated.parallel:同时运行多个动画。其他组合器包括Animated.sequence(动画一个接一个按顺序运行)和Animated.stagger(错开起始时间的动画)。start():开始动画。它可以接受一个可选的回调函数,在动画完成时触发。Animated.View:一个特殊的组件,其样式属性可以被动画化。你必须使用Animated.View、Animated.Text、Animated.Image等,或者使用Animated.createAnimatedComponent()创建自己的可动画组件。useNativeDriver: true:为了提高性能,将动画卸载到原生线程执行。这仅适用于非布局属性(如transform、opacity)。布局属性(width、height、margin、padding、top、left等)必须使用useNativeDriver: false(默认值)并在 JS 线程上运行。
当你点击蓝色方块时,它的宽度、高度和透明度会平滑地动画变化。再次点击将反转动画。
LayoutAnimation(更简单的过渡)
Section titled “LayoutAnimation(更简单的过渡)”LayoutAnimation 提供了一种更简单的方式来动画处理布局属性变化时的过渡(例如,由于状态更新引起)。它会在下次布局计算发生时,将视图动画移动到新位置。
LayoutAnimationExample.js
import React, { useState } from 'react';import { View, StyleSheet, TouchableOpacity, Text, LayoutAnimation, UIManager, Platform} from 'react-native';
// 为 Android 启用 LayoutAnimation(iOS 默认已启用)if (Platform.OS === 'android') { if (UIManager.setLayoutAnimationEnabledExperimental) { UIManager.setLayoutAnimationEnabledExperimental(true); }}
const LayoutAnimationExample = () => { const [boxSize, setBoxSize] = useState(100); const [isToggled, setIsToggled] = useState(false);
const toggleBox = () => { // 配置动画类型。 // 选项:LayoutAnimation.Presets.easeInEaseOut, .linear, .spring // 或定义自定义动画:LayoutAnimation.configureNext(config); LayoutAnimation.configureNext(LayoutAnimation.Presets.spring); setBoxSize(isToggled ? 100 : 200); setIsToggled(!isToggled); };
return ( <View style={styles.container}> <TouchableOpacity onPress={toggleBox}> <View style={[styles.box, { width: boxSize, height: boxSize }]} > <Text style={styles.boxText}>Tap Me</Text> </View> </TouchableOpacity> </View> );};
const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#f5fcff', }, box: { backgroundColor: 'mediumseagreen', justifyContent: 'center', alignItems: 'center', }, boxText: { color: 'white', fontWeight: 'bold' }});
export default LayoutAnimationExample;LayoutAnimation 对于全局布局变化更简单,但不如 Animated 灵活。你需要在引起重新布局的状态变化之前调用 LayoutAnimation.configureNext()。
选择动画 API
Section titled “选择动画 API”- 当需要进行复杂、交互式动画,或需要对动画值和序列进行细粒度控制时,使用
Animated。 - 对于由状态变化触发的简单、即发即忘的布局属性动画,使用
LayoutAnimation。 - 对于更高级、更高性能的动画,特别是基于手势的动画,可以考虑使用 React Native Reanimated 和 React Native Gesture Handler 等库。
查阅官方动画指南:https://reactnative.dev/docs/animations 和 https://reactnative.dev/docs/layoutanimation。