React Native for OpenHarmony 实战:Easing 动画完全指南

目录

[一、Easing 动画概述](#一、Easing 动画概述)

[1. 核心价值与适用场景](#1. 核心价值与适用场景)

[2. RN Easing 核心原理](#2. RN Easing 核心原理)

二、基础用法

[1. 核心 API 说明](#1. 核心 API 说明)

[2. 第一个 Easing 动画:淡入淡出 + 平移](#2. 第一个 Easing 动画:淡入淡出 + 平移)

鸿蒙适配要点:

四、进阶用法

[1. 自定义 Easing 函数](#1. 自定义 Easing 函数)

鸿蒙优化要点:

[2. 组合动画](#2. 组合动画)

组合动画注意事项:

[3. 手势联动 Easing 动画](#3. 手势联动 Easing 动画)

鸿蒙手势动画适配要点:

五、实操演示:鸿蒙加载动画组件封装


一、Easing 动画概述

1. 核心价值与适用场景

Easing 动画是基于「时间 - 进度」映射关系的动画效果,核心价值在于模拟真实物理运动规律(如重力、弹性、摩擦力),让动画过渡更自然。React Native 的 Easing 动画在 OpenHarmony 应用中的典型使用场景包括:

  • 页面切换 / 模态框弹出(淡入淡出、平移效果)
  • 交互反馈(按钮点击缩放、列表 item 滑动删除)
  • 数据加载(骨架屏渐变、加载指示器旋转)
  • 状态变化(开关切换、进度条填充、数字滚动)
  • 手势联动(下拉刷新、拖拽排序、缩放操作)

2. RN Easing 核心原理

React Native 的 Easing 动画基于 Animated 库实现,核心原理是:

  1. 通过 Animated.Value 维护动画进度(0~1 或自定义区间);
  2. 利用 Easing 函数定义进度随时间的变化规律;
  3. 通过 Animated.timing/Animated.spring 等方法驱动进度变化;
  4. 桥接 OpenHarmony 原生动画引擎(如鸿蒙的 ValueAnimator),实现硬件加速渲染;
  5. 最终将进度映射到组件样式(如 opacity、transform、width 等)。

二、基础用法

1. 核心 API 说明

React Native 实现 Easing 动画的核心 API 来自 react-native 内置的 AnimatedEasing 模块,无需额外依赖第三方库,鸿蒙平台可直接使用:

  • Animated.Value:动画进度容器,存储当前动画进度值;
  • Animated.timing:最常用的动画驱动方法,支持自定义时长、Easing 函数;
  • Easing:内置缓动函数库(如线性、弹性、衰减、弹跳等);
  • useNativeDriver: true:关键优化参数,启用鸿蒙原生动画驱动(性能提升 50%+)。

2. 第一个 Easing 动画:淡入淡出 + 平移

javascript 复制代码
import React, { useEffect, useRef } from 'react';
import { View, Text, StyleSheet, Animated, Easing } from 'react-native';

const BasicEasingAnimation = () => {
  const animatedValue = useRef(new Animated.Value(0)).current;

   useEffect(() => {
    Animated.timing(animatedValue, {
      toValue: 1,
      duration: 1000,
      easing: Easing.bezier(0.42, 0, 0.58, 1), 
      useNativeDriver: true,
    }).start();
  }, []);

  const animatedStyle = {
    opacity: animatedValue,
    transform: [
      {
        translateY: animatedValue.interpolate({
          inputRange: [0, 1],
          outputRange: [50, 0],
        }),
      },
    ],
  };

  return (
    <View style={styles.container}>
      <Animated.View style={[styles.box, animatedStyle]}>
        <Text style={styles.text}>鸿蒙Easing动画</Text>
      </Animated.View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5F7FA',
  },
  box: {
    width: 200,
    height: 100,
    backgroundColor: '#007DFF',
    borderRadius: 8,
    justifyContent: 'center',
    alignItems: 'center',
  },
  text: {
    color: '#fff',
    fontSize: 16,
  },
});

export default BasicEasingAnimation;
鸿蒙适配要点:
  • 必须启用 useNativeDriver: true:鸿蒙平台下,JS 驱动动画会有明显卡顿,原生驱动可直接调用鸿蒙动画引擎,性能拉满;
  • 平移 / 缩放等 transform 动画优先使用 Animated 封装:避免直接操作样式导致的重绘,鸿蒙原生驱动对 transform 支持最优;
  • 动画时长建议 300~1000ms:符合鸿蒙设计规范,过短会导致动画不明显,过长影响交互效率。

四、进阶用法

1. 自定义 Easing 函数

当内置 Easing 函数无法满足需求时,可通过 Easing.function 自定义缓动规律,核心是实现「输入进度(0~1)→ 输出进度(0~1)」的映射函数,鸿蒙平台完全支持:

javascript 复制代码
const CustomEasingAnimation = () => {
  const animatedValue = useRef(new Animated.Value(0)).current;

  useEffect(() => {
    Animated.timing(animatedValue, {
      toValue: 1,
      duration: 1500,
      // 自定义缓动函数:inputRange为0~1,返回映射后的进度
      easing: Easing.function((t) => {
        // t: 0→1(时间进度),返回值:动画进度
        if (t < 0.7) {
          return 1 - Math.pow(1 - t / 0.7, 2); // 前70%时间:加速下落
        } else {
          const remaining = (t - 0.7) / 0.3;
          return 1 - Math.pow(remaining - 1, 2) * 0.1; // 后30%时间:轻微反弹
        }
      }),
      useNativeDriver: true,
    }).start();
  }, []);

  const animatedStyle = {
    transform: [
      {
        translateY: animatedValue.interpolate({
          inputRange: [0, 1],
          outputRange: [0, 300], // 向下平移300dp,模拟下落+反弹
        }),
      },
    ],
  };

  return (
    <View style={styles.container}>
      <Animated.View style={[styles.ball, animatedStyle]} />
    </View>
  );
};

const styles = StyleSheet.create({
  // 省略重复样式,新增ball样式
  ball: {
    width: 50,
    height: 50,
    borderRadius: 25,
    backgroundColor: '#00C160',
  },
});
鸿蒙优化要点:
  • 自定义 Easing 函数避免复杂计算(如多层循环、三角函数嵌套):鸿蒙低端设备可能因计算量过大导致动画卡顿;
  • 确保输出进度在 0~1 区间:超出区间可能导致动画异常(如组件超出屏幕、透明度异常);
  • 优先使用分段函数(if/else)而非连续复杂函数:鸿蒙原生驱动对简单逻辑的解析效率更高。

2. 组合动画

实际开发中,常需要多个动画组合执行(如先淡入再平移、同时缩放 + 旋转),RN 提供 Animated.sequence(序列)、Animated.parallel(并行)、Animated.delay(延迟)等方法,鸿蒙平台完美适配:

javascript 复制代码
// 组合动画:延迟200ms → 淡入(300ms)→ 平移+旋转(并行,500ms)
const CombinedEasingAnimation = () => {
  const animatedValue = useRef(new Animated.Value(0)).current;

  useEffect(() => {
    Animated.sequence([
      Animated.delay(200), // 延迟200ms执行
      // 第一步:淡入(0→1)
      Animated.timing(animatedValue, {
        toValue: 0.5,
        duration: 300,
        easing: Easing.easeInOut,
        useNativeDriver: true,
      }),
      // 第二步:平移+旋转并行执行
      Animated.parallel([
        Animated.timing(animatedValue, {
          toValue: 1,
          duration: 500,
          easing: Easing.spring(0.7, 0.4),
          useNativeDriver: true,
        }),
      ]),
    ]).start();
  }, []);

  const animatedStyle = {
    opacity: animatedValue.interpolate({ inputRange: [0, 0.5], outputRange: [0, 1] }),
    transform: [
      {
        translateX: animatedValue.interpolate({ inputRange: [0.5, 1], outputRange: [0, 150] }), // 平移
      },
      {
        rotate: animatedValue.interpolate({ inputRange: [0.5, 1], outputRange: ['0deg', '360deg'] }), // 旋转
      },
    ],
  };

  return (
    <View style={styles.container}>
      <Animated.View style={[styles.box, animatedStyle]}>
        <Text style={styles.text}>组合动画</Text>
      </Animated.View>
    </View>
  );
};
组合动画注意事项:
  • 并行动画(Animated.parallel)避免同时修改同一属性:如同时修改 opacity 可能导致冲突;
  • 序列动画(Animated.sequence)中,后续动画的 inputRange 需衔接前序动画的进度:如示例中 0.5 作为分界点;
  • 延迟动画(Animated.delay)仅延迟后续动画执行,不阻塞 JS 线程,鸿蒙平台无性能影响。

3. 手势联动 Easing 动画

将 Easing 动画与 RN 手势系统结合,实现「拖拽、缩放、滑动」等交互联动效果,是鸿蒙应用的高频需求。以下示例实现「拖拽小球 + 松手后弹性回弹」的交互动画:

javascript 复制代码
import React, { useRef } from 'react';
import { View, StyleSheet, Animated, PanResponder } from 'react-native';

const GestureEasingAnimation = () => {
  // 初始位置:屏幕中心
  const position = useRef(new Animated.ValueXY({ x: 0, y: 0 })).current;

  // 创建手势响应器
  const panResponder = PanResponder.create({
    onStartShouldSetPanResponder: () => true,
    // 拖拽中:实时更新位置
    onPanResponderMove: (_, gesture) => {
      position.setValue({ x: gesture.dx, y: gesture.dy });
    },
    onPanResponderRelease: () => {
      Animated.spring(position, {
        toValue: { x: 0, y: 0 }, // 回弹到中心
        friction: 8, // 阻尼(对应之前的0.8,值越大阻尼越强)
        tension: 30, // 刚度(对应之前的0.3,值越大弹性越强)
        useNativeDriver: true,
      }).start();
    },
  });

  return (
    <View style={styles.container}>
      <Animated.View
        {...panResponder.panHandlers}
        style={[styles.ball, { transform: position.getTranslateTransform() }]}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F5F7FA',
  },
  ball: {
    width: 60,
    height: 60,
    borderRadius: 30,
    backgroundColor: '#FF7D00',
    position: 'absolute',
    top: '50%',
    left: '50%',
    marginLeft: -30,
    marginTop: -30,
  },
});

export default GestureEasingAnimation;
鸿蒙手势动画适配要点:
  • 使用 Animated.ValueXY 管理二维坐标:比单独使用两个 Animated.Value 更高效,鸿蒙原生驱动支持更好;
  • 手势回调中避免复杂逻辑:仅更新动画进度,否则会导致拖拽卡顿;
  • 松手回弹动画优先使用 Animated.spring:模拟真实物理弹性,符合鸿蒙交互设计规范。

五、实操演示:鸿蒙加载动画组件封装

以下是 OpenHarmony 平台生产级的 Easing 动画组件封装,实现「旋转 + 缩放」的加载指示器,结合 Easing 函数模拟平滑的呼吸效果,可直接复用:

javascript 复制代码
import React, { useRef, useEffect } from 'react';
import { View, StyleSheet, Animated, Easing } from 'react-native';

const OHEasingLoading = ({ size = 40, color = "#007DFF", speed = 1.5 }) => {
  // 旋转动画进度值 (0 → 1 循环)
  const rotateAnimate = useRef(new Animated.Value(0)).current;
  // 缩放呼吸动画进度值 (0.8 → 1.2 循环)
  const scaleAnimate = useRef(new Animated.Value(0.8)).current;

  useEffect(() => {
    // 1. 旋转动画:无限循环、线性匀速、无卡顿
    const rotateLoop = Animated.loop(
      Animated.timing(rotateAnimate, {
        toValue: 1,
        duration: 1000 / speed,
        easing: Easing.linear, // 线性匀速旋转,不会忽快忽慢
        useNativeDriver: true, // 鸿蒙必开,性能拉满,无卡顿
        isInteraction: false,  // 非交互动画,优化主线程占用
      })
    );

    // 2. 缩放呼吸动画:无限循环、缓入缓出、鸿蒙兼容无报错
    const scaleLoop = Animated.loop(
      Animated.sequence([
        Animated.timing(scaleAnimate, {
          toValue: 1.2,
          duration: 800 / speed,
          easing: Easing.bezier(0.42, 0, 0.58, 1),
          useNativeDriver: true,
        }),
        Animated.timing(scaleAnimate, {
          toValue: 0.8,
          duration: 800 / speed,
          easing: Easing.bezier(0.42, 0, 0.58, 1),
          useNativeDriver: true,
        }),
      ])
    );

    // 启动动画
    rotateLoop.start();
    scaleLoop.start();

    return () => {
      rotateLoop.stop();
      scaleLoop.stop();
    };
  }, [speed]);

  // 动画样式映射
  const animateStyle = {
    transform: [
      {
        rotate: rotateAnimate.interpolate({
          inputRange: [0, 1],
          outputRange: ['0deg', '360deg'],
        }),
      },
      { scale: scaleAnimate },
    ],
  };

  return (
    <View style={styles.container}>
      <Animated.View style={[styles.loader, animateStyle]}>
        <View
          style={[
            styles.circle,
            {
              width: size,
              height: size,
              borderColor: color,
              borderWidth: size / 10,
              borderRadius: size / 2,
            },
          ]}
        />
      </Animated.View>
    </View>
  );
};

const LoadingDemo = () => {
  return (
    <View style={styles.demoPage}>
      {/* 调用:自定义尺寸50、绿色、速度2倍 */}
      <OHEasingLoading size={50} color="#00C160" speed={2} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    justifyContent: 'center',
    alignItems: 'center',
  },
  loader: {
    justifyContent: 'center',
    alignItems: 'center',
  },
  circle: {
    borderTopColor: 'transparent', // 顶部透明,形成标准圆环缺口
  },
  demoPage: {
    flex: 1,
    backgroundColor: '#F5F7FA',
    justifyContent: 'center',
    alignItems: 'center',
  },
});

export default LoadingDemo;

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

相关推荐
持续前行2 小时前
vscode 中找settings.json 配置
前端·javascript·vue.js
JosieBook2 小时前
【Vue】11 Vue技术——Vue 中的事件处理详解
前端·javascript·vue.js
韩曙亮2 小时前
【jQuery】jQuery 简介 ( JavaScript 库简介 | jQuery 核心概念、特点 | jQuery 下载并使用 )
前端·javascript·jquery
一只小阿乐2 小时前
vue 改变查询参数的值
前端·javascript·vue.js·路由·router·网文·未花中文网
2501_948122632 小时前
React Native for OpenHarmony 实战:Steam 资讯 App 服务条款实现
javascript·react native·react.js·游戏·ecmascript·harmonyos
奚大野...2 小时前
uni-app手机端项目touchmove禁止页面上下拉滑动
前端·javascript·uni-app
Object~2 小时前
4.const和iota
开发语言·前端·javascript
攀登的牵牛花3 小时前
前端向架构突围系列 - 工程化(一):JavaScript 演进史与最佳实践
前端·javascript
夏天想3 小时前
为什么使用window.print打印的页面只有第一页。其他页面没有了。并且我希望打印的是一个弹窗的内容,竟然把弹窗的样式边框和打印的按钮都打印进去了
前端·javascript·html