react-native-image-editor:基于 React Native 鸿蒙生态的图片裁剪项目

图片裁剪编辑原生模块

Branch3Tags3

@hxa-rn/react-native-image-editor

本项目基于 react-native-image-editor 开发。如果在使用过程中有任何问题,可以在 GitCode Issues 提交 Issue,会及时跟进。

项目介绍

@react-native-community/image-editor 提供图片裁剪能力:按调用方给出的偏移与尺寸从原图取矩形区域,可选地缩放到目标展示尺寸,并按指定格式(png / jpeg / webp)另存到应用缓存目录。

架构形态(务必先读):本仓在上游 Android / iOS 实现之外补齐了 HarmonyOS 平台实现,落成一个 ArkTS + C++ TurboModule(RNCImageEditor):裁剪矩形与 EXIF Orientation 换算以上游 Android ImageEditorModuleImpl.kt 为语义基准,displaySize 与 resizeMode 的缩放语义以 iOS RCTImageUtils.mm 的 RCTTargetRect 为基准。

面 鸿蒙侧处置
本地路径裁剪 file:// 与裸绝对路径均支持;裁剪矩形越界显式拒绝并给出可读原因
远程图片裁剪 支持 http(s) 下载后裁剪,可透传自定义请求头;下载失败携带底层原因
data URI 裁剪 支持 data: 内联 Base64;内容损坏时显式拒绝
缩放与 resizeMode displaySize 双维均大于 0 时触发缩放,contain / cover / stretch / center 四档,未知值回落 cover
输出格式 png / jpeg / webp 显式指定,MIME 与文件扩展名同步;未知格式回落源图 MIME 而非报错
EXIF JPEG 输出执行 EXIF 白名单拷贝;裁剪矩形按原图 EXIF Orientation 换算到解码坐标系
Android 专属 URI content:// 与 android.resource:// 在鸿蒙无对应物,显式拒绝并说明原因
结果落盘 结果写入应用缓存目录、文件名带 ReactNative_cropped_image_ 前缀,模块生命周期结束时按该前缀清理
  • 上游基线:上游 npm @react-native-community/image-editor 4.3.1(callstack/react-native-image-editor)
  • 当前发布版本:4.3.1(与上游基线同号)—— npm 包名为 @hxa-rn/react-native-image-editor;上游基线为 4.3.1,JS 公开面(ImageEditor.cropImage)不变。
  • 4.3.1 与 4.3.0 的区别:修复本地 JPEG EXIF Orientation 读取回退和输出方向保留;npm 包内的 HAR 改为打包时从当前鸿蒙源码重建。4.3.0 不含这些修复,请使用 4.3.1。
  • 本库同时提供 ETS 侧与 C++ 侧 codegen 产物,支持 RNOH autolink,宿主无需手动注册 Package 或手动链接 CMake。
  • 鸿蒙侧实现:npm 包内的 harmony/image_editor.har 是包含 ArkTS/C++ 源码的 HAR;HAR 内部 ohpm 模块名为 @oh-rn/image-editor。

鸿蒙使用者必读(3 条,不读会误判)

1. quality 的合法区间是 0~1 的小数 内部按 trunc(quality * 100) 使用。传 80 会得到 8000 而越界被拒,不等价于 0.8;越界时逐字报 quality must be a number between 0 and 1。

2. 不同宽高比缩放是「居中收缩」不是「拉伸」 源图 400×300 全图裁剪、目标 100×100 时,实际裁剪矩形为 (50, 0, 300×300)(左右各裁去 50 像素)再缩到 100×100。

3. 结果文件需要调用方自行清理 裁剪结果落在应用缓存目录,模块生命周期结束时按前缀清理;若调用方需要长期保留,请自行复制到持久目录。

集成指南

1. 从 npm 安装适配包

npm install @hxa-rn/react-native-image-editor@4.3.2 --save-exact --legacy-peer-deps

源码对应 GitCode 的 br_rnoh0.72 分支。

2. JS 导入与包名

第 1 步的 npm install 已在宿主 RN 工程根目录的 package.json 中写入以下依赖,无需手动添加,可打开该文件确认:

{
  "dependencies": {
    "@hxa-rn/react-native-image-editor": "4.3.1"
  }
}
包 作用
@hxa-rn/react-native-image-editor 鸿蒙适配 npm 包:JS/类型、harmony/image_editor.har 与 codegen 产物
@oh-rn/image-editor HAR 内部 ohpm 模块名,只用于 HarmonyOS 工程依赖和 ETS import

在调用裁剪的业务代码文件(如 App.tsx)中照常导入上游包名,不用改:

import ImageEditor from '@react-native-community/image-editor';

RNOH 打 bundle 时,createHarmonyMetroConfig 会按适配包 package.json 里的 harmony.alias 把上游包名重定向到本包;宿主根目录的 metro.config.js 需合并 RNOH Metro 配置(RNOH 工程模板默认如此)。

安装后 node_modules/@hxa-rn/react-native-image-editor/harmony/image_editor.har 应正常存在。

3. 接入鸿蒙原生模块

在宿主鸿蒙工程 harmony/entry/oh-package.json5 的 dependencies 中加入(第一行是宿主已有的 RNOH 依赖,下例为 RNOH 官方预编译包,与本仓示例相同;保持宿主原值即可):

{
  dependencies: {
    "@rnoh/react-native-openharmony": "file:../../node_modules/@react-native-oh/react-native-harmony/react_native_openharmony_release.har",
    "@oh-rn/image-editor": "../../node_modules/@hxa-rn/react-native-image-editor/harmony/image_editor.har",
  },
}

宿主 harmony/build-profile.json5 需在 app.products[] 的 buildOption.strictMode 中开启 useNormalizedOHMUrl(RNOH 要求,RNOH 工程模板默认已开启):

"buildOption": {
  "strictMode": {
    "useNormalizedOHMUrl": true
  }
}

然后在宿主 RN 工程根目录执行:

cd harmony
ohpm install

不需要手动注册 RNPackage,也不需要手动改 CMakeLists:@hxa-rn/react-native-image-editor 的 package.json 自带 harmony.alias / harmony.autolinking 字段(ETS 侧 etsPackageClassName: ImageEditorPackage、C++ 侧 cppPackageClassName: ImageEditorPackage),宿主 harmony/hvigorfile.ts 的 createRNOHModulePlugin 开启 autolinking 时(RNOH 工程模板默认如此),RNOH 自动链接机制能直接发现并接入。

4. 声明网络权限(远程图片裁剪时需要)

🔴 本库 HAR 已在自身 module.json5 声明 ohos.permission.INTERNET(用于「远程图片裁剪」下载源图),仅使用本地路径 / data URI 裁剪的宿主无需额外配置;若宿主整体权限清单由自身 module.json5 统一管理,请确认该权限未被移除。

5. 生成鸿蒙侧 JS Bundle

在宿主 RN 工程根目录按普通 RN 三方库流程打包即可:

npx react-native bundle-harmony --dev false

产物默认写入 harmony/entry/src/main/resources/rawfile/bundle.harmony.js。

使用说明

调用 ImageEditor.cropImage(uri, cropData),返回 Promise:

import ImageEditor from '@react-native-community/image-editor';

const result = await ImageEditor.cropImage(uri, {
  offset: { x: 10, y: 20 },
  size: { width: 200, height: 150 },
});
// result: { path, uri, name, width, height, size, type, base64? }

缩放到目标展示尺寸

displaySize 双维均大于 0 时触发缩放,resizeMode 决定构图方式。

await ImageEditor.cropImage(uri, {
  offset: { x: 0, y: 0 },
  size: { width: 60, height: 40 },
  displaySize: { width: 100, height: 100 },
  resizeMode: 'contain',
});

指定输出格式并取 base64

format 支持 png / jpeg / webp;includeBase64 开启后结果附带 base64 串。

await ImageEditor.cropImage(uri, {
  offset: { x: 0, y: 0 },
  size: { width: 100, height: 100 },
  format: 'webp',
  includeBase64: true,
});

接口文档

以下覆盖本库全部对外符号(JS 侧公开面 + 鸿蒙原生侧 TurboModule)。JS 侧 API 与上游 Android / iOS 实现同签名;鸿蒙侧差异集中在 URI 方案与解码期行为,逐项见「约束与限制」。

公开接口

公开面 = 1 个类 ImageEditor(default export)+ 1 个静态方法 cropImage,另有 2 个类型 ImageCropData / CropResult(在 src/types.ts 导出)。

接口 分类 说明
ImageEditor.cropImage(uri, cropData) Promise<CropResult> 按 offset/size 裁剪,可选缩放与格式转换,结果写入应用缓存目录

入参 cropData

接口 分类 说明
offset {x, y} 必填。裁剪区左上角(原图坐标系)
size {width, height} 必填。裁剪区尺寸(原图坐标系)
displaySize {width, height} 可选。裁剪后缩放到的目标尺寸,双维均大于 0 才触发
resizeMode 'contain'|'cover'|'stretch'|'center' 可选。缩放构图方式,未知值回落 cover
quality number 可选。压缩质量,0~1 的小数,越界被拒
format 'png'|'jpeg'|'webp' 可选。输出格式,未知值回落源图 MIME
includeBase64 boolean 可选,默认 false。结果是否附带 base64 串
headers Record<string,string>|Headers 可选。远端图片请求头

返回 CropResult

接口 分类 说明
path string 结果文件路径(应用缓存目录)
uri string 结果 URI(file:// 前缀)
name string 文件名,扩展名随输出格式变化
width / height number 结果宽 / 高(像素)
size number 结果文件字节数
type 'image/jpeg'|'image/png'|'image/webp' 结果 MIME 类型
base64 string(可选) 仅当 includeBase64: true 时附带

快速验证(运行 Example)

前置条件

依赖 版本要求
Node.js >= 18(示例工程 engines 要求)
DevEco Studio 6.1.1 及以上(示例 targetSdkVersion 为 6.1.1(24))
HarmonyOS SDK API 18+(example/harmony 的 compatibleSdkVersion 为 5.1.0(18))
路径 Windows 下仓库根目录的完整路径不超过 40 个字符,否则原生构建的中间文件路径会超过系统上限;已克隆到长路径时,把整个仓库目录移到短路径即可(仓内引用都是相对路径)

运行步骤

1. 克隆仓库

git clone -b br_rnoh0.72 https://gitcode.com/hxa-rn/react-native-image-editor.git
cd react-native-image-editor

Windows 下可在克隆时指定短目录名,例如 git clone -b br_rnoh0.72 https://gitcode.com/hxa-rn/react-native-image-editor.git C:\r。

2. 进入 example 目录,安装依赖

cd example
npm install --legacy-peer-deps

Example 从 npm 公仓安装 @hxa-rn/react-native-image-editor@4.3.2,不需要在仓根执行 npm pack。

3. 生成 JS Bundle

npm run dev

产物:harmony/entry/src/main/resources/rawfile/bundle.harmony.js

4. 用 DevEco Studio 打开鸿蒙工程

  • 打开 DevEco Studio,选择 example/harmony 目录
  • 首次构建请在 File → Project Structure → Signing Configs 勾选 Automatically generate signature,Apply 后 Sync
  • 等待 Sync 完成

5. 编译并运行 HAP

在 DevEco Studio 中点击运行按钮,将 HAP 安装到设备。

example_auto(自动化用例工程)步骤相同,把 example 换成 example_auto 即可,两者的依赖与构建输出相互独立。

注意:Example 已预置 Package 注册、Metro alias 与 HAR 依赖,无需手动 Link。编 HAP 前必须先执行 npm run dev;改过 JS 后重新执行再运行。Sync 时 harmony/hvigorfile.ts 会用 DevEco 自带的 Node 再核对一遍依赖与 Bundle(已完成的部分自动跳过),Build Output 里能看到「[1/5]…[5/5]」准备日志;Sync 报错时找「[失败]」开头的那行,按提示处理后再 Sync。

从当前源码打包适配包

维护者打包前,将 DEVECO_SDK_HOME 设置为 DevEco Studio 的 sdk 目录,然后在仓库根目录执行:

npm install --legacy-peer-deps
npm pack

npm pack 会先编译当前鸿蒙源码生成 harmony/image_editor.har,再执行类型检查和 JS 构建;任何一步失败都会中止打包。HAR 是生成物,不再提交到源码仓库,避免源码已修复而 npm 包仍携带旧实现。

上述操作只生成本地验证用 npm 包,不会上传 npm。包的版本号取自 package.json;registry 上已有同号版本时不能覆盖,本地包只用于验证当前源码。

验证步骤:

  1. 将 example/ 或 example_auto/ 复制到独立验证目录,先按常规步骤安装该示例的依赖。
  2. 在复制出的示例根目录安装本次生成的 tgz(将下面路径替换为实际路径):
npm install --no-save --package-lock=false --legacy-peer-deps /path/to/hxa-rn-react-native-image-editor-<版本>.tgz
npm run dev
  1. 确认 node_modules/@hxa-rn/react-native-image-editor/harmony/image_editor.har 来自本次本地包,再编译运行该示例。验证记录需注明使用的 tgz 和源码 commit。

仓库里的两个示例依赖固定为 npm 上已发布的版本;新版本发布并验证后再更新。

约束与限制

兼容性:

项 值
示例工程最低兼容 API compatibleSdkVersion 5.1.0(18)
示例工程目标 API targetSdkVersion 6.1.1(24)
HAR 模块 deviceTypes default / tablet / 2in1
上游 SDK 版本 @react-native-community/image-editor@4.3.1
RN / RNOH RN 0.72.5 + @react-native-oh/react-native-harmony@0.72.139

权限:

  • ohos.permission.INTERNET(联网)—— HAR 已在自身 module.json5 声明,用于「远程图片裁剪」下载源图;仅本地路径 / data URI 裁剪场景不会触发联网。
  • 读取宿主沙箱外的本地图片路径时,路径可读性由调用方自行保证,与本库权限声明无关。

使用约束:

项 约束
Android 专属 URI 方案 content:// 与 android.resource:// 在 HarmonyOS 无对应物,显式拒绝并给出可读原因,属平台真限制(上游同源)。
quality 取值区间 合法区间为 0~1 的小数;传 80 会越界被拒而不是等价于 0.8(与上游同源)。
未做解码期降采样 未使用 DecodingOptions.desiredRegion / sampleSize,大图裁剪的内存占用偏高,中低端机型需关注。
结果文件生命周期 结果落在应用缓存目录,模块生命周期结束时按 ReactNative_cropped_image_ 前缀清理;需长期保留请自行复制走(与上游语义一致)。
RNOH 与宿主 随宿主 RN 鸿蒙工程以 ArkTS 源码编译,需宿主 harmony/build-profile.json5 开启 buildOption.strictMode.useNormalizedOHMUrl;不引入 NAPI 侧自定义逻辑,不依赖特定芯片或传感器。

开源 License

本项目基于 MIT 开源。

问题反馈渠道

使用过程中遇到问题,请在 GitCode Issues 提交反馈,会及时跟进。