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

📌 开发环境声明:本文基于 React Native 0.72.90 版本进行开发适配
在这里插入图片描述


🚀 一、开篇引言

翻转卡片是一种常见的交互效果,广泛应用于卡片游戏、问答应用、产品展示等场景。用户点击或滑动卡片时,卡片会翻转显示背面的内容,这种交互方式既直观又有趣。react-native-flip-card 是 React Native 社区中简单易用的翻转卡片组件,支持水平和垂直翻转、自定义动画参数等功能。本文将带你深入了解如何在 HarmonyOS 平台上集成和使用这个实用的交互组件。

1.1 你将学到什么?

  • ✅ FlipCard 的核心概念与工作原理
  • ✅ HarmonyOS 平台的完整集成流程
  • ✅ 水平翻转与垂直翻转
  • ✅ API 属性的深度解析
  • ✅ 实际应用场景的最佳实践

1.2 适用人群

  • 正在进行 React Native 鸿蒙化迁移的开发者
  • 需要实现卡片翻转效果的开发者
  • 对跨平台交互动画开发感兴趣的技术爱好者

1.3 为什么选择 FlipCard?

特点 说明
简单易用 无需复杂配置,开箱即用
跨平台一致 iOS、Android、HarmonyOS 表现一致
灵活配置 支持水平和垂直翻转、自定义动画参数
纯 JS 实现 无需原生依赖,安装即可使用
可控翻转 支持点击翻转和程序控制翻转

📦 二、库概览

2.1 基本信息

项目 内容
库名称 react-native-flip-card
版本信息 3.5.7
官方仓库 https://github.com/moschan/react-native-flip-card
开源协议 MIT

2.2 版本兼容性

三方库版本 支持RN版本
3.5.7 0.72 / 0.77

2.3 核心能力矩阵

能力项 描述 HarmonyOS 支持
水平翻转 flipHorizontal ✅ 完全支持
垂直翻转 flipVertical ✅ 完全支持
点击翻转 clickable ✅ 完全支持
程序控制翻转 flip 属性 ✅ 完全支持
翻转动画回调 onFlipStart/End ✅ 完全支持
原生动画驱动 useNativeDriver ✅ 完全支持

2.4 技术架构图

平台层

动画层

React Native 应用层

FlipCard Component

Face 正面

Back 背面

Animated API

Transform

RotateY/RotateX

Android

iOS

HarmonyOS

2.5 典型应用场景

场景 描述 示例
卡片游戏 翻牌配对游戏 🃏 记忆游戏、扑克游戏
问答应用 翻转查看答案 📝 单词卡片、知识问答
产品展示 正反面展示 🛍️ 商品详情、会员卡
身份认证 正反面身份证 🪪 证件展示

📖 三、安装与配置

3.1 安装依赖

在项目根目录执行以下命令:

npm install react-native-flip-card@3.5.7

或使用 yarn:

yarn add react-native-flip-card@3.5.7

3.2 验证安装

安装完成后,检查 package.json 文件中是否包含以下依赖:

{
  "dependencies": {
    "react-native-flip-card": "3.5.7"
  }
}

3.3 基本导入

import FlipCard from 'react-native-flip-card';

📖 四、API 详解

4.1 FlipCard 组件

核心组件,提供卡片翻转功能。

基本用法:

import FlipCard from 'react-native-flip-card';

<FlipCard style={styles.card}>
  <View style={styles.face}>
    <Text>正面</Text>
  </View>
  <View style={styles.back}>
    <Text>背面</Text>
  </View>
</FlipCard>

4.2 属性详解

flip - 控制翻转状态

控制卡片的翻转状态,可用于程序控制翻转。

类型: boolean

默认值: false

const [isFlipped, setIsFlipped] = useState(false);

<FlipCard
  flip={isFlipped}
  onFlipEnd={() => console.log('翻转完成')}
>
  <View><Text>正面</Text></View>
  <View><Text>背面</Text></View>
</FlipCard>

<TouchableOpacity onPress={() => setIsFlipped(!isFlipped)}>
  <Text>翻转</Text>
</TouchableOpacity>
clickable - 点击翻转

是否允许点击卡片触发翻转。

类型: boolean

默认值: true

<FlipCard clickable={true}>
  <View><Text>正面</Text></View>
  <View><Text>背面</Text></View>
</FlipCard>
friction - 摩擦系数

控制翻转动画的摩擦系数,值越大动画越慢。

类型: number

默认值: 6

<FlipCard friction={3}>
  <View><Text>正面</Text></View>
  <View><Text>背面</Text></View>
</FlipCard>
perspective - 透视效果

应用于翻转变换的透视效果量。

类型: number

默认值: 1000

HarmonyOS 支持: ❌ 暂不支持

<FlipCard perspective={500}>
  <View><Text>正面</Text></View>
  <View><Text>背面</Text></View>
</FlipCard>
flipHorizontal - 水平翻转

是否水平方向翻转。

类型: boolean

默认值: false

<FlipCard flipHorizontal={true} flipVertical={false}>
  <View><Text>正面</Text></View>
  <View><Text>背面</Text></View>
</FlipCard>
flipVertical - 垂直翻转

是否垂直方向翻转。

类型: boolean

默认值: true

<FlipCard flipHorizontal={false} flipVertical={true}>
  <View><Text>正面</Text></View>
  <View><Text>背面</Text></View>
</FlipCard>
alignHeight - 高度对齐

是否保持较大一面的高度。

类型: boolean

默认值: false

<FlipCard alignHeight={true}>
  <View style={{ height: 100 }}><Text>正面</Text></View>
  <View style={{ height: 200 }}><Text>背面</Text></View>
</FlipCard>
alignWidth - 宽度对齐

是否保持较大一面的宽度。

类型: boolean

默认值: false

<FlipCard alignWidth={true}>
  <View style={{ width: 100 }}><Text>正面</Text></View>
  <View style={{ width: 200 }}><Text>背面</Text></View>
</FlipCard>
useNativeDriver - 原生动画驱动

是否使用原生动画驱动。

类型: boolean

默认值: true

<FlipCard useNativeDriver={true}>
  <View><Text>正面</Text></View>
  <View><Text>背面</Text></View>
</FlipCard>

4.3 回调函数

onFlipStart - 翻转开始回调

卡片开始翻转动画时触发。

类型: (isFlipStart: boolean) => void

<FlipCard
  onFlipStart={(isFlipStart) => {
    console.log('翻转开始:', isFlipStart);
  }}
>
  <View><Text>正面</Text></View>
  <View><Text>背面</Text></View>
</FlipCard>
onFlipEnd - 翻转结束回调

卡片完成翻转动画时触发。

类型: (isFlipEnd: boolean) => void

<FlipCard
  onFlipEnd={(isFlipEnd) => {
    console.log('翻转结束:', isFlipEnd);
  }}
>
  <View><Text>正面</Text></View>
  <View><Text>背面</Text></View>
</FlipCard>

💡 五、使用示例

5.1 基础翻转卡片

最简单的使用方式,点击卡片即可翻转。

适用场景: 简单的卡片翻转展示。

import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import FlipCard from 'react-native-flip-card';

const BasicFlipCard = () => {
  return (
    <View style={styles.container}>
      <FlipCard style={styles.card}>
        <View style={styles.face}>
          <Text style={styles.text}>正面</Text>
          <Text style={styles.hint}>点击翻转</Text>
        </View>
        <View style={styles.back}>
          <Text style={styles.text}>背面</Text>
          <Text style={styles.hint}>点击翻回</Text>
        </View>
      </FlipCard>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#f5f5f5',
  },
  card: {
    width: 200,
    height: 200,
  },
  face: {
    flex: 1,
    backgroundColor: '#00d4ff',
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 12,
  },
  back: {
    flex: 1,
    backgroundColor: '#4ECDC4',
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 12,
  },
  text: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#fff',
  },
  hint: {
    fontSize: 14,
    color: 'rgba(255,255,255,0.8)',
    marginTop: 8,
  },
});

export default BasicFlipCard;

代码解析:

  • 默认 clickable={true},点击即可翻转
  • 第一个子元素为正面,第二个子元素为背面
  • 使用 style 设置卡片尺寸

5.2 程序控制翻转

通过按钮控制卡片翻转。

适用场景: 需要外部控制翻转的场景。

import React, { useState } from 'react';
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
import FlipCard from 'react-native-flip-card';

const ControlledFlipCard = () => {
  const [isFlipped, setIsFlipped] = useState(false);

  return (
    <View style={styles.container}>
      <FlipCard
        style={styles.card}
        flip={isFlipped}
        clickable={false}
        friction={3}
        flipHorizontal={true}
        flipVertical={false}
        onFlipEnd={(isFlipEnd) => {
          console.log('翻转完成:', isFlipEnd);
        }}
      >
        <View style={styles.face}>
          <Text style={styles.text}>正面内容</Text>
        </View>
        <View style={styles.back}>
          <Text style={styles.text}>背面内容</Text>
        </View>
      </FlipCard>

      <TouchableOpacity
        style={styles.button}
        onPress={() => setIsFlipped(!isFlipped)}
      >
        <Text style={styles.buttonText}>
          {isFlipped ? '翻回正面' : '翻到背面'}
        </Text>
      </TouchableOpacity>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#1a1a2e',
  },
  card: {
    width: 280,
    height: 180,
  },
  face: {
    flex: 1,
    backgroundColor: '#16213e',
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 16,
    borderWidth: 2,
    borderColor: '#00d4ff',
  },
  back: {
    flex: 1,
    backgroundColor: '#00d4ff',
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 16,
  },
  text: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#fff',
  },
  button: {
    marginTop: 30,
    backgroundColor: '#4ECDC4',
    paddingHorizontal: 32,
    paddingVertical: 14,
    borderRadius: 8,
  },
  buttonText: {
    fontSize: 16,
    fontWeight: '600',
    color: '#fff',
  },
});

export default ControlledFlipCard;

代码解析:

  • flip={isFlipped} 控制翻转状态
  • clickable={false} 禁用点击翻转
  • flipHorizontal={true} 水平翻转
  • 通过按钮切换 isFlipped 状态

5.3 单词卡片应用

模拟单词学习卡片的翻转效果。

适用场景: 单词学习、问答卡片。

import React, { useState } from 'react';
import {
  View,
  Text,
  StyleSheet,
  TouchableOpacity,
  SafeAreaView,
} from 'react-native';
import FlipCard from 'react-native-flip-card';

interface WordCard {
  word: string;
  phonetic: string;
  meaning: string;
  example: string;
}

const wordCards: WordCard[] = [
  {
    word: 'Serendipity',
    phonetic: '/ˌserənˈdɪpəti/',
    meaning: '意外发现美好事物的能力',
    example: 'Finding that book was pure serendipity.',
  },
  {
    word: 'Ephemeral',
    phonetic: '/ɪˈfemərəl/',
    meaning: '短暂的,转瞬即逝的',
    example: 'Fame is ephemeral.',
  },
  {
    word: 'Ubiquitous',
    phonetic: '/juːˈbɪkwɪtəs/',
    meaning: '无处不在的',
    example: 'Smartphones have become ubiquitous.',
  },
];

const WordCardApp = () => {
  const [currentIndex, setCurrentIndex] = useState(0);
  const [isFlipped, setIsFlipped] = useState(false);

  const currentCard = wordCards[currentIndex];

  const handleNext = () => {
    setIsFlipped(false);
    setCurrentIndex((prev) => (prev + 1) % wordCards.length);
  };

  const handlePrev = () => {
    setIsFlipped(false);
    setCurrentIndex((prev) =>
      prev === 0 ? wordCards.length - 1 : prev - 1
    );
  };

  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.header}>
        <Text style={styles.title}>单词卡片</Text>
        <Text style={styles.progress}>
          {currentIndex + 1} / {wordCards.length}
        </Text>
      </View>

      <View style={styles.cardContainer}>
        <FlipCard
          style={styles.card}
          flip={isFlipped}
          clickable={true}
          friction={4}
          flipHorizontal={true}
          flipVertical={false}
        >
          <View style={styles.front}>
            <Text style={styles.word}>{currentCard.word}</Text>
            <Text style={styles.phonetic}>{currentCard.phonetic}</Text>
            <Text style={styles.hint}>点击查看释义</Text>
          </View>
          <View style={styles.back}>
            <Text style={styles.meaning}>{currentCard.meaning}</Text>
            <View style={styles.divider} />
            <Text style={styles.example}>{currentCard.example}</Text>
            <Text style={styles.hint}>点击翻回</Text>
          </View>
        </FlipCard>
      </View>

      <View style={styles.controls}>
        <TouchableOpacity style={styles.navButton} onPress={handlePrev}>
          <Text style={styles.navButtonText}>上一个</Text>
        </TouchableOpacity>
        <TouchableOpacity
          style={styles.flipButton}
          onPress={() => setIsFlipped(!isFlipped)}
        >
          <Text style={styles.flipButtonText}>翻转</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.navButton} onPress={handleNext}>
          <Text style={styles.navButtonText}>下一个</Text>
        </TouchableOpacity>
      </View>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#1a1a2e',
  },
  header: {
    padding: 20,
    alignItems: 'center',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#fff',
  },
  progress: {
    fontSize: 14,
    color: '#888',
    marginTop: 8,
  },
  cardContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    paddingHorizontal: 20,
  },
  card: {
    width: '100%',
    height: 300,
  },
  front: {
    flex: 1,
    backgroundColor: '#16213e',
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 16,
    padding: 20,
  },
  word: {
    fontSize: 32,
    fontWeight: 'bold',
    color: '#00d4ff',
    textAlign: 'center',
  },
  phonetic: {
    fontSize: 18,
    color: '#888',
    marginTop: 12,
  },
  hint: {
    fontSize: 14,
    color: '#666',
    marginTop: 30,
  },
  back: {
    flex: 1,
    backgroundColor: '#00d4ff',
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 16,
    padding: 20,
  },
  meaning: {
    fontSize: 22,
    fontWeight: '600',
    color: '#fff',
    textAlign: 'center',
  },
  divider: {
    width: 60,
    height: 2,
    backgroundColor: 'rgba(255,255,255,0.5)',
    marginVertical: 20,
  },
  example: {
    fontSize: 16,
    color: 'rgba(255,255,255,0.9)',
    textAlign: 'center',
    fontStyle: 'italic',
  },
  controls: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    padding: 20,
    gap: 12,
  },
  navButton: {
    flex: 1,
    backgroundColor: '#16213e',
    padding: 14,
    borderRadius: 8,
    alignItems: 'center',
  },
  navButtonText: {
    color: '#00d4ff',
    fontSize: 14,
    fontWeight: '600',
  },
  flipButton: {
    flex: 1,
    backgroundColor: '#4ECDC4',
    padding: 14,
    borderRadius: 8,
    alignItems: 'center',
  },
  flipButtonText: {
    color: '#fff',
    fontSize: 14,
    fontWeight: '600',
  },
});

export default WordCardApp;

代码解析:

  • 结合列表切换和卡片翻转
  • 点击卡片或按钮都可翻转
  • 切换卡片时自动重置为正面

❓ 八、常见问题

8.1 遗留问题

⚠️ 重要提示:以下属性在 HarmonyOS 平台上暂不支持。

perspective - 透视效果

perspective 属性在 HarmonyOS 平台上暂不支持,但不影响基本翻转功能。


8.2 常见问题解答

Q1: 如何实现水平翻转?

A: 设置 flipHorizontal={true}flipVertical={false}

Q2: 如何禁用点击翻转?

A: 设置 clickable={false},然后通过 flip 属性控制。

Q3: 正反面高度不一致怎么办?

A: 设置 alignHeight={true} 保持较大一面的高度。

Q4: 翻转动画太快/太慢怎么办?

A: 调整 friction 参数,值越大动画越慢。

8.3 最佳实践

  1. 统一尺寸:正反面使用相同尺寸,避免布局跳动
  2. 合理摩擦friction 设置在 3-8 之间效果较好
  3. 原生驱动:保持 useNativeDriver={true} 获得流畅动画
  4. 状态管理:使用 flip 属性进行程序控制时,注意状态同步

💻 九、完整示例代码

综合示例

import React, { useState } from 'react';
import {
  View,
  Text,
  StyleSheet,
  TouchableOpacity,
  SafeAreaView,
  ScrollView,
} from 'react-native';
import FlipCard from 'react-native-flip-card';

export default function App() {
  const [flip1, setFlip1] = useState(false);
  const [flip2, setFlip2] = useState(false);

  return (
    <SafeAreaView style={styles.container}>
      <ScrollView contentContainerStyle={styles.content}>
        <Text style={styles.title}>翻转卡片示例</Text>

        <View style={styles.section}>
          <Text style={styles.sectionTitle}>点击翻转</Text>
          <FlipCard style={styles.card}>
            <View style={[styles.face, { backgroundColor: '#00d4ff' }]}>
              <Text style={styles.cardText}>正面</Text>
            </View>
            <View style={[styles.back, { backgroundColor: '#4ECDC4' }]}>
              <Text style={styles.cardText}>背面</Text>
            </View>
          </FlipCard>
        </View>

        <View style={styles.section}>
          <Text style={styles.sectionTitle}>水平翻转</Text>
          <FlipCard
            style={styles.card}
            flipHorizontal={true}
            flipVertical={false}
          >
            <View style={[styles.face, { backgroundColor: '#FF6B6B' }]}>
              <Text style={styles.cardText}>正面</Text>
            </View>
            <View style={[styles.back, { backgroundColor: '#FFE66D' }]}>
              <Text style={styles.cardText}>背面</Text>
            </View>
          </FlipCard>
        </View>

        <View style={styles.section}>
          <Text style={styles.sectionTitle}>程序控制</Text>
          <FlipCard
            style={styles.card}
            flip={flip1}
            clickable={false}
            flipHorizontal={true}
            flipVertical={false}
          >
            <View style={[styles.face, { backgroundColor: '#45B7D1' }]}>
              <Text style={styles.cardText}>正面</Text>
            </View>
            <View style={[styles.back, { backgroundColor: '#96CEB4' }]}>
              <Text style={styles.cardText}>背面</Text>
            </View>
          </FlipCard>
          <TouchableOpacity
            style={styles.button}
            onPress={() => setFlip1(!flip1)}
          >
            <Text style={styles.buttonText}>翻转</Text>
          </TouchableOpacity>
        </View>

        <View style={styles.section}>
          <Text style={styles.sectionTitle}>自定义动画</Text>
          <FlipCard
            style={styles.card}
            flip={flip2}
            clickable={false}
            friction={3}
            flipHorizontal={true}
            flipVertical={false}
            onFlipStart={(isFlipStart) => console.log('开始:', isFlipStart)}
            onFlipEnd={(isFlipEnd) => console.log('结束:', isFlipEnd)}
          >
            <View style={[styles.face, { backgroundColor: '#9B59B6' }]}>
              <Text style={styles.cardText}>正面</Text>
            </View>
            <View style={[styles.back, { backgroundColor: '#E74C3C' }]}>
              <Text style={styles.cardText}>背面</Text>
            </View>
          </FlipCard>
          <TouchableOpacity
            style={styles.button}
            onPress={() => setFlip2(!flip2)}
          >
            <Text style={styles.buttonText}>翻转</Text>
          </TouchableOpacity>
        </View>
      </ScrollView>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#1a1a2e',
  },
  content: {
    padding: 20,
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#fff',
    textAlign: 'center',
    marginBottom: 24,
  },
  section: {
    marginBottom: 24,
    alignItems: 'center',
  },
  sectionTitle: {
    fontSize: 16,
    color: '#888',
    marginBottom: 12,
  },
  card: {
    width: 200,
    height: 150,
  },
  face: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 12,
  },
  back: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 12,
  },
  cardText: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#fff',
  },
  button: {
    marginTop: 16,
    backgroundColor: '#00d4ff',
    paddingHorizontal: 32,
    paddingVertical: 12,
    borderRadius: 8,
  },
  buttonText: {
    color: '#fff',
    fontSize: 16,
    fontWeight: '600',
  },
});

🔗 十、相关资源

Logo

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

更多推荐