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

📌 开发环境声明:本文基于 React Native 0.72.90 版本进行开发适配


🚀 一、开篇引言

在移动应用开发中,图片选择和裁剪是最常见的功能需求之一。无论是用户头像上传、身份证拍照、还是商品图片编辑,都需要一个强大且易用的图片处理组件。本文将带你深入了解如何在 HarmonyOS 平台上集成和使用 react-native-image-crop-picker,实现完整的图片选择与裁剪功能。

1.1 你将学到什么?

  • ✅ react-native-image-crop-picker 的核心概念与工作原理
  • ✅ HarmonyOS 平台的完整集成流程
  • ✅ 相册选择、相机拍照、图片裁剪的实现
  • ✅ API 的深度解析与调用示例
  • ✅ 常见问题的解决方案

1.2 适用人群

  • 正在进行 React Native 鸿蒙化迁移的开发者
  • 需要实现图片选择裁剪功能的应用开发者
  • 对跨平台图片处理感兴趣的技术爱好者

📦 二、库概览

2.1 基本信息

项目 内容
库名称 @react-native-ohos/react-native-image-crop-picker
原库名称 react-native-image-crop-picker
版本信息 0.40.5 (RN 0.72) / 0.50.2 (RN 0.77) / 0.51.2 (RN 0.82)
官方仓库 https://github.com/react-native-oh-library/react-native-image-crop-picker
开源协议 MIT

2.2 核心能力矩阵

能力项 描述 HarmonyOS 支持
相册选择 从相册选择图片/视频 ✅ 完全支持
相机拍照 调用相机拍照/录像 ✅ 完全支持
图片裁剪 自由裁剪、圆形裁剪 ✅ 完全支持
多图选择 支持批量选择多张图片 ✅ 完全支持
Base64 编码 获取图片 Base64 数据 ✅ 完全支持
图片压缩 压缩图片质量和尺寸 ✅ 完全支持

2.3 技术架构图

原生平台层

Bridge Layer

React Native 应用层

ImagePicker API (JS/TS)

openPicker()

openCamera()

openCropper()

Native Module

ImageCropPickerPackage

ImageCropPickerManager

Android
UCrop + Camera

iOS
TOCropViewController

HarmonyOS
ImageEditAbility

2.4 与 react-native-image-picker 对比

特性 react-native-image-picker react-native-image-crop-picker
相册选择
相机拍照 ❌ 文档暂不支持
图片裁剪 ❌ 需额外库 ✅ 内置支持
自由裁剪
圆形裁剪
多图选择
视频选择
HarmonyOS 支持

2.5 典型应用场景

场景 描述 示例
头像上传 拍照或选择图片后裁剪 👤 圆形头像裁剪
身份证拍照 拍照并裁剪为指定比例 🪪 证件照采集
商品图片 多图选择并批量处理 🛍️ 商品图片上传
图片编辑 对已有图片进行二次裁剪 ✂️ 图片编辑器
视频选择 从相册选择视频文件 🎬 视频上传

⚡ 三、快速开始

3.1 环境要求

依赖项 版本要求
React Native 0.72.x
RNOH (鸿蒙框架) 0.72.90
HarmonyOS SDK 6.0.0.47+ (API 20)
DevEco Studio 5.0.3+ / 6.0+
Node.js 16.18.0+ / 18.x

3.2 一键安装

创建鸿蒙项目的过程不再进行描述,不懂得看这篇:https://blog.csdn.net/u011178696/article/details/151932277
在这里插入图片描述

# RN 0.72 版本
npm install @react-native-ohos/react-native-image-crop-picker@0.40.5-rc.1

# RN 0.77 版本
npm install @react-native-ohos/react-native-image-crop-picker@0.50.2

3.3 验证安装

# 检查 package.json
type package.json | findstr image-crop-picker

# 预期输出
# "@react-native-ohos/react-native-image-crop-picker": "^0.40.5-rc.1"

🔧 四、HarmonyOS 集成详解

4.1 配置清单

📌 按顺序完成以下配置,缺一不可

步骤 配置文件 操作 重要程度
1 harmony/oh-package.json5 添加 overrides ⭐⭐⭐
2 harmony/entry/oh-package.json5 添加依赖 ⭐⭐⭐
3 harmony/entry/src/main/cpp/CMakeLists.txt 配置编译链接 ⭐⭐⭐
4 harmony/entry/src/main/cpp/PackageProvider.cpp 引入头文件 ⭐⭐⭐
5 harmony/entry/src/main/ets/RNPackagesFactory.ts 引入包 ⭐⭐⭐
6 harmony/entry/src/main/module.json5 配置权限 ⭐⭐⭐
7 ImageEditAbility 创建裁剪页面 ⭐⭐⭐

4.2 方式一:HAR 包引入(推荐)

HAR 包引入方式简单快捷,适合大多数场景。

步骤一:配置 overrides

在这里插入图片描述

打开 harmony/oh-package.json5,添加以下配置:

{
  "overrides": {
    "@rnoh/react-native-openharmony": "0.72.90"
  }
}
步骤二:添加依赖

打开 harmony/entry/oh-package.json5,添加依赖:

"dependencies": {
  "@rnoh/react-native-openharmony": "0.72.90",
  "@react-native-ohos/react-native-image-crop-picker": "file:../../node_modules/@react-native-ohos/react-native-image-crop-picker/harmony/image_crop_picker.har"
}
步骤三:同步依赖

点击 DevEco Studio 右上角的 sync 按钮,或者在终端执行:

cd harmony/entry
ohpm install
步骤四:配置 CMakeLists.txt

打开 harmony/entry/src/main/cpp/CMakeLists.txt,添加:

set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")

add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-image-crop-picker/src/main/cpp" ./image-crop-picker)

target_link_libraries(rnoh_app PUBLIC rnoh_image_crop_picker)
步骤五:修改 PackageProvider.cpp

打开 harmony/entry/src/main/cpp/PackageProvider.cpp,添加:

#include "ImageCropPickerPackage.h"

std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
    return {
        std::make_shared<RNOHGeneratedPackage>(ctx),
        std::make_shared<ImageCropPickerPackage>(ctx),
    };
}
步骤六:引入 ImageCropPickerPackage

打开 harmony/entry/src/main/ets/RNPackagesFactory.ts,添加:

import { ImageCropPickerPackage } from '@react-native-ohos/react-native-image-crop-picker/ts';

export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
  return [
    new ImageCropPickerPackage(ctx),
  ];
}

4.3 方式二:源码引入

源码引入方式适合需要调试或修改原生代码的场景。

步骤一:配置 overrides

打开 harmony/oh-package.json5,添加以下配置:

{
  "overrides": {
    "@rnoh/react-native-openharmony": "0.72.90"
  }
}
步骤二:复制源码模块

node_modules/@react-native-ohos/react-native-image-crop-picker/harmony/image_crop_picker 复制到 harmony/ 目录下。

步骤三:添加模块配置

打开 harmony/build-profile.json5,添加模块:

modules: [
  {
    name: 'image_crop_picker',
    srcPath: './image_crop_picker',
  }
]
步骤四:修改 image_crop_picker/oh-package.json5

打开 harmony/image_crop_picker/oh-package.json5,修改 react-native-openharmony 版本与项目版本一致:

{
  "dependencies": {
    "@rnoh/react-native-openharmony": "0.72.90"
  }
}
步骤五:添加依赖

打开 harmony/entry/oh-package.json5,添加依赖:

"dependencies": {
  "@rnoh/react-native-openharmony": "0.72.90",
  "@react-native-ohos/react-native-image-crop-picker": "file:../image_crop_picker"
}
步骤六:修改文件后缀

image_crop_picker/ts.ts 重命名为 image_crop_picker/ts.ets,否则运行时会报错。

步骤七:同步依赖

点击 DevEco Studio 右上角的 sync 按钮,或者在终端执行:

cd harmony/entry
ohpm install
步骤八:配置 CMakeLists.txt

打开 harmony/entry/src/main/cpp/CMakeLists.txt,添加:

set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")

add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-image-crop-picker/src/main/cpp" ./image-crop-picker)

target_link_libraries(rnoh_app PUBLIC rnoh_image_crop_picker)
步骤九:修改 PackageProvider.cpp

打开 harmony/entry/src/main/cpp/PackageProvider.cpp,添加:

#include "ImageCropPickerPackage.h"

std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
    return {
        std::make_shared<RNOHGeneratedPackage>(ctx),
        std::make_shared<ImageCropPickerPackage>(ctx),
    };
}
步骤十:引入 ImageCropPickerPackage

打开 harmony/entry/src/main/ets/RNPackagesFactory.ts,添加:

import { ImageCropPickerPackage } from '@react-native-ohos/react-native-image-crop-picker/ts';

export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
  return [
    new ImageCropPickerPackage(ctx),
  ];
}

4.4 权限配置

打开 harmony/entry/src/main/module.json5,添加以下权限:

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.READ_MEDIA",
        "reason": "$string:read_media_reason",
        "usedScene": {
          "abilities": ["EntryAbility"],
          "when": "inuse"
        }
      },
      {
        "name": "ohos.permission.WRITE_MEDIA",
        "reason": "$string:write_media_reason",
        "usedScene": {
          "abilities": ["EntryAbility"],
          "when": "inuse"
        }
      },
      {
        "name": "ohos.permission.CAMERA",
        "reason": "$string:camera_reason",
        "usedScene": {
          "abilities": ["EntryAbility"],
          "when": "inuse"
        }
      }
    ]
  }
}

然后在 harmony/entry/src/main/resources/base/element/string.json 中添加对应的 reason 说明:

{
  "string": [
    {
      "name": "read_media_reason",
      "value": "用于从相册选择图片"
    },
    {
      "name": "write_media_reason",
      "value": "用于保存裁剪后的图片"
    },
    {
      "name": "camera_reason",
      "value": "用于拍照获取图片"
    }
  ]
}

4.5 创建 ImageEditAbility(裁剪功能必需)

⚠️ 重要:该模块的内容无法通过 Autolink 自动生成,始终需要手动配置。缺少此配置将导致裁剪功能无法正常工作。

步骤一:创建 ImageEditAbility.ets

harmony/entry/src/main/ets/entryability 目录下创建 ImageEditAbility.ets 文件:

import UIAbility from '@ohos.app.ability.UIAbility'
import window from '@ohos.window'
import { BusinessError } from "@ohos.base";

const TAG = 'ImageEditAbility';

export default class ImageEditAbility extends UIAbility {

  onWindowStageCreate(windowStage: window.WindowStage) {
    this.setWindowOrientation(windowStage, window.Orientation.PORTRAIT)
    windowStage.loadContent('pages/ImageEdit', (err, data) => {
      let windowClass: window.Window = windowStage.getMainWindowSync()
      let isLayoutFullScreen = true
      windowClass.setWindowLayoutFullScreen(isLayoutFullScreen).then(() => {
        console.info('Succeeded in setting the window layout to full-screen mode.')
      }).catch((err: BusinessError) => {
        console.error(`Failed to set the window layout to full-screen mode. Code is ${err.code}, message is ${err.message}`)
      })

      let type = window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR;
      let avoidArea = windowClass.getWindowAvoidArea(type);
      let bottomRectHeight = avoidArea.bottomRect.height;
      AppStorage.setOrCreate('bottomRectHeight', bottomRectHeight);

      type = window.AvoidAreaType.TYPE_SYSTEM;
      avoidArea = windowClass.getWindowAvoidArea(type);
      let topRectHeight = avoidArea.topRect.height;
      AppStorage.setOrCreate('topRectHeight', topRectHeight);

      windowClass.on('avoidAreaChange', (data) => {
        if (data.type === window.AvoidAreaType.TYPE_SYSTEM) {
          let topRectHeight = data.area.topRect.height;
          AppStorage.setOrCreate('topRectHeight', topRectHeight);
        } else if (data.type == window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR) {
          let bottomRectHeight = data.area.bottomRect.height;
          AppStorage.setOrCreate('bottomRectHeight', bottomRectHeight);
        }
      });

      if (err.code) {
        console.info(TAG, 'Failed to load the content. Cause: %{public}s',
          JSON.stringify(err) ?? '')
        return;
      }
      console.info(TAG, 'Succeeded in loading the content')
    });
    try {
      windowStage.getMainWindowSync().setWindowLayoutFullScreen(true, (err) => {
        if (err.code) {
          console.error('Failed to enable the full-screen mode. Cause: ' + JSON.stringify(err));
          return;
        }
        console.info('Succeeded in enabling the full-screen mode.');
      })
    } catch (exception) {
      console.error('Failed to set the system bar to be invisible. Cause: ' + JSON.stringify(exception));
    }
  }

  setWindowOrientation(stage: window.WindowStage, orientation: window.Orientation): void {
    console.info(TAG, "into setWindowOrientation :")
    if (!stage || !orientation) {
      return;
    }
    stage.getMainWindow().then(windowInstance => {
      windowInstance.setPreferredOrientation(orientation);
    })
  }

  onBackground() {
    this.context.terminateSelf();
  }
}
步骤二:注册 ImageEditAbility

打开 harmony/entry/src/main/module.json5,在 abilities 数组中添加:

{
  "module": {
    "abilities": [
      {
        "name": "ImageEditAbility",
        "srcEntry": "./ets/entryability/ImageEditAbility.ets",
        "description": "$string:EntryAbility_desc",
        "icon": "$media:icon",
        "startWindowIcon": "$media:startIcon",
        "startWindowBackground": "$color:start_window_background",
        "removeMissionAfterTerminate": true
      }
    ]
  }
}
步骤三:创建 ImageEdit.ets 页面

harmony/entry/src/main/ets/pages 目录下创建 ImageEdit.ets 文件:

import { ImageEditInfo } from '@react-native-ohos/react-native-image-crop-picker';

@Entry
@Component
struct ImageEdit {
  @State cropperCircleOverlay: boolean = false;

  aboutToAppear(): void {
    this.cropperCircleOverlay = AppStorage.Get('cropperCircleOverlay') || false
  }

  build() {
    Row() {
      Column() {
        if (!this.cropperCircleOverlay) {
          ImageEditInfo()
        } 
      }
      .width('100%')
    }
    .height('100%')
  }
}
步骤四:配置 main_pages.json

打开 harmony/entry/src/main/resources/base/profile/main_pages.json,添加 ImageEdit 页面:

{
  "src": [
    "pages/Index",
    "pages/ImageEdit"
  ]
}

4.6 同步并运行

点击 DevEco Studio 右上角的 sync 按钮,然后编译、运行即可。


💻 五、实战演练

在这里插入图片描述
在这里插入图片描述

场景:图片选择与裁剪

需求描述:实现从相册选择图片并进行裁剪的功能。

实现思路

  1. 使用 openPicker() 从相册选择图片
  2. 配置裁剪参数,如宽高、是否圆形裁剪等
  3. 处理裁剪后的图片路径

实现代码

import React, { useState } from 'react';
import {
  View,
  Text,
  StyleSheet,
  ScrollView,
  TouchableOpacity,
  SafeAreaView,
  Image,
  Alert,
} from 'react-native';
import ImagePicker from 'react-native-image-crop-picker';

export default function App() {
  const [avatar, setAvatar] = useState<string | null>(null);
  const [imageInfo, setImageInfo] = useState<string>('');

  const pickAndCropImage = async () => {
    try {
      const image = await ImagePicker.openPicker({
        width: 300,
        height: 300,
        cropping: true,
        cropperCircleOverlay: true,
        compressImageQuality: 0.8,
      });
      setAvatar(image.path);
      setImageInfo(`尺寸: ${image.width}x${image.height}\n大小: ${(image.size / 1024).toFixed(2)}KB`);
    } catch (error) {
      console.log('用户取消选择');
    }
  };

  const takePhoto = async () => {
    try {
      const image = await ImagePicker.openCamera({
        width: 400,
        height: 300,
        cropping: true,
        mediaType: 'photo',
      });
      setAvatar(image.path);
      setImageInfo(`尺寸: ${image.width}x${image.height}`);
    } catch (error) {
      console.log('拍照失败:', error);
    }
  };

  const pickMultiple = async () => {
    try {
      const images = await ImagePicker.openPicker({
        multiple: true,
        maxFiles: 5,
        mediaType: 'photo',
      });
      Alert.alert('提示', `已选择 ${images.length} 张图片`);
    } catch (error) {
      console.log('选择失败:', error);
    }
  };

  return (
    <SafeAreaView style={styles.container}>
      <ScrollView style={styles.content} contentContainerStyle={styles.scrollContent}>
        <Text style={styles.title}>Image Crop Picker 演示</Text>

        <View style={styles.avatarSection}>
          {avatar ? (
            <Image source={{ uri: avatar }} style={styles.avatar} />
          ) : (
            <View style={styles.avatarPlaceholder}>
              <Text style={styles.avatarPlaceholderText}>点击下方按钮选择图片</Text>
            </View>
          )}
          {imageInfo ? <Text style={styles.imageInfo}>{imageInfo}</Text> : null}
        </View>

        <View style={styles.section}>
          <Text style={styles.sectionTitle}>头像选择(圆形裁剪)</Text>
          <TouchableOpacity style={styles.button} onPress={pickAndCropImage}>
            <Text style={styles.buttonText}>从相册选择并裁剪</Text>
          </TouchableOpacity>
        </View>

        <View style={styles.section}>
          <Text style={styles.sectionTitle}>拍照</Text>
          <TouchableOpacity style={[styles.button, styles.cameraButton]} onPress={takePhoto}>
            <Text style={styles.buttonText}>打开相机拍照</Text>
          </TouchableOpacity>
        </View>

        <View style={styles.section}>
          <Text style={styles.sectionTitle}>多图选择</Text>
          <TouchableOpacity style={[styles.button, styles.multiButton]} onPress={pickMultiple}>
            <Text style={styles.buttonText}>选择多张图片(最多5张)</Text>
          </TouchableOpacity>
        </View>
      </ScrollView>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f5f5f5',
  },
  content: {
    flex: 1,
  },
  scrollContent: {
    padding: 20,
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#333',
    textAlign: 'center',
    marginBottom: 30,
  },
  avatarSection: {
    alignItems: 'center',
    marginBottom: 30,
  },
  avatar: {
    width: 150,
    height: 150,
    borderRadius: 75,
    marginBottom: 10,
  },
  avatarPlaceholder: {
    width: 150,
    height: 150,
    borderRadius: 75,
    backgroundColor: '#e0e0e0',
    alignItems: 'center',
    justifyContent: 'center',
    marginBottom: 10,
  },
  avatarPlaceholderText: {
    color: '#999',
    fontSize: 14,
    textAlign: 'center',
    paddingHorizontal: 20,
  },
  imageInfo: {
    fontSize: 12,
    color: '#666',
    textAlign: 'center',
  },
  section: {
    backgroundColor: '#fff',
    borderRadius: 12,
    padding: 16,
    marginBottom: 16,
  },
  sectionTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#333',
    marginBottom: 12,
  },
  button: {
    backgroundColor: '#007AFF',
    paddingVertical: 12,
    paddingHorizontal: 24,
    borderRadius: 8,
    alignItems: 'center',
  },
  buttonText: {
    color: '#fff',
    fontSize: 16,
    fontWeight: '600',
  },
  cameraButton: {
    backgroundColor: '#34C759',
  },
  multiButton: {
    backgroundColor: '#FF9500',
  },
});

📖 六、API 详解

6.1 openPicker - 从相册选择图片

从相册中选择图片或视频,支持单选、多选、裁剪等功能。

属性名 类型 说明 默认值
width number 裁剪后的图片宽度 200
height number 裁剪后的图片高度 200
cropping boolean 是否启用裁剪 false
multiple boolean 是否多选 false
maxFiles number 最大选择数量 5
mediaType string 媒体类型:photo/video/any ‘photo’
includeBase64 boolean 是否返回 Base64 数据 false
compressImageQuality number 图片压缩质量 (0-1) 1
cropperCircleOverlay boolean 是否圆形裁剪框 false
cropperToolbarTitle string 裁剪页面标题 ‘’

调用示例

import ImagePicker from 'react-native-image-crop-picker';

// 基础选择 - 单张图片
const pickSingle = async () => {
  try {
    const image = await ImagePicker.openPicker({
      width: 300,
      height: 400,
      cropping: true,
    });
    console.log('图片路径:', image.path);
    console.log('图片尺寸:', image.width, 'x', image.height);
  } catch (error) {
    console.log('用户取消选择');
  }
};

// 多图选择
const pickMultiple = async () => {
  try {
    const images = await ImagePicker.openPicker({
      multiple: true,
      maxFiles: 5,
      mediaType: 'photo',
    });
    console.log('已选择', images.length, '张图片');
    images.forEach((img, index) => {
      console.log(`图片${index + 1}:`, img.path);
    });
  } catch (error) {
    console.log('选择失败:', error);
  }
};

// 圆形裁剪(头像)
const pickAvatar = async () => {
  try {
    const avatar = await ImagePicker.openPicker({
      width: 200,
      height: 200,
      cropping: true,
      cropperCircleOverlay: true,
      compressImageQuality: 0.8,
    });
    console.log('头像路径:', avatar.path);
  } catch (error) {
    console.log('用户取消选择');
  }
};

// 获取 Base64 数据
const pickWithBase64 = async () => {
  try {
    const image = await ImagePicker.openPicker({
      width: 300,
      height: 300,
      cropping: true,
      includeBase64: true,
    });
    console.log('Base64数据长度:', image.data?.length);
  } catch (error) {
    console.log('选择失败:', error);
  }
};

// 选择视频
const pickVideo = async () => {
  try {
    const video = await ImagePicker.openPicker({
      mediaType: 'video',
    });
    console.log('视频路径:', video.path);
    console.log('视频时长:', video.duration, '秒');
  } catch (error) {
    console.log('选择失败:', error);
  }
};

6.2 openCamera - 从相机拍照

调用相机进行拍照或录像。

属性名 类型 说明 默认值
width number 裁剪后的图片宽度 200
height number 裁剪后的图片高度 200
cropping boolean 是否启用裁剪 false
mediaType string 媒体类型:photo/video ‘photo’
useFrontCamera boolean 是否使用前置摄像头 false
cropperCircleOverlay boolean 是否圆形裁剪框 false

调用示例

import ImagePicker from 'react-native-image-crop-picker';

// 基础拍照
const takePhoto = async () => {
  try {
    const image = await ImagePicker.openCamera({
      width: 300,
      height: 400,
      cropping: true,
    });
    console.log('照片路径:', image.path);
  } catch (error) {
    console.log('拍照失败:', error);
  }
};

// 自拍(前置摄像头 + 圆形裁剪)
const takeSelfie = async () => {
  try {
    const selfie = await ImagePicker.openCamera({
      width: 200,
      height: 200,
      cropping: true,
      useFrontCamera: true,
      cropperCircleOverlay: true,
    });
    console.log('自拍路径:', selfie.path);
  } catch (error) {
    console.log('拍照失败:', error);
  }
};

// 录制视频
const recordVideo = async () => {
  try {
    const video = await ImagePicker.openCamera({
      mediaType: 'video',
    });
    console.log('视频路径:', video.path);
    console.log('视频时长:', video.duration, '秒');
  } catch (error) {
    console.log('录制失败:', error);
  }
};

// 拍照并压缩
const takeCompressedPhoto = async () => {
  try {
    const image = await ImagePicker.openCamera({
      width: 800,
      height: 600,
      cropping: true,
      compressImageQuality: 0.7,
    });
    console.log('压缩后大小:', (image.size / 1024).toFixed(2), 'KB');
  } catch (error) {
    console.log('拍照失败:', error);
  }
};

6.3 openCropper - 裁剪已有图片

对已有的图片进行裁剪,适用于二次编辑场景。

属性名 类型 说明 默认值
path string 图片路径(必填) -
width number 裁剪后的图片宽度 200
height number 裁剪后的图片高度 200
cropperCircleOverlay boolean 是否圆形裁剪框 false
freeStyleCropEnabled boolean 是否自由裁剪 false
cropperToolbarTitle string 裁剪页面标题 ‘’

调用示例

import ImagePicker from 'react-native-image-crop-picker';

// 基础裁剪
const cropImage = async (imagePath: string) => {
  try {
    const croppedImage = await ImagePicker.openCropper({
      path: imagePath,
      width: 300,
      height: 300,
    });
    console.log('裁剪后路径:', croppedImage.path);
  } catch (error) {
    console.log('裁剪失败:', error);
  }
};

// 圆形裁剪(头像编辑)
const cropAvatar = async (imagePath: string) => {
  try {
    const avatar = await ImagePicker.openCropper({
      path: imagePath,
      width: 200,
      height: 200,
      cropperCircleOverlay: true,
      cropperToolbarTitle: '编辑头像',
    });
    console.log('头像路径:', avatar.path);
  } catch (error) {
    console.log('裁剪失败:', error);
  }
};

// 自由裁剪
const freeStyleCrop = async (imagePath: string) => {
  try {
    const croppedImage = await ImagePicker.openCropper({
      path: imagePath,
      freeStyleCropEnabled: true,
      cropperToolbarTitle: '自由裁剪',
    });
    console.log('裁剪后尺寸:', croppedImage.width, 'x', croppedImage.height);
  } catch (error) {
    console.log('裁剪失败:', error);
  }
};

// 固定比例裁剪
const cropWithRatio = async (imagePath: string) => {
  try {
    const croppedImage = await ImagePicker.openCropper({
      path: imagePath,
      width: 400,
      height: 300,
      cropperToolbarTitle: '16:9 裁剪',
    });
    console.log('裁剪后路径:', croppedImage.path);
  } catch (error) {
    console.log('裁剪失败:', error);
  }
};

6.4 clean - 清除临时文件

清除所有临时文件,释放存储空间。

调用示例

import ImagePicker from 'react-native-image-crop-picker';

// 清除所有临时文件
const cleanAll = async () => {
  try {
    await ImagePicker.clean();
    console.log('临时文件已清除');
  } catch (error) {
    console.log('清除失败:', error);
  }
};

// 清除单个文件
const cleanSingle = async (filePath: string) => {
  try {
    await ImagePicker.cleanSingle(filePath);
    console.log('文件已删除:', filePath);
  } catch (error) {
    console.log('删除失败:', error);
  }
};

6.5 返回数据结构

选择或裁剪完成后,返回的图片对象结构:

属性名 类型 说明
path string 图片路径
size number 文件大小(字节)
width number 图片宽度
height number 图片高度
mime string MIME 类型(如 image/jpeg)
filename string 文件名
data string Base64 数据(需设置 includeBase64)
duration number 视频时长(秒,仅视频)
creationDate string 创建日期

使用示例

const image = await ImagePicker.openPicker({
  width: 300,
  height: 300,
  cropping: true,
  includeBase64: true,
});

console.log('路径:', image.path);
console.log('尺寸:', image.width, 'x', image.height);
console.log('大小:', (image.size / 1024).toFixed(2), 'KB');
console.log('类型:', image.mime);
console.log('文件名:', image.filename);
if (image.data) {
  console.log('Base64长度:', image.data.length);
}

🎨 七、进阶技巧

7.1 图片压缩优化

// 根据图片大小动态调整压缩质量
const pickWithSmartCompress = async () => {
  try {
    const image = await ImagePicker.openPicker({
      width: 800,
      height: 800,
      cropping: true,
      compressImageQuality: 0.7,
    });
    
    // 如果仍然过大,可以进一步压缩
    if (image.size > 500 * 1024) {
      console.log('图片较大,建议进一步处理');
    }
    
    return image;
  } catch (error) {
    console.log('选择失败:', error);
  }
};

7.2 错误处理最佳实践

const safePickImage = async () => {
  try {
    const image = await ImagePicker.openPicker({
      width: 300,
      height: 300,
      cropping: true,
    });
    return { success: true, data: image };
  } catch (error: any) {
    if (error.message?.includes('cancel')) {
      return { success: false, error: '用户取消选择' };
    }
    if (error.message?.includes('permission')) {
      return { success: false, error: '请授予相册访问权限' };
    }
    return { success: false, error: '选择图片失败' };
  }
};

❓ 八、常见问题

8.1 裁剪功能无法使用

问题原因:未配置 ImageEditAbility

解决方案

  1. 确保创建 ImageEditAbility.ets 文件
  2. 在 module.json5 中注册 ImageEditAbility
  3. 创建 ImageEdit.ets 页面
  4. 在 main_pages.json 中添加页面路径

8.2 权限被拒绝

问题原因:未正确配置权限或用户拒绝授权

解决方案

  1. 检查 module.json5 中是否配置了必要权限
  2. 引导用户在系统设置中开启权限

8.3 图片选择后无响应

问题原因:Promise 未正确处理

解决方案

// ✅ 正确处理
try {
  const image = await ImagePicker.openPicker({...});
} catch (error) {
  console.log('用户可能取消了选择');
}

// ❌ 错误处理
const image = await ImagePicker.openPicker({...}); // 未处理异常

📊 九、性能优化

9.1 图片压缩策略

// 根据用途选择合适的尺寸和质量
const getImageConfig = (purpose: 'avatar' | 'banner' | 'thumbnail') => {
  const configs = {
    avatar: { width: 200, height: 200, quality: 0.8 },
    banner: { width: 1200, height: 600, quality: 0.9 },
    thumbnail: { width: 100, height: 100, quality: 0.6 },
  };
  return configs[purpose];
};

const pickImage = async (purpose: 'avatar' | 'banner' | 'thumbnail') => {
  const config = getImageConfig(purpose);
  return await ImagePicker.openPicker({
    width: config.width,
    height: config.height,
    cropping: true,
    compressImageQuality: config.quality,
  });
};

9.2 及时清理临时文件

// 在组件卸载时清理
useEffect(() => {
  return () => {
    ImagePicker.clean().catch(console.log);
  };
}, []);

🎯 十、总结

10.1 核心要点回顾

知识点 要点
API 选择 openPicker 相册、openCamera 相机、openCropper 裁剪
裁剪配置 ImageEditAbility 必须手动配置
权限配置 READ_MEDIA、WRITE_MEDIA、CAMERA
错误处理 使用 try-catch 处理用户取消等情况
性能优化 根据用途选择合适的尺寸和压缩质量
Logo

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

更多推荐