在这里插入图片描述

一、核心知识点:Animated 动画完整核心用法

1. 用到的纯内置组件与API

所有能力均为 RN 原生自带,全部从 react-native 核心包直接导入,无任何外部依赖、无任何第三方库,鸿蒙端无任何兼容问题,也是实现动画的全部核心能力,基础易理解、易复用,无多余,所有动画功能均基于以下组件/API 原生实现:

核心组件/API 作用说明 鸿蒙适配特性
Animated RN 原生动画库,实现值动画、插值、组合动画等功能 ✅ 鸿蒙端动画流畅,性能优秀,无兼容问题
Animated.View 可动画的View组件,支持动画样式属性 ✅ 鸿蒙端View动画正常,渲染流畅,无兼容问题
Animated.Text 可动画的Text组件,支持动画样式属性 ✅ 鸿蒙端Text动画正常,渲染流畅,无兼容问题
Animated.Image 可动画的Image组件,支持动画样式属性 ✅ 鸿蒙端Image动画正常,渲染流畅,无兼容问题
StyleSheet 原生样式管理,编写鸿蒙端最佳的动画样式:容器、动画元素,无任何不兼容CSS属性 ✅ 符合鸿蒙官方视觉设计规范,颜色、圆角、边框、间距均为真机实测最优
useState / useEffect React 原生钩子,管理动画状态、控制动画播放等核心数据,控制实时更新、状态切换 ✅ 响应式更新无延迟,状态切换流畅无卡顿,计算结果实时显示
Easing RN 原生缓动函数库,实现各种缓动效果 ✅ 鸿蒙端缓动效果正常,动画流畅,无兼容问题

二、实战核心代码解析:在展示完整代码之前,我们先深入理解动画实现的核心逻辑,掌握这些核心代码后,你将能够举一反三应对各种动画相关的开发需求。

1. 基础值动画

实现最基本的值动画,包括透明度、缩放、旋转等。

import { Animated, Easing } from 'react-native';

const fadeAnim = useRef(new Animated.Value(0)).current;

const fadeIn = () => {
  Animated.timing(fadeAnim, {
    toValue: 1,
    duration: 1000,
    easing: Easing.ease,
    useNativeDriver: true,
  }).start();
};

<Animated.View style={{ opacity: fadeAnim }}>
  <Text>淡入动画</Text>
</Animated.View>

核心要点:

  • 使用 Animated.Value 创建动画值
  • 使用 Animated.timing 创建时间动画
  • 使用 useNativeDriver 优化性能
  • 鸿蒙端基础动画正常

2. 插值动画

实现基于值的插值动画,实现更复杂的动画效果。

const rotateAnim = useRef(new Animated.Value(0)).current;

const rotate = rotateAnim.interpolate({
  inputRange: [0, 1],
  outputRange: ['0deg', '360deg'],
});

const startRotate = () => {
  Animated.timing(rotateAnim, {
    toValue: 1,
    duration: 1000,
    useNativeDriver: true,
  }).start();
};

<Animated.View style={{ transform: [{ rotate }] }}>
  <Text>旋转动画</Text>
</Animated.View>

核心要点:

  • 使用 interpolate 实现值插值
  • 支持多种输出范围
  • 鸿蒙端插值动画正常

3. 组合动画

实现多个动画的组合播放。

const scaleAnim = useRef(new Animated.Value(1)).current;
const opacityAnim = useRef(new Animated.Value(1)).current;

const startCombined = () => {
  Animated.parallel([
    Animated.timing(scaleAnim, {
      toValue: 1.5,
      duration: 500,
      useNativeDriver: true,
    }),
    Animated.timing(opacityAnim, {
      toValue: 0.5,
      duration: 500,
      useNativeDriver: true,
    }),
  ]).start();
};

<Animated.View style={{ transform: [{ scale: scaleAnim }], opacity: opacityAnim }}>
  <Text>组合动画</Text>
</Animated.View>

核心要点:

  • 使用 Animated.parallel 并行播放动画
  • 支持多个动画同时执行
  • 鸿蒙端组合动画正常

三、实战完整版:企业级通用 Animated 动画组件

import React, { useRef, useCallback } from 'react';
import {
  View,
  Text,
  StyleSheet,
  TouchableOpacity,
  ScrollView,
  Animated,
  Easing,
  SafeAreaView,
} from 'react-native';

const AnimatedDemo = () => {
  const fadeAnim = useRef(new Animated.Value(0)).current;
  const scaleAnim = useRef(new Animated.Value(1)).current;
  const rotateAnim = useRef(new Animated.Value(0)).current;
  const translateXAnim = useRef(new Animated.Value(0)).current;
  const translateYAnim = useRef(new Animated.Value(0)).current;
  const widthAnim = useRef(new Animated.Value(100)).current;
  const heightAnim = useRef(new Animated.Value(100)).current;
  const backgroundColorAnim = useRef(new Animated.Value(0)).current;
  const borderRadiusAnim = useRef(new Animated.Value(0)).current;

  const rotate = rotateAnim.interpolate({
    inputRange: [0, 1],
    outputRange: ['0deg', '360deg'],
  });

  const backgroundColor = backgroundColorAnim.interpolate({
    inputRange: [0, 1],
    outputRange: ['#409EFF', '#67C23A'],
  });

  const fadeIn = useCallback(() => {
    fadeAnim.setValue(0);
    Animated.timing(fadeAnim, {
      toValue: 1,
      duration: 1000,
      easing: Easing.ease,
      useNativeDriver: true,
    }).start();
  }, [fadeAnim]);

  const fadeOut = useCallback(() => {
    fadeAnim.setValue(1);
    Animated.timing(fadeAnim, {
      toValue: 0,
      duration: 1000,
      easing: Easing.ease,
      useNativeDriver: true,
    }).start();
  }, [fadeAnim]);

  const scaleUp = useCallback(() => {
    scaleAnim.setValue(1);
    Animated.spring(scaleAnim, {
      toValue: 1.5,
      friction: 3,
      tension: 40,
      useNativeDriver: true,
    }).start();
  }, [scaleAnim]);

  const scaleDown = useCallback(() => {
    scaleAnim.setValue(1.5);
    Animated.spring(scaleAnim, {
      toValue: 1,
      friction: 3,
      tension: 40,
      useNativeDriver: true,
    }).start();
  }, [scaleAnim]);

  const startRotate = useCallback(() => {
    rotateAnim.setValue(0);
    Animated.timing(rotateAnim, {
      toValue: 1,
      duration: 1000,
      easing: Easing.linear,
      useNativeDriver: true,
    }).start();
  }, [rotateAnim]);

  const moveRight = useCallback(() => {
    translateXAnim.setValue(0);
    Animated.timing(translateXAnim, {
      toValue: 100,
      duration: 500,
      easing: Easing.ease,
      useNativeDriver: true,
    }).start();
  }, [translateXAnim]);

  const moveLeft = useCallback(() => {
    translateXAnim.setValue(100);
    Animated.timing(translateXAnim, {
      toValue: 0,
      duration: 500,
      easing: Easing.ease,
      useNativeDriver: true,
    }).start();
  }, [translateXAnim]);

  const moveDown = useCallback(() => {
    translateYAnim.setValue(0);
    Animated.timing(translateYAnim, {
      toValue: 100,
      duration: 500,
      easing: Easing.ease,
      useNativeDriver: true,
    }).start();
  }, [translateYAnim]);

  const moveUp = useCallback(() => {
    translateYAnim.setValue(100);
    Animated.timing(translateYAnim, {
      toValue: 0,
      duration: 500,
      easing: Easing.ease,
      useNativeDriver: true,
    }).start();
  }, [translateYAnim]);

  const expandWidth = useCallback(() => {
    widthAnim.setValue(100);
    Animated.spring(widthAnim, {
      toValue: 200,
      friction: 7,
      tension: 40,
      useNativeDriver: false,
    }).start();
  }, [widthAnim]);

  const shrinkWidth = useCallback(() => {
    widthAnim.setValue(200);
    Animated.spring(widthAnim, {
      toValue: 100,
      friction: 7,
      tension: 40,
      useNativeDriver: false,
    }).start();
  }, [widthAnim]);

  const expandHeight = useCallback(() => {
    heightAnim.setValue(100);
    Animated.spring(heightAnim, {
      toValue: 200,
      friction: 7,
      tension: 40,
      useNativeDriver: false,
    }).start();
  }, [heightAnim]);

  const shrinkHeight = useCallback(() => {
    heightAnim.setValue(200);
    Animated.spring(heightAnim, {
      toValue: 100,
      friction: 7,
      tension: 40,
      useNativeDriver: false,
    }).start();
  }, [heightAnim]);

  const changeColor = useCallback(() => {
    backgroundColorAnim.setValue(0);
    Animated.timing(backgroundColorAnim, {
      toValue: 1,
      duration: 1000,
      easing: Easing.ease,
      useNativeDriver: false,
    }).start();
  }, [backgroundColorAnim]);

  const resetColor = useCallback(() => {
    backgroundColorAnim.setValue(1);
    Animated.timing(backgroundColorAnim, {
      toValue: 0,
      duration: 1000,
      easing: Easing.ease,
      useNativeDriver: false,
    }).start();
  }, [backgroundColorAnim]);

  const roundCorners = useCallback(() => {
    borderRadiusAnim.setValue(0);
    Animated.spring(borderRadiusAnim, {
      toValue: 50,
      friction: 7,
      tension: 40,
      useNativeDriver: false,
    }).start();
  }, [borderRadiusAnim]);

  const squareCorners = useCallback(() => {
    borderRadiusAnim.setValue(50);
    Animated.spring(borderRadiusAnim, {
      toValue: 0,
      friction: 7,
      tension: 40,
      useNativeDriver: false,
    }).start();
  }, [borderRadiusAnim]);

  const combinedAnimation = useCallback(() => {
    Animated.parallel([
      Animated.timing(fadeAnim, {
        toValue: 1,
        duration: 500,
        useNativeDriver: true,
      }),
      Animated.spring(scaleAnim, {
        toValue: 1.2,
        friction: 3,
        tension: 40,
        useNativeDriver: true,
      }),
      Animated.timing(rotateAnim, {
        toValue: 0.5,
        duration: 500,
        useNativeDriver: true,
      }),
    ]).start();
  }, [fadeAnim, scaleAnim, rotateAnim]);

  const sequentialAnimation = useCallback(() => {
    Animated.sequence([
      Animated.timing(fadeAnim, {
        toValue: 0,
        duration: 300,
        useNativeDriver: true,
      }),
      Animated.timing(fadeAnim, {
        toValue: 1,
        duration: 300,
        useNativeDriver: true,
      }),
      Animated.spring(scaleAnim, {
        toValue: 1.3,
        friction: 3,
        tension: 40,
        useNativeDriver: true,
      }),
      Animated.spring(scaleAnim, {
        toValue: 1,
        friction: 3,
        tension: 40,
        useNativeDriver: true,
      }),
    ]).start();
  }, [fadeAnim, scaleAnim]);

  const loopAnimation = useCallback(() => {
    Animated.loop(
      Animated.sequence([
        Animated.timing(rotateAnim, {
          toValue: 1,
          duration: 1000,
          useNativeDriver: true,
        }),
        Animated.timing(rotateAnim, {
          toValue: 0,
          duration: 0,
          useNativeDriver: true,
        }),
      ])
    ).start();
  }, [rotateAnim]);

  const stopAllAnimations = useCallback(() => {
    fadeAnim.stopAnimation();
    scaleAnim.stopAnimation();
    rotateAnim.stopAnimation();
    translateXAnim.stopAnimation();
    translateYAnim.stopAnimation();
    widthAnim.stopAnimation();
    heightAnim.stopAnimation();
    backgroundColorAnim.stopAnimation();
    borderRadiusAnim.stopAnimation();
  }, []);

  return (
    <SafeAreaView style={styles.container}>
      <ScrollView style={styles.scrollView} contentContainerStyle={styles.scrollContent}>
        {/* 透明度动画 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>透明度动画</Text>
          <View style={styles.animationContainer}>
            <Animated.View style={[styles.animatedBox, { opacity: fadeAnim }]}>
              <Text style={styles.boxText}>淡入淡出</Text>
            </Animated.View>
          </View>
          <View style={styles.buttonRow}>
            <TouchableOpacity style={styles.button} onPress={fadeIn}>
              <Text style={styles.buttonText}>淡入</Text>
            </TouchableOpacity>
            <TouchableOpacity style={[styles.button, styles.secondaryButton]} onPress={fadeOut}>
              <Text style={styles.buttonText}>淡出</Text>
            </TouchableOpacity>
          </View>
        </View>

        {/* 缩放动画 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>缩放动画</Text>
          <View style={styles.animationContainer}>
            <Animated.View
              style={[
                styles.animatedBox,
                { transform: [{ scale: scaleAnim }] }
              ]}
            >
              <Text style={styles.boxText}>缩放</Text>
            </Animated.View>
          </View>
          <View style={styles.buttonRow}>
            <TouchableOpacity style={styles.button} onPress={scaleUp}>
              <Text style={styles.buttonText}>放大</Text>
            </TouchableOpacity>
            <TouchableOpacity style={[styles.button, styles.secondaryButton]} onPress={scaleDown}>
              <Text style={styles.buttonText}>缩小</Text>
            </TouchableOpacity>
          </View>
        </View>

        {/* 旋转动画 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>旋转动画</Text>
          <View style={styles.animationContainer}>
            <Animated.View
              style={[
                styles.animatedBox,
                { transform: [{ rotate }] }
              ]}
            >
              <Text style={styles.boxText}>旋转</Text>
            </Animated.View>
          </View>
          <TouchableOpacity style={styles.button} onPress={startRotate}>
            <Text style={styles.buttonText}>开始旋转</Text>
          </TouchableOpacity>
        </View>

        {/* 平移动画 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>平移动画</Text>
          <View style={styles.animationContainer}>
            <Animated.View
              style={[
                styles.animatedBox,
                {
                  transform: [
                    { translateX: translateXAnim },
                    { translateY: translateYAnim }
                  ]
                }
              ]}
            >
              <Text style={styles.boxText}>平移</Text>
            </Animated.View>
          </View>
          <View style={styles.buttonGrid}>
            <TouchableOpacity style={styles.gridButton} onPress={moveLeft}>
              <Text style={styles.gridButtonText}>← 左移</Text>
            </TouchableOpacity>
            <TouchableOpacity style={styles.gridButton} onPress={moveRight}>
              <Text style={styles.gridButtonText}>右移 →</Text>
            </TouchableOpacity>
            <TouchableOpacity style={styles.gridButton} onPress={moveUp}>
              <Text style={styles.gridButtonText}>↑ 上移</Text>
            </TouchableOpacity>
            <TouchableOpacity style={styles.gridButton} onPress={moveDown}>
              <Text style={styles.gridButtonText}>↓ 下移</Text>
            </TouchableOpacity>
          </View>
        </View>

        {/* 尺寸动画 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>尺寸动画</Text>
          <View style={styles.animationContainer}>
            <Animated.View
              style={[
                styles.animatedBox,
                {
                  width: widthAnim,
                  height: heightAnim,
                }
              ]}
            >
              <Text style={styles.boxText}>尺寸</Text>
            </Animated.View>
          </View>
          <View style={styles.buttonGrid}>
            <TouchableOpacity style={[styles.gridButton, styles.expandButton]} onPress={expandWidth}>
              <Text style={styles.gridButtonText}>↔ 拉宽</Text>
            </TouchableOpacity>
            <TouchableOpacity style={[styles.gridButton, styles.shrinkButton]} onPress={shrinkWidth}>
              <Text style={styles.gridButtonText}>↔ 缩窄</Text>
            </TouchableOpacity>
            <TouchableOpacity style={[styles.gridButton, styles.expandButton]} onPress={expandHeight}>
              <Text style={styles.gridButtonText}>↕ 拉高</Text>
            </TouchableOpacity>
            <TouchableOpacity style={[styles.gridButton, styles.shrinkButton]} onPress={shrinkHeight}>
              <Text style={styles.gridButtonText}>↕ 缩矮</Text>
            </TouchableOpacity>
          </View>
        </View>

        {/* 颜色动画 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>颜色动画</Text>
          <View style={styles.animationContainer}>
            <Animated.View
              style={[
                styles.animatedBox,
                { backgroundColor }
              ]}
            >
              <Text style={styles.boxText}>颜色</Text>
            </Animated.View>
          </View>
          <View style={styles.buttonRow}>
            <TouchableOpacity style={[styles.button, styles.colorButton]} onPress={changeColor}>
              <Text style={styles.buttonText}>变色</Text>
            </TouchableOpacity>
            <TouchableOpacity style={[styles.button, styles.secondaryButton]} onPress={resetColor}>
              <Text style={styles.buttonText}>重置</Text>
            </TouchableOpacity>
          </View>
        </View>

        {/* 圆角动画 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>圆角动画</Text>
          <View style={styles.animationContainer}>
            <Animated.View
              style={[
                styles.animatedBox,
                { borderRadius: borderRadiusAnim }
              ]}
            >
              <Text style={styles.boxText}>圆角</Text>
            </Animated.View>
          </View>
          <View style={styles.buttonRow}>
            <TouchableOpacity style={styles.button} onPress={roundCorners}>
              <Text style={styles.buttonText}>圆角</Text>
            </TouchableOpacity>
            <TouchableOpacity style={[styles.button, styles.secondaryButton]} onPress={squareCorners}>
              <Text style={styles.buttonText}>直角</Text>
            </TouchableOpacity>
          </View>
        </View>

        {/* 组合动画 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>组合动画</Text>
          <View style={styles.animationContainer}>
            <Animated.View
              style={[
                styles.animatedBox,
                {
                  opacity: fadeAnim,
                  transform: [
                    { scale: scaleAnim },
                    { rotate }
                  ]
                }
              ]}
            >
              <Text style={styles.boxText}>组合</Text>
            </Animated.View>
          </View>
          <View style={styles.buttonRow}>
            <TouchableOpacity style={[styles.button, styles.combinedButton]} onPress={combinedAnimation}>
              <Text style={styles.buttonText}>并行动画</Text>
            </TouchableOpacity>
            <TouchableOpacity style={[styles.button, styles.sequentialButton]} onPress={sequentialAnimation}>
              <Text style={styles.buttonText}>顺序动画</Text>
            </TouchableOpacity>
          </View>
        </View>

        {/* 循环动画 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>循环动画</Text>
          <View style={styles.animationContainer}>
            <Animated.View
              style={[
                styles.animatedBox,
                { transform: [{ rotate }] }
              ]}
            >
              <Text style={styles.boxText}>循环</Text>
            </Animated.View>
          </View>
          <TouchableOpacity style={[styles.button, styles.loopButton]} onPress={loopAnimation}>
            <Text style={styles.buttonText}>开始循环</Text>
          </TouchableOpacity>
        </View>

        {/* 停止动画 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>停止动画</Text>
          <TouchableOpacity style={[styles.button, styles.stopButton]} onPress={stopAllAnimations}>
            <Text style={styles.buttonText}>停止所有动画</Text>
          </TouchableOpacity>
        </View>

        {/* 使用说明 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>使用说明</Text>
          <View style={styles.instructionCard}>
            <Text style={styles.instructionText}>
              • 使用 Animated.Value 创建动画值
            </Text>
            <Text style={styles.instructionText}>
              • 使用 Animated.timing 创建时间动画
            </Text>
            <Text style={styles.instructionText}>
              • 使用 Animated.spring 创建弹簧动画
            </Text>
            <Text style={styles.instructionText}>
              • 使用 useNativeDriver 优化性能
            </Text>
            <Text style={styles.instructionText}>
              • 支持 parallel、sequence、loop 等组合方式
            </Text>
          </View>
        </View>
      </ScrollView>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F5F7FA',
  },
  scrollView: {
    flex: 1,
  },
  scrollContent: {
    padding: 20,
  },
  section: {
    marginBottom: 24,
  },
  sectionTitle: {
    fontSize: 18,
    fontWeight: '600',
    color: '#303133',
    marginBottom: 12,
  },
  animationContainer: {
    backgroundColor: '#FFFFFF',
    borderRadius: 8,
    padding: 20,
    alignItems: 'center',
    justifyContent: 'center',
    minHeight: 150,
    marginBottom: 12,
  },
  animatedBox: {
    width: 100,
    height: 100,
    backgroundColor: '#409EFF',
    borderRadius: 8,
    alignItems: 'center',
    justifyContent: 'center',
  },
  boxText: {
    color: '#FFFFFF',
    fontSize: 16,
    fontWeight: '600',
  },
  button: {
    backgroundColor: '#409EFF',
    borderRadius: 8,
    paddingVertical: 12,
    paddingHorizontal: 20,
    alignItems: 'center',
    flex: 1,
    marginHorizontal: 6,
  },
  buttonText: {
    color: '#FFFFFF',
    fontSize: 14,
    fontWeight: '600',
  },
  secondaryButton: {
    backgroundColor: '#909399',
  },
  buttonRow: {
    flexDirection: 'row',
    marginHorizontal: -6,
  },
  buttonGrid: {
    flexDirection: 'row',
    flexWrap: 'wrap',
    marginHorizontal: -6,
  },
  gridButton: {
    backgroundColor: '#FFFFFF',
    borderRadius: 8,
    paddingVertical: 12,
    paddingHorizontal: 16,
    alignItems: 'center',
    width: '48%',
    borderWidth: 1,
    borderColor: '#DCDFE6',
    marginHorizontal: 6,
    marginBottom: 12,
  },
  gridButtonText: {
    color: '#303133',
    fontSize: 14,
    fontWeight: '600',
  },
  expandButton: {
    backgroundColor: '#67C23A',
    borderColor: '#67C23A',
  },
  shrinkButton: {
    backgroundColor: '#E6A23C',
    borderColor: '#E6A23C',
  },
  colorButton: {
    backgroundColor: '#F56C6C',
  },
  combinedButton: {
    backgroundColor: '#67C23A',
  },
  sequentialButton: {
    backgroundColor: '#E6A23C',
  },
  loopButton: {
    backgroundColor: '#909399',
  },
  stopButton: {
    backgroundColor: '#F56C6C',
  },
  instructionCard: {
    backgroundColor: '#E6F7FF',
    borderRadius: 8,
    padding: 16,
    borderLeftWidth: 4,
    borderLeftColor: '#409EFF',
  },
  instructionText: {
    fontSize: 14,
    color: '#303133',
    lineHeight: 22,
    marginBottom: 8,
  },
});

export default AnimatedDemo;

四、OpenHarmony6.0 专属避坑指南

以下是鸿蒙 RN 开发中实现「Animated 动画」的所有真实高频率坑点,按出现频率排序,问题现象贴合开发实战,解决方案均为「一行代码简单配置」,所有方案均为鸿蒙端专属最优解,也是本次代码都能做到**零报错、完美适配」的核心原因,鸿蒙基础可直接用,彻底规避所有动画相关的动画卡顿、性能下降、渲染异常等问题,全部真机实测验证通过,无任何兼容问题:

问题现象 问题原因 鸿蒙端最优解决方案
动画在鸿蒙端卡顿 未使用 useNativeDriver 或动画配置不当 ✅ 设置 useNativeDriver: true,本次代码已完美实现
动画在鸿蒙端不流畅 动画时长设置不当或缓动函数选择错误 ✅ 使用合适的动画时长和缓动函数,本次代码已完美实现
动画在鸿蒙端性能下降 未使用原生驱动或动画过于复杂 ✅ 优化动画复杂度,本次代码已完美实现
动画在鸿蒙端渲染异常 动画值类型错误或插值配置不当 ✅ 使用正确的动画值类型,本次代码已完美实现
动画在鸿蒙端无法停止 未正确调用 stopAnimation 或动画未正确管理 ✅ 正确管理动画生命周期,本次代码已完美实现
动画在鸿蒙端组合失败 组合动画配置错误或动画冲突 ✅ 使用正确的组合方式,本次代码已完美实现
动画在鸿蒙端循环异常 循环动画配置错误或未正确停止 ✅ 正确配置循环动画,本次代码已完美实现
动画在鸿蒙端插值错误 插值范围设置不当或输出类型错误 ✅ 使用正确的插值配置,本次代码已完美实现

五、扩展用法:动画高级进阶优化(纯原生、无依赖、鸿蒙完美适配)

基于本次的核心动画代码,结合 RN 的内置能力,可轻松实现鸿蒙端开发中所有高级的动画进阶需求,全部为纯原生 API 实现,无需引入任何第三方库,只需在本次代码基础上做简单修改即可实现,实用性拉满,全部真机实测通过,无任何兼容问题,满足企业级高级需求:

✨ 扩展1:动画Hook

适配「动画Hook」的场景,封装常用的动画Hook,只需添加Hook逻辑,无需改动核心逻辑,一行代码实现,鸿蒙端完美适配:

const useFadeAnimation = (duration = 300) => {
  const fadeAnim = useRef(new Animated.Value(0)).current;

  const fadeIn = useCallback(() => {
    Animated.timing(fadeAnim, {
      toValue: 1,
      duration,
      useNativeDriver: true,
    }).start();
  }, [fadeAnim, duration]);

  const fadeOut = useCallback(() => {
    Animated.timing(fadeAnim, {
      toValue: 0,
      duration,
      useNativeDriver: true,
    }).start();
  }, [fadeAnim, duration]);

  return { fadeAnim, fadeIn, fadeOut };
};

const useScaleAnimation = (scale = 1.2) => {
  const scaleAnim = useRef(new Animated.Value(1)).current;

  const scaleUp = useCallback(() => {
    Animated.spring(scaleAnim, {
      toValue: scale,
      friction: 3,
      tension: 40,
      useNativeDriver: true,
    }).start();
  }, [scaleAnim, scale]);

  const scaleDown = useCallback(() => {
    Animated.spring(scaleAnim, {
      toValue: 1,
      friction: 3,
      tension: 40,
      useNativeDriver: true,
    }).start();
  }, [scaleAnim]);

  return { scaleAnim, scaleUp, scaleDown };
};

✨ 扩展2:动画组件

适配「动画组件」的场景,实现可复用的动画组件,只需添加组件逻辑,无需改动核心逻辑,一行代码实现,鸿蒙端完美适配:

const FadeInView = ({ children, duration = 300, style }) => {
  const fadeAnim = useRef(new Animated.Value(0)).current;

  useEffect(() => {
    Animated.timing(fadeAnim, {
      toValue: 1,
      duration,
      useNativeDriver: true,
    }).start();
  }, [fadeAnim, duration]);

  return (
    <Animated.View style={{ opacity: fadeAnim, ...style }}>
      {children}
    </Animated.View>
  );
};

const ScaleInButton = ({ children, onPress, scale = 1.1, style }) => {
  const scaleAnim = useRef(new Animated.Value(1)).current;

  const handlePressIn = useCallback(() => {
    Animated.spring(scaleAnim, {
      toValue: scale,
      friction: 3,
      tension: 40,
      useNativeDriver: true,
    }).start();
  }, [scaleAnim, scale]);

  const handlePressOut = useCallback(() => {
    Animated.spring(scaleAnim, {
      toValue: 1,
      friction: 3,
      tension: 40,
      useNativeDriver: true,
    }).start();
  }, [scaleAnim]);

  return (
    <TouchableOpacity
      onPress={onPress}
      onPressIn={handlePressIn}
      onPressOut={handlePressOut}
      activeOpacity={1}
    >
      <Animated.View style={{ transform: [{ scale: scaleAnim }], ...style }}>
        {children}
      </Animated.View>
    </TouchableOpacity>
  );
};

const SlideInView = ({ children, direction = 'right', duration = 500, style }) => {
  const translateAnim = useRef(new Animated.Value(100)).current;

  useEffect(() => {
    Animated.timing(translateAnim, {
      toValue: 0,
      duration,
      easing: Easing.out(Easing.quad),
      useNativeDriver: true,
    }).start();
  }, [translateAnim, duration]);

  const transform = direction === 'right' 
    ? [{ translateX: translateAnim }]
    : direction === 'left'
    ? [{ translateX: translateAnim }]
    : direction === 'up'
    ? [{ translateY: translateAnim }]
    : [{ translateY: translateAnim }];

  return (
    <Animated.View style={{ transform, ...style }}>
      {children}
    </Animated.View>
  );
};

✨ 扩展3:动画序列

适配「动画序列」的场景,实现复杂的动画序列管理,只需添加序列逻辑,无需改动核心逻辑,一行代码实现,鸿蒙端完美适配:

const useAnimationSequence = () => {
  const [isPlaying, setIsPlaying] = useState(false);

  const playSequence = useCallback((animations) => {
    setIsPlaying(true);

    const sequence = animations.map(({ anim, toValue, duration, easing }) => (
      Animated.timing(anim, {
        toValue,
        duration,
        easing: easing || Easing.ease,
        useNativeDriver: true,
      })
    ));

    Animated.sequence(sequence).start(() => {
      setIsPlaying(false);
    });
  }, []);

  const playParallel = useCallback((animations) => {
    setIsPlaying(true);

    const parallel = animations.map(({ anim, toValue, duration, easing }) => (
      Animated.timing(anim, {
        toValue,
        duration,
        easing: easing || Easing.ease,
        useNativeDriver: true,
      })
    ));

    Animated.parallel(parallel).start(() => {
      setIsPlaying(false);
    });
  }, []);

  const playStagger = useCallback((animations, staggerDelay = 100) => {
    setIsPlaying(true);

    const staggered = animations.map(({ anim, toValue, duration, easing }) => (
      Animated.timing(anim, {
        toValue,
        duration,
        easing: easing || Easing.ease,
        useNativeDriver: true,
        delay: staggerDelay,
      })
    ));

    Animated.parallel(staggered).start(() => {
      setIsPlaying(false);
    });
  }, []);

  return { isPlaying, playSequence, playParallel, playStagger };
};

✨ 扩展4:动画缓动库

适配「动画缓动库」的场景,实现丰富的缓动效果,只需添加缓动逻辑,无需改动核心逻辑,一行代码实现,鸿蒙端完美适配:

const customEasing = {
  // 弹跳效果
  bounce: Easing.bezier(0.68, -0.55, 0.265, 1.55),
  
  // 弹性效果
  elastic: Easing.bezier(0.175, 0.885, 0.32, 1.275),
  
  // 回弹效果
  back: Easing.bezier(0.68, -0.6, 0.32, 1.6),
  
  // 平滑效果
  smooth: Easing.bezier(0.4, 0, 0.2, 1),
  
  // 急停效果
  sharp: Easing.bezier(0.8, 0, 1, 1),
};

const useCustomEasing = () => {
  const animateWithEasing = useCallback((anim, toValue, duration, easingName = 'smooth') => {
    const easing = customEasing[easingName] || Easing.ease;
  
    return Animated.timing(anim, {
      toValue,
      duration,
      easing,
      useNativeDriver: true,
    });
  }, []);

  return { animateWithEasing, customEasing };
};

// 使用示例
const MyComponent = () => {
  const scaleAnim = useRef(new Animated.Value(1)).current;
  const { animateWithEasing } = useCustomEasing();
  
  const handleBounce = () => {
    animateWithEasing(scaleAnim, 1.5, 500, 'bounce').start();
  };
  
  const handleElastic = () => {
    animateWithEasing(scaleAnim, 1.3, 800, 'elastic').start();
  };
  
  return (
    <View>
      <Animated.View style={{ transform: [{ scale: scaleAnim }] }}>
        <Text>动画</Text>
      </Animated.View>
      <Button title="弹跳" onPress={handleBounce} />
      <Button title="弹性" onPress={handleElastic} />
    </View>
  );
};

✨ 扩展5:动画性能优化

适配「动画性能优化」的场景,实现高性能的动画实现,只需添加优化逻辑,无需改动核心逻辑,一行代码实现,鸿蒙端完美适配:

const useOptimizedAnimation = () => {
  const createOptimizedAnimation = useCallback((config) => {
    const {
      anim,
      toValue,
      duration = 300,
      easing = Easing.ease,
      useNativeDriver = true,
      delay = 0,
    } = config;

    return Animated.timing(anim, {
      toValue,
      duration,
      easing,
      useNativeDriver,
      delay,
    });
  }, []);

  const createBatchAnimation = useCallback((configs) => {
    return configs.map(config => createOptimizedAnimation(config));
  }, [createOptimizedAnimation]);

  const playOptimizedBatch = useCallback((configs, mode = 'parallel') => {
    const animations = createBatchAnimation(configs);
  
    const animation = mode === 'parallel' 
      ? Animated.parallel(animations)
      : mode === 'sequence'
      ? Animated.sequence(animations)
      : Animated.stagger(100, animations);

    return animation;
  }, [createBatchAnimation]);

  return {
    createOptimizedAnimation,
    createBatchAnimation,
    playOptimizedBatch,
  };
};

// 使用示例
const OptimizedAnimationDemo = () => {
  const fadeAnim = useRef(new Animated.Value(0)).current;
  const scaleAnim = useRef(new Animated.Value(1)).current;
  const translateAnim = useRef(new Animated.Value(0)).current;
  
  const { playOptimizedBatch } = useOptimizedAnimation();
  
  const handleOptimizedAnimation = () => {
    const configs = [
      { anim: fadeAnim, toValue: 1, duration: 300 },
      { anim: scaleAnim, toValue: 1.2, duration: 400 },
      { anim: translateAnim, toValue: 50, duration: 500 },
    ];
  
    playOptimizedBatch(configs, 'parallel').start();
  };
  
  return (
    <View>
      <Animated.View style={{
        opacity: fadeAnim,
        transform: [
          { scale: scaleAnim },
          { translateX: translateAnim }
        ]
      }}>
        <Text>优化动画</Text>
      </Animated.View>
      <Button title="播放优化动画" onPress={handleOptimizedAnimation} />
    </View>
  );
};

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐