在这里插入图片描述

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

📋 前言

react-native-shadow-2 是一个功能强大的阴影效果库,它为 React Native 应用提供了丰富多样的立体阴影效果。该库基于 SVG 技术实现,支持渐变阴影、自定义阴影方向、选择性阴影渲染等高级特性,能够轻松创建出精美的立体视觉效果。该库支持 Android、iOS 和 HarmonyOS 三端,为跨平台应用开发提供了统一的阴影效果解决方案,特别适合需要增强 UI 视觉深度的场景,如粒子立体效果、卡片悬浮、按钮立体感等。

🎯 库简介

基本信息
  • 库名称: react-native-shadow-2

  • 版本信息:

    • 7.0.8: 支持 RN 0.72 版本
    • 7.1.1: 支持 RN 0.77 版本
  • 官方仓库: https://github.com/SrBrahma/react-native-shadow-2

  • 主要功能:

    • 渐变阴影:支持 startColor 和 endColor 实现渐变阴影效果
    • 自定义距离:精确控制阴影的延伸距离
    • 阴影偏移:支持水平和垂直方向的阴影偏移
    • 选择性渲染:可选择性渲染特定边或角的阴影
    • 内部阴影:支持内容内部的阴影效果
    • 安全渲染:处理特殊形状的首屏渲染问题
    • 动态控制:支持动态启用/禁用阴影效果
  • 依赖关系: 依赖 react-native-svg 库,需要先配置 SVG 支持

  • 兼容性验证: 该第三方库支持直接从 npm 下载,完整支持 HarmonyOS 平台

为什么需要这个库?
  • 视觉效果: 提供丰富的立体阴影效果,增强 UI 视觉深度
  • 渐变支持: 支持渐变阴影,比原生 shadowColor 更灵活
  • 精确控制: 精确控制阴影的方向、距离、颜色等属性
  • 跨平台: 支持 Android、iOS、HarmonyOS 三端,统一的 API
  • 高性能: 基于 SVG 实现,性能优异
  • 易于使用: API 设计简洁明了,易于集成和使用
  • 鸿蒙支持: 完整支持 HarmonyOS 平台,实现真正的跨平台

📦 安装步骤

1. 使用 npm 安装

根据您的 RN 版本选择对应的包名:

RN 0.72 版本:

npm install react-native-shadow-2@7.0.8

RN 0.77 版本:

npm install react-native-shadow-2@7.1.1
2. 验证安装

安装完成后,检查 package.json 文件,应该能看到新增的依赖:

{
  "dependencies": {
    "react-native-shadow-2": "^7.0.8",
    // ... 其他依赖
  }
}

🔧 HarmonyOS 平台配置 ⭐

1. 配置 react-native-svg(必需依赖)⭐⭐⭐

[!WARNING] react-native-shadow-2 依赖 react-native-svg,必须先配置 react-native-svg!

本库 HarmonyOS 侧实现依赖 react-native-svg 的原生端代码,如已在 HarmonyOS 工程中引入过该库,则无需再次引入,可跳过本章节步骤,直接使用。

如未引入请参照这篇文章进行配置:

2. 无需额外配置

[!TIP] react-native-shadow-2 是纯 JavaScript 库,无需额外的原生端配置!

配置完 react-native-svg 后,react-native-shadow-2 可以直接使用,无需额外的原生端配置。

💻 完整代码示例

下面是一个完整的示例,展示了如何使用 react-native-shadow-2 实现粒子立体效果:

import React, { useState } from 'react';
import {
  View,
  StyleSheet,
  Text,
  ScrollView,
  TouchableOpacity,
  SafeAreaView,
  Animated,
} from 'react-native';
import { Shadow } from 'react-native-shadow-2';

interface ShadowConfig {
  startColor: string;
  endColor: string;
  distance: number;
  offset: [number, number];
  paintInside: boolean;
  sides: {
    start: boolean;
    end: boolean;
    top: boolean;
    bottom: boolean;
  };
  corners: {
    topStart: boolean;
    topEnd: boolean;
    bottomStart: boolean;
    bottomEnd: boolean;
  };
}

function ShadowDemo() {
  const [activeTab, setActiveTab] = useState<'particles' | 'cards' | 'buttons'>('particles');

  return (
    <SafeAreaView style={styles.container}>
      {/* 标题区域 */}
      <View style={styles.header}>
        <Text style={styles.headerTitle}>立体效果演示</Text>
        <Text style={styles.headerSubtitle}>react-native-shadow-2</Text>
      </View>

      {/* 标签切换 */}
      <View style={styles.tabContainer}>
        <TouchableOpacity
          style={[styles.tab, activeTab === 'particles' && styles.activeTab]}
          onPress={() => setActiveTab('particles')}
        >
          <Text style={[styles.tabText, activeTab === 'particles' && styles.activeTabText]}>
            粒子立体
          </Text>
        </TouchableOpacity>
        <TouchableOpacity
          style={[styles.tab, activeTab === 'cards' && styles.activeTab]}
          onPress={() => setActiveTab('cards')}
        >
          <Text style={[styles.tabText, activeTab === 'cards' && styles.activeTabText]}>
            卡片悬浮
          </Text>
        </TouchableOpacity>
        <TouchableOpacity
          style={[styles.tab, activeTab === 'buttons' && styles.activeTab]}
          onPress={() => setActiveTab('buttons')}
        >
          <Text style={[styles.tabText, activeTab === 'buttons' && styles.activeTabText]}>
            按钮立体
          </Text>
        </TouchableOpacity>
      </View>

      {/* 内容区域 */}
      <ScrollView style={styles.content}>
        {activeTab === 'particles' && <ParticleShadows />}
        {activeTab === 'cards' && <CardShadows />}
        {activeTab === 'buttons' && <ButtonShadows />}
      </ScrollView>
    </SafeAreaView>
  );
}

// 粒子立体效果
const ParticleShadows = () => {
  return (
    <View style={styles.section}>
      <Text style={styles.sectionTitle}>粒子立体效果</Text>

      {/* 基础粒子 */}
      <View style={styles.demoRow}>
        <Text style={styles.label}>基础粒子</Text>
        <Shadow
          distance={10}
          startColor="#409EFF40"
          endColor="#409EFF00"
        >
          <View style={styles.particle} />
        </Shadow>
      </View>

      {/* 渐变粒子 */}
      <View style={styles.demoRow}>
        <Text style={styles.label}>渐变粒子</Text>
        <Shadow
          distance={15}
          startColor="#67C23A80"
          endColor="#67C23A00"
        >
          <View style={[styles.particle, { backgroundColor: '#67C23A' }]} />
        </Shadow>
      </View>

      {/* 大距离粒子 */}
      <View style={styles.demoRow}>
        <Text style={styles.label}>大距离粒子</Text>
        <Shadow
          distance={25}
          startColor="#E6A23C60"
          endColor="#E6A23C00"
        >
          <View style={[styles.particle, { backgroundColor: '#E6A23C' }]} />
        </Shadow>
      </View>

      {/* 底部阴影 */}
      <View style={styles.demoRow}>
        <Text style={styles.label}>底部阴影</Text>
        <Shadow
          distance={20}
          sides={{ start: false, end: false, top: false, bottom: true }}
          startColor="#F56C6C80"
          endColor="#F56C6C00"
        >
          <View style={[styles.particle, { backgroundColor: '#F56C6C' }]} />
        </Shadow>
      </View>

      {/* 强阴影粒子 */}
      <View style={styles.demoRow}>
        <Text style={styles.label}>强阴影粒子</Text>
        <Shadow
          distance={15}
          startColor="#90939960"
          endColor="#90939900"
        >
          <View style={[styles.particle, { backgroundColor: '#909399' }]} />
        </Shadow>
      </View>

      {/* 轻阴影粒子 */}
      <View style={styles.demoRow}>
        <Text style={styles.label}>轻阴影粒子</Text>
        <Shadow
          distance={5}
          startColor="#409EFF20"
          endColor="#409EFF00"
        >
          <View style={[styles.particle, { backgroundColor: '#409EFF' }]} />
        </Shadow>
      </View>
    </View>
  );
};

// 卡片悬浮效果
const CardShadows = () => {
  return (
    <View style={styles.section}>
      <Text style={styles.sectionTitle}>卡片悬浮效果</Text>

      {/* 基础卡片 */}
      <View style={styles.cardContainer}>
        <Text style={styles.label}>基础卡片</Text>
        <Shadow
          distance={20}
          startColor="#00000020"
          endColor="#00000000"
        >
          <View style={styles.card}>
            <Text style={styles.cardTitle}>基础卡片</Text>
            <Text style={styles.cardText}>这是一个基础的悬浮卡片效果</Text>
          </View>
        </Shadow>
      </View>

      {/* 彩色卡片 */}
      <View style={styles.cardContainer}>
        <Text style={styles.label}>彩色卡片</Text>
        <Shadow
          distance={25}
          startColor="#409EFF60"
          endColor="#409EFF00"
        >
          <View style={[styles.card, { backgroundColor: '#409EFF' }]}>
            <Text style={[styles.cardTitle, { color: '#FFFFFF' }]}>彩色卡片</Text>
            <Text style={[styles.cardText, { color: '#FFFFFF' }]}>这是一个彩色的悬浮卡片</Text>
          </View>
        </Shadow>
      </View>

      {/* 大阴影卡片 */}
      <View style={styles.cardContainer}>
        <Text style={styles.label}>大阴影卡片</Text>
        <Shadow
          distance={40}
          startColor="#00000030"
          endColor="#00000000"
        >
          <View style={styles.card}>
            <Text style={styles.cardTitle}>大阴影卡片</Text>
            <Text style={styles.cardText}>这是一个大阴影的悬浮卡片</Text>
          </View>
        </Shadow>
      </View>

      {/* 绿色卡片 */}
      <View style={styles.cardContainer}>
        <Text style={styles.label}>绿色卡片</Text>
        <Shadow
          distance={20}
          startColor="#67C23A60"
          endColor="#67C23A00"
        >
          <View style={[styles.card, { backgroundColor: '#67C23A' }]}>
            <Text style={[styles.cardTitle, { color: '#FFFFFF' }]}>绿色卡片</Text>
            <Text style={[styles.cardText, { color: '#FFFFFF' }]}>这是一个绿色的悬浮卡片</Text>
          </View>
        </Shadow>
      </View>
    </View>
  );
};

// 按钮立体效果
const ButtonShadows = () => {
  return (
    <View style={styles.section}>
      <Text style={styles.sectionTitle}>按钮立体效果</Text>

      {/* 基础按钮 */}
      <View style={styles.buttonContainer}>
        <Text style={styles.label}>基础按钮</Text>
        <Shadow
          distance={8}
          startColor="#409EFF40"
          endColor="#409EFF00"
        >
          <TouchableOpacity style={styles.button}>
            <Text style={styles.buttonText}>基础按钮</Text>
          </TouchableOpacity>
        </Shadow>
      </View>

      {/* 强阴影按钮 */}
      <View style={styles.buttonContainer}>
        <Text style={styles.label}>强阴影按钮</Text>
        <Shadow
          distance={12}
          startColor="#409EFF60"
          endColor="#409EFF00"
        >
          <TouchableOpacity style={styles.button}>
            <Text style={styles.buttonText}>强阴影按钮</Text>
          </TouchableOpacity>
        </Shadow>
      </View>

      {/* 轻阴影按钮 */}
      <View style={styles.buttonContainer}>
        <Text style={styles.label}>轻阴影按钮</Text>
        <Shadow
          distance={5}
          startColor="#00000020"
          endColor="#00000000"
        >
          <TouchableOpacity style={styles.button}>
            <Text style={styles.buttonText}>轻阴影按钮</Text>
          </TouchableOpacity>
        </Shadow>
      </View>

      {/* 渐变按钮 */}
      <View style={styles.buttonContainer}>
        <Text style={styles.label}>渐变按钮</Text>
        <Shadow
          distance={15}
          startColor="#E6A23C60"
          endColor="#E6A23C00"
        >
          <TouchableOpacity style={[styles.button, { backgroundColor: '#E6A23C' }]}>
            <Text style={styles.buttonText}>渐变按钮</Text>
          </TouchableOpacity>
        </Shadow>
      </View>

      {/* 红色按钮 */}
      <View style={styles.buttonContainer}>
        <Text style={styles.label}>红色按钮</Text>
        <Shadow
          distance={10}
          startColor="#F56C6C50"
          endColor="#F56C6C00"
        >
          <TouchableOpacity style={[styles.button, { backgroundColor: '#F56C6C' }]}>
            <Text style={styles.buttonText}>红色按钮</Text>
          </TouchableOpacity>
        </Shadow>
      </View>

      {/* 禁用状态 */}
      <View style={styles.buttonContainer}>
        <Text style={styles.label}>禁用状态</Text>
        <Shadow
          distance={8}
          startColor="#409EFF40"
          endColor="#409EFF00"
          disabled={true}
        >
          <TouchableOpacity style={[styles.button, styles.disabledButton]} disabled={true}>
            <Text style={[styles.buttonText, { color: '#C0C4CC' }]}>禁用按钮</Text>
          </TouchableOpacity>
        </Shadow>
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F5F7FA',
  },
  header: {
    padding: 20,
    backgroundColor: '#FFFFFF',
    borderBottomWidth: 1,
    borderBottomColor: '#EBEEF5',
  },
  headerTitle: {
    fontSize: 24,
    fontWeight: '700',
    color: '#303133',
    textAlign: 'center',
    marginBottom: 8,
  },
  headerSubtitle: {
    fontSize: 16,
    fontWeight: '500',
    color: '#909399',
    textAlign: 'center',
  },
  tabContainer: {
    flexDirection: 'row',
    backgroundColor: '#FFFFFF',
    borderBottomWidth: 1,
    borderBottomColor: '#EBEEF5',
  },
  tab: {
    flex: 1,
    paddingVertical: 16,
    alignItems: 'center',
  },
  activeTab: {
    borderBottomWidth: 2,
    borderBottomColor: '#409EFF',
  },
  tabText: {
    fontSize: 16,
    fontWeight: '500',
    color: '#909399',
  },
  activeTabText: {
    color: '#409EFF',
    fontWeight: '600',
  },
  content: {
    flex: 1,
    padding: 16,
  },
  section: {
    marginBottom: 20,
  },
  sectionTitle: {
    fontSize: 20,
    fontWeight: '600',
    color: '#303133',
    marginBottom: 16,
  },
  demoRow: {
    flexDirection: 'row',
    alignItems: 'center',
    marginBottom: 20,
    backgroundColor: '#FFFFFF',
    padding: 16,
    borderRadius: 8,
  },
  label: {
    fontSize: 14,
    fontWeight: '500',
    color: '#606266',
    width: 80,
  },
  particle: {
    width: 60,
    height: 60,
    borderRadius: 30,
    backgroundColor: '#409EFF',
  },
  cardContainer: {
    marginBottom: 20,
  },
  card: {
    backgroundColor: '#FFFFFF',
    borderRadius: 12,
    padding: 20,
    shadowColor: '#000000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 8,
    elevation: 4,
  },
  cardTitle: {
    fontSize: 18,
    fontWeight: '600',
    color: '#303133',
    marginBottom: 8,
  },
  cardText: {
    fontSize: 14,
    color: '#606266',
    lineHeight: 20,
  },
  buttonContainer: {
    marginBottom: 20,
  },
  button: {
    backgroundColor: '#409EFF',
    borderRadius: 8,
    paddingVertical: 12,
    paddingHorizontal: 24,
    alignItems: 'center',
  },
  roundButton: {
    width: 80,
    height: 80,
    borderRadius: 40,
    justifyContent: 'center',
  },
  disabledButton: {
    backgroundColor: '#F5F7FA',
  },
  buttonText: {
    color: '#FFFFFF',
    fontSize: 16,
    fontWeight: '600',
  },
});

export default ShadowDemo;

🎨 实际应用场景

完整示例代码已展示了以下实际应用场景:

  • 粒子立体效果: 为粒子添加立体阴影,增强视觉深度
  • 卡片悬浮效果: 为卡片添加悬浮阴影,提升交互体验
  • 按钮立体效果: 为按钮添加立体阴影,增强可点击感
  • 渐变阴影: 使用 startColor 和 endColor 实现渐变阴影效果
  • 选择性阴影: 使用 sides 和 corners 属性选择性渲染阴影
  • 内部阴影: 使用 paintInside 实现内部阴影效果
  • 安全渲染: 使用 safeRender 处理特殊形状的首屏渲染

⚠️ 注意事项与最佳实践

1. 配置 react-native-svg(重要)

[!WARNING] react-native-shadow-2 依赖 react-native-svg,必须先配置 react-native-svg!

# 安装 react-native-svg
npm install react-native-svg

# 按照 react-native-svg 文档配置原生端代码
2. 阴影颜色使用

使用带透明度的颜色值,避免阴影过于突兀:

// 正确:使用带透明度的颜色
<Shadow
  startColor="#409EFF40"
  endColor="#409EFF00"
>
  <View style={styles.box} />
</Shadow>

// 错误:使用不透明颜色
<Shadow
  startColor="#409EFF"
  endColor="#409EFF"
>
  <View style={styles.box} />
</Shadow>
3. distance 参数配置

合理设置 distance 参数,避免阴影过大或过小:

// 小阴影:适合小元素
<Shadow distance={8}>

// 中等阴影:适合卡片、按钮
<Shadow distance={15}>

// 大阴影:适合强调元素
<Shadow distance={30}>
4. offset 参数使用

使用 offset 参数实现特定方向的阴影效果:

// 向下偏移:悬浮效果
<Shadow offset={[0, 4]} distance={8}>

// 向上偏移:凹陷效果
<Shadow offset={[0, -4]} distance={8}>

// 对角偏移:立体效果
<Shadow offset={[5, 5]} distance={12}>
5. 鸿蒙端限制(重要)

[!WARNING] 由于 react-native-shadow-2 强依赖 react-native-svg,但 react-native-svg 鸿蒙端目前仅实现少部分属性,导致以下属性在鸿蒙端不可用

  • offset: 阴影偏移(水平和垂直)
  • paintInside: 内部阴影
  • corners: 角落选择性渲染

原因: react-native-svg issue#5

解决方案: 仅使用以下鸿蒙端支持的属性:

// ✅ 鸿蒙端支持的属性
<Shadow
  distance={10}           // ✅ 支持
  startColor="#409EFF40"   // ✅ 支持
  endColor="#409EFF00"     // ✅ 支持
  sides={{...}}            // ✅ 支持(不含 corners)
  style={{...}}            // ✅ 支持
  containerStyle={{...}}   // ✅ 支持
  stretch={true}           // ✅ 支持
  safeRender={true}        // ✅ 支持
  disabled={false}         // ✅ 支持
>
  <View style={styles.box} />
</Shadow>

// ❌ 鸿蒙端不支持的属性
<Shadow
  offset={[5, 5]}         // ❌ 不可用
  paintInside={true}       // ❌ 不可用
  corners={{...}}          // ❌ 不可用
>
  <View style={styles.box} />
</Shadow>
6. sides 选择性渲染

使用 sides 属性选择性渲染特定边的阴影:

// 仅底部阴影
<Shadow
  sides={{ start: false, end: false, top: false, bottom: true }}
>

// 左右阴影
<Shadow
  sides={{ start: true, end: true, top: false, bottom: false }}
>

// 顶部和底部阴影
<Shadow
  sides={{ start: false, end: false, top: true, bottom: true }}
>
7. corners 选择性渲染

使用 corners 属性选择性渲染特定角的阴影:

// 对角阴影
<Shadow
  corners={{
    topStart: true,
    topEnd: false,
    bottomStart: false,
    bottomEnd: true,
  }}
>

// 顶部阴影
<Shadow
  corners={{
    topStart: true,
    topEnd: true,
    bottomStart: false,
    bottomEnd: false,
  }}
>
7. corners 选择性渲染(鸿蒙端不可用)

[!WARNING] corners 属性在鸿蒙端不可用,请勿使用!

// ❌ 鸿蒙端不可用
<Shadow
  corners={{
    topStart: true,
    topEnd: false,
    bottomStart: false,
    bottomEnd: true,
  }}
>
  <View style={styles.box} />
</Shadow>

如需实现类似效果,可以考虑使用原生 shadow 属性或等待 react-native-svg 鸿蒙端完整实现。

8. safeRender 使用

对于圆形或其他特殊形状,使用 safeRender 避免首屏渲染问题:

<Shadow
  safeRender={true}
  distance={10}
>
  <View style={styles.circle} />
</Shadow>
9. disabled 状态

使用 disabled 属性动态控制阴影效果:

const [disabled, setDisabled] = useState(false);

<Shadow disabled={disabled}>
  <TouchableOpacity style={styles.button}>
    <Text>按钮</Text>
  </TouchableOpacity>
</Shadow>
10. 性能优化

避免在列表中使用过多的 Shadow 组件,影响性能:

// 不推荐:每个列表项都使用 Shadow
<FlatList
  data={items}
  renderItem={({ item }) => (
    <Shadow>
      <View style={styles.item} />
    </Shadow>
  )}
/>

// 推荐:仅对重要元素使用 Shadow
<FlatList
  data={items}
  renderItem={({ item }) => (
    <View style={styles.item} />
  )}
/>
11. 版本兼容性

根据 RN 版本选择对应的库版本:

RN 版本 库版本
0.72 7.0.8
0.77 7.1.1

📊 对比:原生 shadow vs react-native-shadow-2

特性 原生 shadow react-native-shadow-2
渐变阴影 ❌ 不支持 ✅ 支持
选择性渲染 ❌ 不支持 ✅ 支持
内部阴影 ❌ 不支持 ✅ 支持
阴影偏移 ⚠️ 仅支持垂直 ✅ 支持水平和垂直
角落控制 ❌ 不支持 ✅ 支持
性能 ✅ 最佳 ✅ 优秀
使用复杂度 ✅ 简单 ⚠️ 中等
视觉效果 ⚠️ 基础 ✅ 丰富

📝 总结

通过集成 react-native-shadow-2,我们为项目添加了强大的立体阴影效果能力。这个库基于 SVG 技术实现,提供了丰富多样的阴影效果,可以轻松创建出精美的立体视觉效果,大大提升了应用的视觉体验。

关键要点回顾
  • 安装依赖: npm install react-native-shadow-2
  • 配置 SVG: 必须先配置 react-native-svg
  • 集成代码: 使用 Shadow 组件提供的各种属性
  • 支持功能: 渐变阴影、选择性渲染、内部阴影、安全渲染等
  • 测试验证: 确保三端表现一致
  • 重要提醒: 本库依赖 react-native-svg,必须先配置 SVG 支持
常用属性快速参考
// 基础阴影
<Shadow distance={10}>
  <View style={styles.box} />
</Shadow>

// 渐变阴影
<Shadow
  startColor="#409EFF40"
  endColor="#409EFF00"
  distance={15}
>
  <View style={styles.box} />
</Shadow>

// 大距离阴影
<Shadow
  distance={25}
>
  <View style={styles.box} />
</Shadow>

// 注意:offset 属性在鸿蒙端不可用

// 选择性边阴影
<Shadow
  sides={{ start: false, end: false, top: false, bottom: true }}
>
  <View style={styles.box} />
</Shadow>

// 选择性边阴影
<Shadow
  sides={{ start: false, end: false, top: false, bottom: true }}
>
  <View style={styles.box} />
</Shadow>

// 注意:corners 属性在鸿蒙端不可用

// 安全渲染
<Shadow
  safeRender={true}
  distance={10}
>
  <View style={styles.circle} />
</Shadow>

// 禁用阴影
<Shadow disabled={true}>
  <View style={styles.box} />
</Shadow>
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐