一、项目背景与目标

1. 项目概述

- 基于 React Native 开发的开源鸿蒙跨平台医疗应用
- 包含 4 个底部标签页:AI 问诊、问诊历史、用户中心、设置/消息
- 核心功能:AI 问诊、问诊历史记录、用户管理、设置管理

2. 动效集成目标

- 全面覆盖页面转场、组件交互、数据加载等核心场景
- 提升应用视觉体验与交互流畅度
- 确保在开源鸿蒙设备上流畅运行
- 完成开源鸿蒙设备运行验证

二、技术栈与工具

1. 核心技术

- React Native:跨平台应用开发框架
- OpenHarmony:开源鸿蒙平台
- @react-native-oh/rea- ct-native-harmony:开源鸿蒙平台适配
- React Native Animated API:动画实现
- TypeScript:类型安全

2. 开发工具

- Visual Studio Code:代码编辑器
- npm/yarn:包管理工具
- Metro:React Native 开发服务器
- 开源鸿蒙设备:应用运行验证

 三、动效能力集成过程

1.页面转场动效 实现位置:

src/navigation/AppRoot.tsx

开发过程:

1. 初始实现 :使用 Animated.Value 存储每个页面的透明度
2. 优化过程 :
 - 减少动画持续时间从 300ms 到 250ms,提高响应速度
 - 增加 translateY 动画,使页面切换更有层次感
 - 使用 useNativeDriver: true 提高动画性能
 - 实现并行动画,同时处理当前页面隐藏和目标页面显示

关键代码:

// 页面切换动画
const handleTabPress = useCallback((key: string) => {
  if (key === activeKey) return;
  
  // 隐藏当前页面
  Animated.timing(animationValues.current[activeKey], {
    toValue: 0,
    duration: 250,
    useNativeDriver: true,
  }).start();
  
  // 显示目标页面
  Animated.timing(animationValues.current[key], {
    toValue: 1,
    duration: 250,
    useNativeDriver: true,
  }).start();
  
  setActiveKey(key);
}, [activeKey]);

2. 组件交互动效

2.1 按钮交互动效

实现位置:
- src/screens/HomeScreen.tsx
- src/screens/DataScreen.tsx 开发过程
1. 初始实现 :使用 Animated.spring 实现按钮按下反馈
2. 优化过程 :
   - 增加摩擦系数和张力,使动画更紧凑、更有弹性
   - 调整动画缩放比例,使反馈更自然
   - 使用 useCallback 缓存事件处理函数,提高性能

关键代码:

// 按钮按下动画
const handlePressIn = () => {
  Animated.spring(buttonScale, {
    toValue: 0.95,
    useNativeDriver: true,
    friction: 6,
    tension: 40,
  }).start();
};

// 按钮释放动画
const handlePressOut = () => {
  Animated.spring(buttonScale, {
    toValue: 1,
    useNativeDriver: true,
    friction: 6,
    tension: 40,
  }).start();
};

2.2 输入框交互动效

实现位置:
- src/screens/HomeScreen.tsx

开发过程:
1. 初始实现 :使用 Animated.spring 实现输入框聚焦效果
2. 优化过程 :
   - 减少缩放比例至 1.01,提高性能
   - 优化动画参数,使效果更自然
   - 实现聚焦和失焦的对称动画

关键代码:

// 输入框聚焦动画
const handleInputFocus = () => {
  Animated.spring(inputFocusAnim, {
    toValue: 1.01,
    useNativeDriver: true,
    friction: 6,
    tension: 40,
  }).start();
};

// 输入框失焦动画
const handleInputBlur = () => {
  Animated.spring(inputFocusAnim, {
    toValue: 1,
    useNativeDriver: true,
    friction: 6,
    tension: 40,
  }).start();
};

3. 数据加载动效

3.1 骨架屏动画

实现位置
- src/components/SkeletonLoader.tsx

开发过程:
1. 初始实现 :创建基本的骨架屏组件
2. 优化过程 :
   - 添加闪烁动画效果,提高用户体验
   - 实现可配置的骨架屏组件,支持自定义宽度、高度和圆角
   - 创建卡片式骨架屏组件,用于问诊历史页面

关键代码:

// 骨架屏闪烁动画
const animation = Animated.loop(
  Animated.sequence([
    Animated.timing(opacity, {
      toValue: 0.7,
      duration: 1000,
      useNativeDriver: true,
    }),
    Animated.timing(opacity, {
      toValue: 0.3,
      duration: 1000,
      useNativeDriver: true,
    }),
  ])
);

animation.start();

3.2 列表项入场动画

实现位置:
- src/screens/DataScreen.tsx

开发过程:
1. 初始实现 :使用 setTimeout 实现列表项的 staggered 入场效果
2. 优化过程 :
   - 减少动画延迟时间至 80ms,使动画序列更紧凑
   - 减少动画持续时间至 300ms,提高响应速度
   - 优化动画参数,使入场效果更自然

关键代码:

// 列表项入场动画
mockHistory.forEach((item, index) => {
  if (!listItemAnimValues[item.id]) {
    listItemAnimValues[item.id] = new Animated.Value(0);
  }
  
  setTimeout(() => {
    Animated.timing(listItemAnimValues[item.id], {
      toValue: 1,
      duration: 300,
      useNativeDriver: true,
    }).start();
  }, index * 80);
});

4. 模态框动效

实现位置:
- src/screens/DataScreen.tsx

开发过程:
1. 初始实现 :实现基本的模态框显示和隐藏
2. 优化过程 :
   - 添加淡入淡出动画效果
   - 添加缩放动画效果
   - 使用 Animated.parallel 同时执行多个动画
   - 优化动画参数,使模态框的出现和消失更自然

关键代码:

// 模态框打开动画
Animated.parallel([
  Animated.timing(modalFadeAnim, {
    toValue: 1,
    duration: 250,
    useNativeDriver: true,
  }),
  Animated.spring(modalScaleAnim, {
    toValue: 1,
    useNativeDriver: true,
    friction: 6,
    tension: 40,
  }),
]).start();

// 模态框关闭动画
Animated.parallel([
  Animated.timing(modalFadeAnim, {
    toValue: 0,
    duration: 200,
    useNativeDriver: true,
  }),
  Animated.spring(modalScaleAnim, {
    toValue: 0.9,
    useNativeDriver: true,
    friction: 6,
    tension: 40,
  }),
]).start();

四、遇到的问题与解决方案


1. 技术问题

问题 1:AI 问诊页面输入信息时崩溃
原因 :输入长度无限制,导致状态更新过于频繁
解决方案 :添加输入长度限制(最大 500 字符),优化状态管理

问题 2:问诊历史页面诊断详情点击无反应
原因 :缺少 onPress 处理函数
解决方案 :添加 onPress 处理函数,实现模态对话框显示

问题 3:页面切换时状态丢失
原因 :组件被卸载和重新挂载
解决方案 :使用 useRef 存储页面引用,使用绝对定位保持组件实例

问题 4:动画性能问题
原因 :动画持续时间过长,参数设置不合理
解决方案 :优化动画参数,使用 useNativeDriver: true

最终效果:

Emulator 2026-02-07 19-32-57

五、开发经验总结

动画实现最佳实践:
1. 性能优先 :使用 useNativeDriver: true,优化动画参数
2. 用户体验 :调整动画持续时间和参数,确保流畅自然
3. 代码质量 :使用 TypeScript 类型安全,优化代码结构
4. 平台适配 :针对不同平台调整动画参数,确保兼容性

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐