React Native【详解】动画

发布于:2025-06-22 ⋅ 阅读:(19) ⋅ 点赞:(0)

基础动画的实现流程

  1. 使用支持动画的组件

          <Animated.View
            style={[
              {
                opacity: fadeAnim, // 绑定透明度动画值
              },
            ]}
          >
            <Text>动画元素</Text>
          </Animated.View>
    
    • Animated.View:用于创建动画容器,支持所有 View 的属性。
    • Animated.Text:用于创建文本动画,支持所有 Text 的属性。
    • Animated.Image:用于创建图片动画,支持所有 Image 的属性。
    • Animated.ScrollView:用于创建滚动视图动画,支持所有 ScrollView 的属性。
    • Animated.FlatList:用于创建列表动画,支持所有 FlatList 的属性。
    • Animated.SectionList:用于创建列表动画,支持所有 SectionList 的属性。
  2. 导入 Animated

    import {
      Animated
    } from "react-native";
    
  3. 声明动画变量

      // 透明度动画值
      const fadeAnim = useState(new Animated.Value(0))[0]; // 初始值为0(完全透明)
    

      // 透明度动画值
      const fadeAnim = useRef(new Animated.Value(0)).current; // 初始值为0(完全透明)
    
  4. 执行动画

      // 淡入动画函数
      const fadeIn = () => {
        Animated.timing(fadeAnim, {
          toValue: 1, // 目标值为1(完全不透明)
          duration: 1000, // 动画持续时间1秒
          useNativeDriver: false, // 是否启用原生动画驱动,建议不使用
        }).start();
      };
    

动画的启动 start

通过动画函数定义好动画后,需执行 start 方法,才会启动动画

start 方法可以传入一个动画结束/中断的回调函数

Animated.timing(value, {
  toValue: 1,
  duration: 1000,
}).start(({ finished }) => {
  if (finished) {
    console.log('动画成功完成');
  } else {
    console.log('动画被中断');
  }
});

动画类型

平移

可以通过设置不同的样式实现

  • marginLeft、marginRight、marginTop、marginBottom
  • translateX、translateY
  • 绝对定位时 left、right、top、bottom
import React, { useState } from "react";
import {
  Animated,
  Button,
  SafeAreaView,
  StyleSheet,
  Text,
  View,
} from "react-native";
const App = () => {
  // 使用 Animated.ValueXY 控制二维平移
  const positionAnim = useState(new Animated.ValueXY({ x: 0, y: 0 }))[0];
  // 使用单独的 Animated.Value 控制一维平移
  const translateX = useState(new Animated.Value(0))[0];
  const translateY = useState(new Animated.Value(0))[0];
  // 水平移动函数 - 使用 Animated.ValueXY
  const moveHorizontal = () => {
    Animated.timing(positionAnim, {
      toValue: { x: positionAnim.x._value === 0 ? 150 : 0, y: 0 },
      duration: 1000,
      useNativeDriver: true,
    }).start();
  };
  // 垂直移动函数 - 使用 Animated.ValueXY
  const moveVertical = () => {
    Animated.timing(positionAnim, {
      toValue: { x: 0, y: positionAnim.y._value === 0 ? 150 : 0 },
      duration: 1000,
      useNativeDriver: true,
    }).start();
  };
  // 对角线移动函数 - 使用 Animated.ValueXY
  const moveDiagonal = () => {
    Animated.timing(positionAnim, {
      toValue: {
        x: positionAnim.x._value === 0 ? 150 : 0,
        y: positionAnim.y._value === 0 ? 150 : 0,
      },
      duration: 1000,
      useNativeDriver: true,
    }).start();
  };
  // 复合移动函数 - 使用单独的 Animated.Value
  const complexMove = () => {
    Animated.sequence([
      // 向右移动
      Animated.timing(translateX, {
        toValue: 150,
        duration: 1000,
        useNativeDriver: true,
      }),
      // 向下移动
      Animated.timing(translateY, {
        toValue: 150,
        duration: 1000,
        useNativeDriver: true,
      }),
      // 向左移动
      Animated.timing(translateX, {
        toValue: 0,
        duration: 1000,
        useNativeDriver: true,
      }),
      // 向上移动
      Animated.timing(translateY, {
        toValue: 0,
        duration: 1000,
        useNativeDriver: true,
      }),
    ]).start();
  };
  // 重置所有动画
  const resetAnimation = () => {
    positionAnim.setValue({ x: 0, y: 0 });
    translateX.setValue(0);
    translateY.setValue(0);
  };
  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.controls}>
        <Button title="水平移动" onPress={moveHorizontal} />
        <Button title="垂直移动" onPress={moveVertical} />
        <Button title="对角线移动" onPress={moveDiagonal} />
        <Button title="复合移动" onPress={complexMove} />
        <Button title="重置" onPress={resetAnimation} color="red" />
      </View>
      {/* 使用 Animated.ValueXY 的平移动画 */}
      <Animated.View
        style={[
          styles.box,
          {
            transform: [
              { translateX: positionAnim.x },
              { translateY: positionAnim.y },
            ],
          },
        ]}
      >
        <Text style={styles.text}>ValueXY 平移</Text>
      </Animated.View>
      {/* 使用单独 Animated.Value 的平移动画 */}
      <Animated.View
        style={[
          styles.box,
          {
            transform: [{ translateX }, { translateY }],
            backgroundColor: "green",
            marginTop: 20,
          },
        ]}
      >
        <Text style={styles.text}>独立值 平移</Text>
      </Animated.View>
    </SafeAreaView>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
  },
  controls: {
    marginBottom: 20,
    width: "80%",
    flexDirection: "row",
    justifyContent: "space-around",
    flexWrap: "wrap",
  },
  box: {
    width: 120,
    height: 120,
    backgroundColor: "blue",
    justifyContent: "center",
    alignItems: "center",
    borderRadius: 10,
  },
  text: {
    color: "white",
    fontSize: 16,
  },
});
export default App;

旋转

需用 interpolate 实现角度值格式转换

import React, { useState } from "react";
import {
  Animated,
  Button,
  SafeAreaView,
  StyleSheet,
  Text,
  View,
} from "react-native";
const App = () => {
  // 2D旋转动画值 (单位是弧度)
  const rotateValue = useState(new Animated.Value(0))[0];
  // 开始旋转
  const startRotation = () => {
    Animated.loop(
      Animated.timing(rotateValue, {
        toValue: rotateValue._value + Math.PI * 2, // 每次增加360度
        duration: 3000, // 旋转一周的时间(毫秒)
        useNativeDriver: true, // 使用原生驱动以获得更好的性能
      })
    ).start();
  };
  // 停止旋转
  const stopRotation = () => {
    rotateValue.stopAnimation();
  };
  // 重置旋转
  const resetRotation = () => {
    rotateValue.setValue(0);
  };
  // 将弧度值转换为角度值
  const rotateInterpolate = rotateValue.interpolate({
    inputRange: [0, Math.PI * 2],
    outputRange: ["0deg", "360deg"],
  });
  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.controls}>
        <Button title="开始旋转" onPress={startRotation} />
        <Button title="停止旋转" onPress={stopRotation} />
        <Button title="重置" onPress={resetRotation} color="red" />
      </View>
      {/* 旋转视图 */}
      <Animated.View
        style={[
          styles.box,
          {
            transform: [{ rotate: rotateInterpolate }],
          },
        ]}
      >
        <Text style={styles.text}>2D旋转动画</Text>
      </Animated.View>
    </SafeAreaView>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
  },
  controls: {
    marginBottom: 20,
    width: "80%",
    flexDirection: "row",
    justifyContent: "space-around",
    flexWrap: "wrap",
  },
  box: {
    width: 150,
    height: 150,
    backgroundColor: "blue",
    justifyContent: "center",
    alignItems: "center",
    borderRadius: 10,
  },
  text: {
    color: "white",
    fontSize: 18,
  },
});
export default App;

缩放

import React, { useState } from "react";
import {
  Animated,
  Button,
  SafeAreaView,
  StyleSheet,
  Text,
  View,
} from "react-native";
const App = () => {
  // 缩放动画值
  const scaleValue = useState(new Animated.Value(1))[0]; // 初始值为1(原始大小)
  // 放大动画
  const zoomIn = () => {
    Animated.timing(scaleValue, {
      toValue: 2, // 放大到2倍大小
      duration: 1000,
      useNativeDriver: true,
    }).start();
  };
  // 缩小动画
  const zoomOut = () => {
    Animated.timing(scaleValue, {
      toValue: 0.5, // 缩小到0.5倍大小
      duration: 1000,
      useNativeDriver: true,
    }).start();
  };
  // 恢复原始大小
  const resetScale = () => {
    Animated.timing(scaleValue, {
      toValue: 1, // 恢复到原始大小
      duration: 500,
      useNativeDriver: true,
    }).start();
  };
  // 脉冲动画(循环放大缩小)
  const pulse = () => {
    Animated.loop(
      Animated.sequence([
        Animated.timing(scaleValue, {
          toValue: 1.2,
          duration: 500,
          useNativeDriver: true,
        }),
        Animated.timing(scaleValue, {
          toValue: 0.8,
          duration: 500,
          useNativeDriver: true,
        }),
        Animated.timing(scaleValue, {
          toValue: 1,
          duration: 500,
          useNativeDriver: true,
        }),
      ])
    ).start();
  };
  // 停止所有动画
  const stopAnimation = () => {
    scaleValue.stopAnimation();
  };
  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.controls}>
        <Button title="放大" onPress={zoomIn} />
        <Button title="缩小" onPress={zoomOut} />
        <Button title="恢复" onPress={resetScale} />
        <Button title="脉冲" onPress={pulse} />
        <Button title="停止" onPress={stopAnimation} color="red" />
      </View>
      {/* 缩放视图 */}
      <Animated.View
        style={[
          styles.box,
          {
            transform: [{ scale: scaleValue }],
          },
        ]}
      >
        <Text style={styles.text}>缩放动画</Text>
      </Animated.View>
    </SafeAreaView>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
  },
  controls: {
    marginBottom: 20,
    width: "80%",
    flexDirection: "row",
    justifyContent: "space-around",
    flexWrap: "wrap",
  },
  box: {
    width: 150,
    height: 150,
    backgroundColor: "blue",
    justifyContent: "center",
    alignItems: "center",
    borderRadius: 10,
  },
  text: {
    color: "white",
    fontSize: 18,
  },
});
export default App;

渐变

渐变透明度 – 淡入/淡出

import React, { useState } from "react";
import {
  Animated,
  Button,
  SafeAreaView,
  StyleSheet,
  Text,
  View,
} from "react-native";
const App: React.FC = () => {
  // 透明度动画值
  const fadeAnim = useState(new Animated.Value(0))[0]; // 初始值为0(完全透明)
  // 淡入函数
  const fadeIn = () => {
    Animated.timing(fadeAnim, {
      toValue: 1, // 目标值为1(完全不透明)
      duration: 1500, // 动画持续时间(毫秒)
      useNativeDriver: true,
    }).start();
  };
  // 淡出函数
  const fadeOut = () => {
    Animated.timing(fadeAnim, {
      toValue: 0, // 目标值为0(完全透明)
      duration: 1500,
      useNativeDriver: true,
    }).start();
  };
  // 淡入淡出循环函数
  const fadeInOutLoop = () => {
    Animated.loop(
      Animated.sequence([
        Animated.timing(fadeAnim, {
          toValue: 1,
          duration: 1500,
          useNativeDriver: true,
        }),
        Animated.timing(fadeAnim, {
          toValue: 0,
          duration: 1500,
          useNativeDriver: true,
        }),
      ])
    ).start();
  };
  // 停止动画
  const stopAnimation = () => {
    fadeAnim.stopAnimation();
  };
  // 重置动画
  const resetAnimation = () => {
    stopAnimation();
    fadeAnim.setValue(0);
  };
  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.controls}>
        <Button title="淡入" onPress={fadeIn} />
        <Button title="淡出" onPress={fadeOut} />
        <Button title="循环淡入淡出" onPress={fadeInOutLoop} />
        <Button title="停止" onPress={stopAnimation} />
        <Button title="重置" onPress={resetAnimation} color="red" />
      </View>
      {/* 淡入淡出视图 */}
      <Animated.View
        style={[
          styles.box,
          {
            opacity: fadeAnim, // 绑定透明度动画值
          },
        ]}
      >
        <Text style={styles.text}>淡入淡出示例</Text>
      </Animated.View>
    </SafeAreaView>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
  },
  controls: {
    marginBottom: 20,
    width: "80%",
    flexDirection: "row",
    justifyContent: "space-around",
    flexWrap: "wrap",
  },
  box: {
    width: 200,
    height: 200,
    backgroundColor: "blue",
    justifyContent: "center",
    alignItems: "center",
    borderRadius: 10,
    marginTop: 20,
  },
  text: {
    color: "white",
    fontSize: 20,
    fontWeight: "bold",
  },
});
export default App;

渐变背景色

import React, { useState } from "react";
import {
  Animated,
  Button,
  SafeAreaView,
  StyleSheet,
  Text,
  View,
} from "react-native";
const App: React.FC = () => {
  // 渐变动画值
  const gradientAnim = useState(new Animated.Value(0))[0];
  // 开始渐变动画
  const startGradient = () => {
    Animated.loop(
      Animated.sequence([
        Animated.timing(gradientAnim, {
          toValue: 1,
          duration: 3000,
          useNativeDriver: true,
        }),
        Animated.timing(gradientAnim, {
          toValue: 0,
          duration: 3000,
          useNativeDriver: true,
        }),
      ])
    ).start();
  };
  // 停止渐变动画
  const stopGradient = () => {
    gradientAnim.stopAnimation();
  };
  // 重置渐变动画
  const resetGradient = () => {
    stopGradient();
    gradientAnim.setValue(0);
  };
  // 背景色插值 - 从蓝色渐变到绿色再到红色
  const backgroundColor = gradientAnim.interpolate({
    inputRange: [0, 0.5, 1],
    outputRange: [
      "rgba(0, 122, 255, 1)", // 蓝色
      "rgba(76, 217, 100, 1)", // 绿色
      "rgba(255, 59, 48, 1)", // 红色
    ],
  });
  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.controls}>
        <Button title="开始渐变" onPress={startGradient} />
        <Button title="停止渐变" onPress={stopGradient} />
        <Button title="重置" onPress={resetGradient} color="red" />
      </View>
      {/* 渐变背景色视图 */}
      <Animated.View
        style={[
          styles.box,
          {
            backgroundColor,
          },
        ]}
      >
        <Text style={styles.text}>渐变背景色示例</Text>
      </Animated.View>
    </SafeAreaView>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
  },
  controls: {
    marginBottom: 20,
    width: "80%",
    flexDirection: "row",
    justifyContent: "space-around",
    flexWrap: "wrap",
  },
  box: {
    width: 250,
    height: 250,
    justifyContent: "center",
    alignItems: "center",
    borderRadius: 15,
  },
  text: {
    color: "white",
    fontSize: 22,
    fontWeight: "bold",
    textAlign: "center",
  },
});
export default App;

二维(矢量)动画 Animated.ValueXY

适合需要同时控制 x 和 y 坐标的场景

// 创建一个初始位置为 {x: 0, y: 0} 的 Animated.ValueXY
  const position = useRef(new Animated.ValueXY()).current;

  const moveSquare = () => {
    // 同时对 x 和 y 进行动画
    Animated.timing(position, {
      toValue: { x: 200, y: 100 },
      duration: 1000,
      useNativeDriver: true,
    }).start();
  };
<Animated.View
        style={[
          { transform: position.getTranslateTransform() } // 应用平移变换,返回 [{translateX: x}, {translateY: y}]
        ]}
      />

      <Animated.View
        style={[{ marginLeft: position.x, marginTop: position.y }]}
      />

动画函数

动画类型 适用场景 示例
timing 平滑过渡(如淡入淡出、平移) 淡入淡出、缩放
spring 弹性效果(如按钮点击反馈) 弹跳、弹性拖拽
decay 惯性滚动(如列表松手后继续滚动) 滚动列表、滑动控件
parallel 多属性同时动画(如边淡入边缩放) 组合动画
sequence 按顺序执行动画(如步骤引导) 依次显示多个元素
stagger 交错动画(如网格元素依次出现) 瀑布流加载、菜单展开
loop 循环动画(如加载指示器) 旋转图标、呼吸效果

平滑动画 Animated.timing()

参数名 类型 描述 默认值
toValue number 动画的目标值 必需
duration number 动画持续时间(毫秒) 500
easing function 缓动函数,控制动画的速度曲线(如加速、减速、弹跳等) Easing.inOut(Easing.ease)
delay number 动画延迟开始的时间(毫秒) 0
isInteraction boolean 标记动画是否为用户交互的结果(影响动画优先级) true
useNativeDriver boolean 是否使用原生驱动(性能优化)。 false

缓动函数 easing

可通过 https://easings.net/ 可视化查看动画效果

在这里插入图片描述

easing: Animated.Easing.ease, // 先加速后减速(默认)

取值有:

  • Animated.Easing.ease:先加速后减速(默认),比较平缓
  • Animated.Easing.linear:匀速运动 – 速度曲线为一次方函数(即直线)
  • Animated.Easing.quad:速度曲线为二次方函数(即抛物线)
  • Animated.Easing.cubic:速度曲线为三次方函数(比抛物线更陡)
  • Animated.Easing.sin:速度曲线为正弦曲线
  • Animated.Easing.exp:速度曲线为指数曲线
  • Animated.Easing.circle:速度曲线为环形(即圆)
  • Animated.Easing.bounce:弹跳效果(不会超过最终位置,类似弹力球落地撞回,回弹高度不断衰减,直到停止)
  • Animated.Easing.elastic(amplitude):弹性效果
    • amplitude 为可选参数,表示振幅,默认值为 1
  • Animated.Easing.in(EasingFunc):加速,将参数(缓动函数)的速度曲线应用于动画的开始阶段
  • Animated.Easing.out(EasingFunc):减速,将参数(缓动函数)的速度曲线应用于动画的结束阶段
  • Animated.Easing.inOut(type):先加速后减速,将参数(缓动函数)的速度曲线应用于动画的开始和结束阶段
  • Animated.Easing.back(overshoot):后拉特效
    • overshoot 为可选参数,表示 “超出” 的程度,默认值为 1.70158,值越大,超出的距离越远,回弹效果越明显,推荐值为 3
  • Animated.Easing.bezier(x1, y1, x2, y2) 创建自定义的贝塞尔曲线缓动函数
    • x1, y1:第一个控制点的坐标

    • x2, y2:第二个控制点的坐标

      可通过下方链接,可视化调节曲线,获取到目标参数值
      https://cubic-bezier.com/

在这里插入图片描述

弹性动画 Animated.spring()

模拟真实世界中弹簧的物理行为,包括弹性、阻尼和质量

使用场景

  • 交互反馈:按钮点击、拖拽释放后的回弹效果
  • 导航转换:页面切换时的弹性过渡
  • 加载指示器:使用弹簧动画创建弹性加载效果
  • 微交互:如点赞、收藏等状态变化的反馈动画
Animated.spring(scaleAnim, {
        toValue: 1,
        friction: 2, // 摩擦力(阻尼),值越小弹性越大
        tension: 30, // 张力(弹性),值越大弹簧越硬
        useNativeDriver: false,
      }).start(),

共三种配置方式,只能选一种!

配置方式一:bounciness + speed

  • bounciness(弹性): 越大弹的幅度越大,默认值8
  • speed(速度): 越大弹得越快,默认值12

配置方式二:tension + friction

  • tension(张力):控制速度,越大速度越快,默认值40
  • friction(摩擦):控制能量耗散的速度,决定弹簧停止振动的快慢,越小弹得越久,默认值7

配置方式三:tension + friction

  • stiffness(刚度): 弹簧刚度系数,越大越弹,默认为100
  • damping(阻尼): 弹簧运动因摩擦力而受到阻尼,越小越弹,默认值10
  • mass(质量): 附着在弹末端的物体的质量,越大惯性越大,动画越难停下,越小惯性越小,动画很快停下,默认值1

其他参数

  • velocity(速度): 附着在弹上物体的初始速度,默认值0
  • overshootClamping(过冲): 弹是否应夹紧而不应弹跳,默认为false
  • restDisplacementThreshold(恢复位移阈值): 从静止状态开始的位移阈值,低于该阈值,弹簧应被视为静止状态,默认为0.001
  • restspeedthreshold(弹簧静止速度),单位为像素/秒,默认为0.001
  • delay(延迟):延迟后启动动画,默认为0

衰减动画 Animated.decay()

用于滚动或拖拽结束后的惯性效果。

const scrollAnim = new Animated.Value(0);

Animated.decay(scrollAnim, {
  velocity: 1,      // 初始速度(必选)
  deceleration: 0.997, // 减速系数
  useNativeDriver: false,
}).start();

deceleration 的值越小,则衰减越快,动画越快结束。

组合动画

方法 执行方式 中断行为 适用场景
parallel 并行执行 默认全部中断 多属性同步变化、多元素协同动画
sequence 按顺序执行 中断当前,后续不执行 分步动画、依赖关系动画
stagger 错开时间并行执行 默认全部中断 级联效果、列表项依次出现
loop 无限循环执行 手动停止 加载动画、背景循环效果

同时执行 Animated.parallel

Animated.parallel([
  Animated.timing(opacity, { toValue: 1 }),    // 淡入
  Animated.spring(scale, { toValue: 1.5 }),   // 缩放
  Animated.timing(rotation, { toValue: 1 }),  // 旋转
]).start();

顺序执行 Animated.sequence

Animated.sequence([
  Animated.timing(opacity, { toValue: 1 }),    // 第一步:淡入
  Animated.timing(position, { toValue: { x: 100, y: 0 } }),  // 第二步:平移
  Animated.spring(scale, { toValue: 0.8 }),   // 第三步:缩放回弹
]).start();

错开执行 Animated.stagger

Animated.stagger(100, [  // 每个动画间隔100ms
  Animated.timing(items[0].opacity, { toValue: 1 }),
  Animated.timing(items[1].opacity, { toValue: 1 }),
  Animated.timing(items[2].opacity, { toValue: 1 }),
]).start();

循环执行 Animated.loop

Animated.loop(
  Animated.sequence([
    Animated.timing(rotation, { toValue: 1, duration: 2000 }),
    Animated.timing(rotation, { toValue: 0, duration: 2000 }),
  ])
).start();

动画控制

延迟执行 Animated.delay

Animated.delay(1000); // 创建一个1000ms(1秒)的延迟

Animated.sequence([
  Animated.delay(500),             // 先等待500ms
  Animated.timing(value1, { ... }), // 然后执行第一个动画
  Animated.delay(300),             // 再等待300ms
  Animated.timing(value2, { ... }), // 最后执行第二个动画
]).start();

布局动画 LayoutAnimation

适用于自动给布局变化添加动画,特别丝滑好用

Android 中需手动开启布局动画

import { LayoutAnimation, UIManager } from 'react-native';

// 启用布局动画(仅 Android 需要)
if (Platform.OS === 'android') {
  UIManager.setLayoutAnimationEnabledExperimental(true);
}

预设动画效果

  1. LayoutAnimation.Presets.linear

    • 匀速变化
    • 持续时间:500ms

  2. LayoutAnimation.Presets.easeInEaseOut

    • 先慢后快再慢
    • 持续时间:300ms

  3. LayoutAnimation.Presets.spring

    • 带有弹性效果
    • 持续时间:700ms
    • 弹性参数:阻尼系数 0.5,初始速度 0.8

自定义动画配置

LayoutAnimation.configureNext(
  LayoutAnimation.create(
    500,                         // 动画持续时间(毫秒)
    LayoutAnimation.Types.easeInEaseOut, // 动画类型
    LayoutAnimation.Properties.opacity  // 要动画的属性
  )
);

支持的动画类型:spring、linear、easeInEaseOut、easeIn、easeOut
支持的动画属性:opacity、scaleXY、translate、width、height

实战范例:显隐组件

import React, { useState } from 'react';
import {
  Button,
  LayoutAnimation,
  StyleSheet,
  UIManager,
  View,
  Platform,
} from 'react-native';

// 启用布局动画(仅 Android 需要)
if (Platform.OS === 'android') {
  UIManager.setLayoutAnimationEnabledExperimental(true);
}

const LayoutAnimationExample = () => {
  const [showBox, setShowBox] = useState(true);

  const toggleBox = () => {
    // 配置下一次布局更新的动画
    LayoutAnimation.configureNext(LayoutAnimation.Presets.spring);
    
    // 修改状态,触发布局更新
    setShowBox(!showBox);
  };

  return (
    <View style={styles.container}>
      <Button title="切换显示/隐藏" onPress={toggleBox} />
      
      {showBox && (
        <View style={styles.box} />
      )}
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    padding: 20,
  },
  box: {
    width: 200,
    height: 200,
    backgroundColor: 'blue',
    borderRadius: 10,
    marginTop: 20,
  },
});

export default LayoutAnimationExample;

实战范例


网站公告

今日签到

点亮在社区的每一天
去签到