ReactNative for OpenHarmony项目鸿蒙化三方库:react-native-image-crop-picker — 图片选择裁剪组件
知识点要点API 选择openPicker 相册、openCamera 相机、openCropper 裁剪裁剪配置ImageEditAbility 必须手动配置权限配置错误处理使用 try-catch 处理用户取消等情况性能优化根据用途选择合适的尺寸和压缩质量。
欢迎加入开源鸿蒙跨平台社区: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 技术架构图
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 按钮,然后编译、运行即可。
💻 五、实战演练


场景:图片选择与裁剪
需求描述:实现从相册选择图片并进行裁剪的功能。
实现思路:
- 使用
openPicker()从相册选择图片 - 配置裁剪参数,如宽高、是否圆形裁剪等
- 处理裁剪后的图片路径
实现代码:
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
解决方案:
- 确保创建 ImageEditAbility.ets 文件
- 在 module.json5 中注册 ImageEditAbility
- 创建 ImageEdit.ets 页面
- 在 main_pages.json 中添加页面路径
8.2 权限被拒绝
问题原因:未正确配置权限或用户拒绝授权
解决方案:
- 检查 module.json5 中是否配置了必要权限
- 引导用户在系统设置中开启权限
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 处理用户取消等情况 |
| 性能优化 | 根据用途选择合适的尺寸和压缩质量 |
更多推荐



所有评论(0)