React Native for OpenHarmony 实战:三方库 react-native-image-pan-zoom 的鸿蒙化适配指南
本文记录把 react-native-image-pan-zoom 适配到 HarmonyOS 的完整过程。
这个库是纯 JS 的图片平移缩放组件(单指拖动、双指捏合、双击缩放),零运行时依赖,接入很轻。但它把两个不同性质的问题一起带了进来,而且都不在"能不能跑"这个层面上:
- 它的交付包里有一个会删掉自己入口文件的
prepare脚本 ——npm pack和npm install都会因此失败,而且失败之后包是坏的; - 它的效果全部由
transform体现,而布局 dump 不反映transform—— 也就是说,"用什么当判据"这件事本身要先解决,否则会把"生效了"误判成"没生效"。
第二条尤其值得记:我是先用一个正对照证明"变换确实生效、是判据不灵",才换成像素判据,最后拿到 57 / 117 / 35 和 300px 位移这些定量结果的。

一、先说结论
| 项 | 结果 |
|---|---|
| 上游最新版 | 2.1.12;gitHead = 30a7b4d4c832faebfcdb3c1131869801df56c6e3;许可证是 ISC(不是常见的 MIT) |
| 库类型 | 纯 JS 手势库(class 组件),把 transform 加在包裹 Image 的 Animated.View 上 |
| 是否需要原生适配 | 不需要(无 harmony/、无 NativeModules、无 requireNativeComponent) |
| 运行时依赖 | 零(dependencies 不存在) |
| 与 npm 上游的差异 | 23 / 24 个文件去掉 CR 后逐字符一致(含全部 built/** 编译产物);唯一差异是 package.json |
| 编译 | assembleHap 4 分 11 秒;HAP 82,050,332 字节(增量 +65,626) |
| 打包资源 | Copied 14 assets(原 13 + 1 张自建测试图,精确对上) |
| 设备侧断言 | ✅ 45 / 45 全部通过 |
| ⚠️ 安装硬约束 | 必须 npm install --ignore-scripts;不加则 exit 2,且 built/index.js 被删除(第 3 节) |
| ★ 判据纠正 | 布局 dump 不反映 transform ⇒ 这类库必须用像素判据(第 5 节) |
| ★ 一个真实的 API 陷阱 | centerOn({x, y, scale}) 的 x/y 会被 scale 放大:y=50 在 scale=2 下实际位移 100vp = 300 像素(实测精确吻合,第 7 节) |
一句话结论:库本身能用、渲染与变换都正确;但装它要加 --ignore-scripts,验它要换判据。
二、判定过程:三步走完,确认是纯 JS 库
| 步 | 做法 | 这个库的结果 |
|---|---|---|
| ① | package.json 里有没有 harmony.autolinking | 没有 |
| ② | 仓库里有没有 harmony/ | 没有 |
| ③ | 代码里有没有 NativeModules / requireNativeComponent / TurboModuleRegistry | 全无 |
dependencies 字段直接不存在 ⇒ 零运行时依赖,不存在"传递依赖装不上"的问题。
但这个库的入口是编译产物,这一点与一般的纯 JS 库不同:
"main": "built/index.js",
"types": "built/index.d.ts",
也就是说 src/ 是 TypeScript 源码,真正被 require 的是仓库里已提交的 built/。
正因为如此,第 3 节那个 prepare 脚本才致命。
三、⚠️ 安装硬约束:prepare 会删掉交付包自己的入口
3.1 脚本长什么样
"scripts": {
"prepare": "rm -rf built && tsc",
"start": "tsc -w --outDir demo/built",
...
}
prepare 会在 npm pack 以及 从 tgz / git+ 安装 时自动执行。而 built/ 是已提交、随包发布的(12 个文件,.gitignore 并没有忽略它)。
于是它做了两件都很糟的事:
第一,rm -rf built 把交付包自带的入口文件删了。
第二,它重新编译时用的不是包里声明的 TypeScript。
包里 devDependencies 写的是 typescript@^3.9.2,但消费者安装时不会装 devDependencies,所以 tsc 解析到的是环境 PATH 上的全局版本。我这次实测到的是 TypeScript 6.0.3,直接报两个错:
tsconfig.json(10,15): error TS5107: Option 'target=ES5' is deprecated and will stop functioning in TypeScript 7.0.
tsconfig.json(11,5): error TS5011: The common source directory of 'tsconfig.json' is './src'.
The 'rootDir' setting must be explicitly set to this or another path to adjust your output's file layout.
而 tsc 默认即使报错也会 emit,加上缺 rootDir 把公共根判成了包根 ⇒ 产物多套了一层 src/:
| 布局 | |
|---|---|
| 交付的原版 | built/index.js、built/image-zoom/*.js |
prepare 跑完后 | built/src/index.js、built/src/image-zoom/*.js |
⇒ main 指向的 built/index.js 不存在了。 失败模式从"装不上"升级成"包静默损坏"。
3.2 实测(三组对照)
⚠️ 实验是在库的副本上做的 —— 因为这个脚本会真的删目录。做完复查真库:
built/12 个文件、git 干净。
| 实验 | 命令 | exit | built/index.js | 出现 built/src | 结论 |
|---|---|---|---|---|---|
| 1 | npm pack | 2 | —(tgz 未产出) | — | 连包都打不出来 |
| 2 | npm install(file:,不加 --ignore-scripts) | 2 | 被删除 | 是 | 装不上,而且把入口删了 |
| 3 | npm install --ignore-scripts | 0 | 保留(12 文件) | 否 | ✅ 唯一可行 |
实验 2 的原始报错:
npm error code 2
npm error path <库目录>
npm error command C:\windows\system32\cmd.exe /d /s /c rm -rf built && tsc
npm error tsconfig.json(10,15): error TS5107: ...
npm error tsconfig.json(11,5): error TS5011: ...
3.3 这是谁的锅:上游的脚本,但交付包本该处理
- npm 元数据里的
scripts.prepare与交付包完全一致 ⇒ 不是适配者引入的; - 但交付包改了
package.json的 4 个字段(description / repository / homepage / test),却把这行会破坏安装的prepare原样留下了; - ⇒ 作为鸿蒙交付包,至少应该把"必须
--ignore-scripts"写进 README,或者干脆修掉这个脚本(不删目录、或补上rootDir)。
3.4 正确做法
# ✅ 必须这样装
npm install --ignore-scripts
# ❌ 这两种都会触发 prepare 并失败
npm install # exit 2
npm pack # exit 2
判据:凡是随包发布编译产物的库,只要看到
prepare/prepack动了那个产物目录,就先做一次安装实验;而且实验必须在副本上做。
四、交付包质量
4.1 与 npm 上游逐文件比对(先去 CR)
| 类别 | 结果 |
|---|---|
| 一致 | 23 个文件(src/** 源码、.eslintrc.js、.prettierrc、tslint.json、tsconfig.json、LICENSE、README.md,以及 built/** 全部 12 个编译产物) |
| 不同 | 1 个:package.json |
| 交付包缺少 | 0 |
| 交付包多出 | 6 个:.gitignore、两个 README.OpenHarmony*、代码检查报告、spec.json、契约测试 |
package.json 只改了 4 处:description 追加鸿蒙字样、repository.url 指向 AtomGit、homepage 指向 AtomGit、新增 scripts.test。
实现与产物双重零改动。
4.2 做得好的
- 连编译产物都与 npm 逐字符一致(这点比只比源码更严格);
spec.json带license: ISC(与 npm 一致),并写了native: false/rnoh 0.84.3/reactNative 0.84.1;- 零运行时依赖;
- 一个看着像错、其实没错的地方:包名是
react-native-image-pan-zoom,而spec.json里的upstream.url写的是github.com/ascoders/react-native-image-zoom—— 查 npm 的repository字段证实上游仓库确实叫这个名,所以不是缺陷。(名字不一致要先查证再定性。)
4.3 问题
| 问题 | 说明 |
|---|---|
prepare 会破坏安装(见第三节) | 交付包改了 package.json 4 个字段却没处理它;也没在 README 里写"必须 --ignore-scripts" |
spec.json 缺 upstreamCommit | 而 npm 明确有 gitHead(30a7b4d4…)⇒ 属应修项 |
validation 是裸字符串 "pass" | 没有可复核的测试项清单 |
| 契约测试是包级 smoke test | 只检查包名 / 版本 / main 能 require.resolve / 中文 README 存在。一次组件都没渲染过 —— 本轮 45 条断言里任何一条写错,它都是绿的 |
| 两个交付 README 都没写 | ① 必须 --ignore-scripts 安装;② 组件有"ref 命令式"与"centerOn prop 受控"两套独立驱动方式(行为不同,见 8.4) |
五、验证设计:先解决"用什么当判据"
这个库的效果只有两种体现:scale 和 translate。所以我一开始的想法很自然:
它把
transform加在包裹Image的Animated.View上,那读布局矩形不就能量到缩放和位移了吗?
这个想法是错的,而且如果不做对照,我会得到一个彻底的假结论。下面把过程完整记下来,因为它比结论更有用。
5.1 第一版判据:布局矩形
组件的结构是:
<View style={{width: cropWidth, height: cropHeight}} {...panHandlers}>
<Animated.View style={{transform: [{scale}, {translateX}, {translateY}]}}>
<Image style={{width: imageWidth, height: imageHeight}} />
</Animated.View>
</View>
我通过 ref 调命令式方法把 scale 驱动到已知值(这样就不依赖双指手势),然后读布局:
| 时刻 | 设备日志里的证据 | 布局 dump 里的矩形 |
|---|---|---|
| 基线 | — | Image x=60 y=684 w=900 h=600 |
调 centerOn({x:0, y:0, scale:2}) 后 | centerOn(...),随后 onMove centerOn x=0 y=0 s=2.000 | Image x=60 y=684 w=900 h=600(完全没变) |
再调 centerOn({x:100, y:50, scale:2}) 后 | onMove centerOn x=100 y=50 s=2.000 | 仍然完全没变 |
⇒ 三条证据互相矛盾:回调说 scale 已经是 2,布局说尺寸一点没变。
5.2 关键一步:做正对照,而不是直接写"没生效"
"测不到"和"没效果"必须用对照组分开。 我做的正对照是看屏幕:截图里
- 变换区的图满屏红、看不到蓝边(放大 2× 后,图的蓝边被裁到裁剪窗外);
- 而同页另一个未变换的区域的图,蓝边清晰可见(原尺寸)。
⇒ 变换确实生效了,是布局 dump 不反映它。
devecocli ui layout 给的是 Yoga 的布局帧,而 transform 是渲染层属性。所以对这个库来说,布局 dump 是无效判据。
5.3 换判据:给测试图做一把"像素标尺"
既然要读像素,就得让像素可解释。我自建了一张 300×200 的 PNG:
- 红底;
- 12px 蓝色边框 —— 作为"是否被放大到裁出窗外"的二值信号;
- 正中间一条 20px 白条 —— 作为精确的 scale 标尺;
- 外层容器的
backgroundColor: '#000000'—— 当图比裁剪窗小时会露出黑底。
裁剪窗实测矩形 x=60 y=684 w=900 h=600(= 300×200 vp,density 3),窗口中心 Y = 984。
⚠️ 一个自己踩的小坑:JPEG 有压缩,白条边界有几像素过渡(实测 57 / 117 / 35,而不是理论的 60 / 120 / 34.2)。用阈值扫描要接受 ±5% 误差,别把 57 当 60 就判失败。


六、像素判据的定量结果
沿裁剪窗中心竖线扫描白条的 Y 跨度:
| 命令式调用 | 白条可见跨度 | 高度 | 中线 Y | 左边缘像素 | 解读 |
|---|---|---|---|---|---|
reset()(s=1, x=0, y=0) | 955–1012 | 57 | 984 | 白 | 基线:白条中线 = 窗口中心 ✓ |
centerOn({scale:2}) | 925–1042 | 117 ≈ 2×57 | 984 | 白 | scale 生效,且绕中心缩放(中线不动)✓ |
centerOn({scale:0.6}) | 966–1001 | 35 ≈ 0.6×57 | 984 | 黑 (0,1,3) | scale 生效 + 图小于窗口露出黑底 ✓(双重确认) |
centerOn({x:100, y:50, scale:2}) | 1226–1283(被窗口下沿 1284 截断) | — | 1284 | 黑 | 内容确实位移 ✓ |
几条判据互相独立、互相印证:
- 白条高度是连续量,直接给出 scale(2× 与 0.6× 都落在 ±5% 内);
- 中线不动 ⇒ 缩放绕中心(不是绕左上角);
- 左边缘由白变黑 ⇒ 图变小到比窗口还小,与
scale=0.6一致; - 位移状态下左边缘也是黑的 + 白条整体下移 ⇒ 平移确实发生。
七、一个真实的 API 陷阱:centerOn 的 x/y 会被 scale 放大
最后一行的白条"高度只有 57",一开始让我以为"scale 没生效"。按模型算就完全对上了。
源码里 transform 数组的顺序是:
transform: [ {scale}, {translateX}, {translateY} ]
矩阵从前往后乘 ⇒ 平移量会被前面的 scale 放大。y: 50 在 scale: 2 下 ⇒ 实际位移 100 vp = 300 物理像素:
- 白条中心 =
984 + 300 = 1284 - 白条完整跨度 =
1284 ± 58.5= 1225.5 … 1342.5 - 裁剪窗下沿是 1284 ⇒ 可见部分 = 1226 … 1284
- 实测 = 1226 … 1283 ✓✓ 精确吻合
⇒ centerOn({x, y, scale}) 里的 x / y 不是屏幕位移,而是会被 scale 乘一遍的"变换空间"位移。
这是一个只有做定量测量才会发现的结论:如果只检查"有没有动",就会漏掉这个 2 倍关系;如果只看"高度不对",就会误判成"缩放失败"。
八、静态契约与命令式语义(33 + 9 条断言)
8.1 默认导出是 class 组件
typeof ImageZoom === 'function' // ✓ class 组件
不是 memo / forwardRef 包装出来的对象(那个坑本系列踩过,属于另一类库)。
8.2 defaultProps 是一个 ImageZoomProps 实例(30 个键)
关键默认值(实测值):
| 字段 | 默认值 |
|---|---|
cropWidth / cropHeight / imageWidth / imageHeight | 100 |
minScale / maxScale | 0.6 / 10 |
panToMove / pinchToZoom / enableDoubleClickZoom / enableCenterFocus | true |
enableSwipeDown | false(swipeDownThreshold = 230) |
useNativeDriver / useHardwareTextureAndroid | false / true |
clickDistance / doubleClickInterval / longPressTime / maxOverflow | 10 / 175 / 800 / 100 |
运行时命名导出:default、ImageZoomProps、ImageZoomState(export * 把两个 class 也带到了运行时)。
8.3 ⚠️ "方法存在"要分清原型方法与 class 属性箭头函数
这是一个很容易写错断言的地方:
| 写法 | 挂在哪里 |
|---|---|
public centerOn(params) {} | prototype 上 |
public reset() {} | prototype 上 |
public resetScale = (): void => {} | 实例自有属性(原型上是 undefined) |
我第一版把 resetScale 当原型方法断言,得到一个假失败。改成同时断言两件事(“不在原型上” + “在实例上”)之后,这条反而变成了一条有价值的记录。
⇒ 断言"某个方法存在"时,先看源码是哪种写法。
8.4 两套驱动方式互不相通
componentDidMount() { if (this.props.centerOn) this.centerOn(this.props.centerOn); }
componentDidUpdate(p) { if (/* centerOn prop 发生变化 */) this.centerOn(this.props.centerOn); }
⇒ 只有传 centerOn prop 才会有"自动居中/重放"行为;通过 ref 调 centerOn() 不会触发这两者。也就是说"ref 命令式"与"prop 受控"是两套独立的驱动方式,别混着理解。
8.5 reset() 不发 onMove
public reset() {
this.scale = 1; this.animatedScale.setValue(1);
this.positionX = 0; this.animatedPositionX.setValue(0);
this.positionY = 0; this.animatedPositionY.setValue(0);
}
- 立即生效(
setValue,没有动画); - 不调用
imageDidMove⇒ 复位后onMove不会回调,页面上的"最近 onMove"会停留在复位前的值。
⚠️ 这一点很容易误判成"复位没生效" —— 实际上画面已经复位了,只是回调没发。
8.6 onMove 是唯一的观测通道
载荷实测:
{"type":"centerOn","positionX":0,"positionY":0,"scale":2,"zoomCurrentDistance":0}
它同时是"命令式调用是否完成"的信号(centerOn 动画结束时回调 type: 'centerOn')。
九、已知限制与本次验证边界
9.1 能力与坑的清单
| 项 | 说明 |
|---|---|
| 安装 | 必须 npm install --ignore-scripts(第 3 节) |
| 判据 | 效果全是 transform ⇒ 布局 dump 无效,必须用像素(第 5 节) |
centerOn 的 x/y | 会被 scale 放大(第 7 节) |
reset() | 立即生效但不发 onMove(8.5) |
| 驱动方式 | ref 命令式与 centerOn prop 两套独立(8.4) |
| 方法位置 | centerOn/reset 在原型上,resetScale 在实例上(8.3) |
9.2 本次验证的边界
- 只在一台模拟器上验证(
Pura X View,density 3,1320×2232),没有真机。 - 双指捏合缩放(
pinchToZoom)没有做:uitest uiInput不支持多指手势,本轮是用centerOn()把 scale 驱动到已知值来验证缩放通道的 ⇒ "捏合手势本身"能否被 RNOH 的 PanResponder 正确解析,未验证。 - 单指拖拽没有做定量测量:页面上留了拖拽区,但没做"拖 → 量位移"的定量对比(缩放/平移通道已由命令式方式定量验证)。
- 双击缩放、长按、下滑退出(
enableSwipeDown/onSwipeDown)、onClick/onDoubleClick、边界回弹(maxOverflow)、horizontalOuterRangeOffset/onDragLeft/responderRelease均未测。 minScale/maxScale的实际钳制未测:只断言了默认值是 0.6 / 10,没测"缩放到超界是否被钳住"。enableCenterFocus(居中吸附)未测。useNativeDriver: true未测(默认 false 时动画由 JS 驱动)。useHardwareTextureAndroid在鸿蒙上的实际效果未测。centerOnprop 受控路径未测(本轮全部走 ref 命令式)。- 没有跨平台对照(未在 iOS/Android 上跑同一套用例)。
未改动库代码。
十、常见问题
Q1:为什么 npm install 装不上,还报 rm -rf built 失败?
因为交付包的 package.json 里有 "prepare": "rm -rf built && tsc"。prepare 会在 npm pack 和从 tgz / git+ 安装时自动执行:
rm -rf built把随包发布的built/(也就是main指向的入口)删掉;tsc解析到的是环境里的全局 TypeScript(消费者装不到包内 pin 的 devDependency),加上tsconfig.json缺rootDir,产物被生成到built/src/下;- ⇒
built/index.js不存在了,npm 以 exit 2 失败。
唯一可行做法:
npm install --ignore-scripts
Q2:npm pack 也失败,正常吗?
正常,同一个原因(pack 也会跑 prepare)。这条路当前不可用。
Q3:为什么用布局 dump 量不出缩放?图片明明变大了。
因为 devecocli ui layout 给的是 Yoga 布局帧,而缩放是加在 Animated.View 上的 transform(渲染层属性)。实测:onMove 报 scale=2、屏幕上蓝边被放大裁掉,但矩形三次完全不变。
⇒ 这个库必须用像素判据(见 Q4)。
Q4:没有双指手势工具,怎么验证缩放?
用命令式方法把 scale 驱动到已知值,再用像素验证:
zoomRef.current.centerOn({x: 0, y: 0, scale: 2, duration: 0});
我让测试图自带蓝色边框(放大后会被裁掉)和正中间的白色横条(高度是精确的 scale 标尺),实测:
| 调用 | 白条高度 | 结论 |
|---|---|---|
reset() | 57 | 基线 |
scale=2 | 117 ≈ 2× | ✓ |
scale=0.6 | 35 ≈ 0.6× + 边缘露黑底 | ✓ |
Q5:centerOn({x: 100, y: 50, scale: 2}) 的位移为什么是 300 像素而不是 150?
因为 transform 数组顺序是 [{scale}, {translateX}, {translateY}],矩阵从前往后乘 ⇒ 平移量会被 scale 放大。
y: 50 × scale: 2 = 100 vp = 300 物理像素(density 3)。实测白条中心从 984 移到 1284,正好 +300 ✓
Q6:我调了 reset(),但"最近 onMove"还显示旧值,是没生效吗?
生效了。 reset() 用 setValue 立即复位,但不调用 imageDidMove ⇒ 不会触发 onMove 回调,所以显示停留在旧值。以画面为准。
Q7:通过 ref 调 centerOn() 和传 centerOn prop 有什么区别?
两套独立方式:
- 传
centerOnprop ⇒componentDidMount会居中一次,且componentDidUpdate在该 prop 变化时重放; - ref 调
centerOn()⇒ 只是立刻执行一次动画,不经过那两个生命周期,也不会因为 prop 变化而重放。
Q8:minScale / maxScale 默认是多少?
0.6 / 10(defaultProps 实测值)。注意最小值小于 1,也就是说默认允许把图缩到比裁剪窗还小(我实测 scale=0.6 时图比窗口小、露出了容器的黑底)。是否钳制未在本轮实测。
Q9:为什么 ImageZoom.prototype.resetScale 是 undefined?
因为源码里它是 class 属性箭头函数(public resetScale = (): void => {}),属于实例自有属性;而 centerOn / reset 是普通方法,挂在 prototype 上。断言"方法存在"时要注意这个区别。
Q10:怎么接入宿主?
npm install "git+https://atomgit.com/oh-react-native/react-native-image-pan-zoom.git#2.1.12-ohos-1.0.0" --ignore-scripts
// metro.config.js —— file: 装法是软链,需要把真实目录加进 watchFolders
watchFolders: [ path.resolve(__dirname, '../react-native-image-pan-zoom') ],
<ImageZoom
ref={zoomRef}
cropWidth={300} cropHeight={200}
imageWidth={300} imageHeight={200}
panToMove pinchToZoom
onMove={(p) => console.log(p.type, p.scale, p.positionX, p.positionY)}
>
<Image source={require('./photo.png')} style={{width: 300, height: 200}} />
</ImageZoom>
不需要 HAR、权限、ohpm、link-harmony。
小结
react-native-image-pan-zoom 是一个纯 JS、零运行时依赖的图片平移缩放组件:23/24 个文件与上游逐字符一致(含全部编译产物),设备侧 45 / 45 断言全过,缩放与位移都经过定量像素验证。
真正值得记下来的是这几条:
-
"判据"本身要先验证。 我原以为"布局 dump 反映 transform",这一轮用正对照(
onMove报scale=2+ 屏幕上蓝边被放大裁掉,而矩形纹丝不动)证明它不反映。⇒ 换成像素判据之后,才拿到 57 / 117 / 35 与 300px 位移这些定量结果。 如果不做这个对照,就会写成"缩放没生效"——一个彻底的假结论。("测不到"和"没效果"必须用对照组分开。) -
prepare脚本可以毁掉交付包自己。 这次不是"打不出包",而是rm -rf built把main指向的入口删掉,再用环境里的 tsc(消费者装不到包内 pin 的版本)重新生成到另一个目录。⇒ 凡是随包发布编译产物的库,看到会动那个目录的prepare/prepack,就必须先做安装实验,而且一定要在副本上做(它会真删)。 -
transform数组的顺序会改变语义。[{scale}, {translateX}, {translateY}]让平移被 scale 放大 ——y: 50实际走了 300 像素。这只有做定量测量才会发现:只检查"有没有动"会漏掉 2 倍关系,只看"高度不对"又会误判成缩放失败。 -
断言"方法存在"要分清原型与实例。
public foo() {}在prototype上;public foo = () => {}在实例上。写错位置就是一个假失败。 -
回调类断言必须排在交互之后。 “一键跑全部"很容易把这类断言的顺序搞反 ⇒ 要么标成 informational,要么在协议里明确规定"先交互、后断言”。
-
"名字不一致"要先查证再定性。 包叫
react-native-image-pan-zoom,上游仓库却叫react-native-image-zoom—— 看着像填错,但 npm 的repository字段证实上游确实如此,不是缺陷。

本篇用到的库
| 项 | 内容 |
|---|---|
| 三方库 | react-native-image-pan-zoom(上游 2.1.12 的鸿蒙适配版) |
| 适配仓库 | https://atomgit.com/oh-react-native/react-native-image-pan-zoom |
| 适配 TAG | 2.1.12-ohos-1.0.0 |
| 需要 HAR / 权限 / ohpm | 都不需要(纯 JS,零运行时依赖) |
| 库类型 | 纯 JS 手势库:单指拖动、双指捏合、双击缩放的图片查看组件 |
| 运行时依赖 | 零(dependencies 字段不存在) |
| 许可证 | ISC(上游与交付包一致) |
| 上游仓库 | https://github.com/ascoders/react-native-image-zoom(⚠️ 仓库名与包名不同,已核实) |
| 上游基线 | npm gitHead = 30a7b4d4c832faebfcdb3c1131869801df56c6e3 |
| 宿主工程 | RNOH084Demo(测试页 rnAppKey = ImagePanZoomTestApp) |
| 安装方式(重要) | 必须加 --ignore-scripts |
# ⚠️ 必须带 --ignore-scripts:否则 prepare 会 `rm -rf built` 并让 main 目标消失
npm install "git+https://atomgit.com/oh-react-native/react-native-image-pan-zoom.git#2.1.12-ohos-1.0.0" --ignore-scripts
// metro.config.js —— file: / 软链装法需要把真实目录加进 watchFolders
watchFolders: [ path.resolve(__dirname, '../react-native-image-pan-zoom') ],
// 用法
import ImageZoom from 'react-native-image-pan-zoom';
const zoomRef = useRef(null);
<ImageZoom
ref={zoomRef}
cropWidth={300}
cropHeight={200}
imageWidth={300}
imageHeight={200}
minScale={0.6}
maxScale={10}
panToMove
pinchToZoom
onMove={(p) => console.log(p.type, p.scale, p.positionX, p.positionY)}
>
<Image source={require('./photo.png')} style={{width: 300, height: 200}} />
</ImageZoom>;
// 命令式驱动(不依赖双指手势,可精确到已知值)
zoomRef.current?.centerOn({x: 0, y: 0, scale: 2, duration: 300});
zoomRef.current?.reset(); // 注意:立即生效,但**不会**触发 onMove
# 换页启动测试页(force-stop 不能省,换页参数只在冷启动生效)
hdc shell aa force-stop com.rnoh084.demo
hdc shell aa start -b com.rnoh084.demo -a EntryAbility --ps rnAppKey ImagePanZoomTestApp
验证环境
| 项 | 版本 |
|---|---|
| React Native | 0.84.1 |
| React | 19.2.3 |
| RNOH(npm / ohpm) | @react-native-oh/react-native-harmony / @rnoh/react-native-openharmony 0.84.3 |
| Node.js / npm | v24.14.0 / 11.9.0 |
| DevEco Studio | 26.0.0.621 |
| HarmonyOS SDK | API 26(26.0.0.32) |
| 设备 | HarmonyOS 7.0.0(26.0.0) Beta2 模拟器 Pura X View(ohos-x64),1320×2232,density 3 |
| 宿主 HAP 产物 | entry-default-signed.hap(82,050,332 字节) |
| 本次增量构建 | assembleHap 4 分 11 秒 / 重编 4 分 15 秒,HAP +65,626 字节;打包资源 13 → 14 |
| 验证规模 | 设备侧 45 / 45 断言全部通过;像素判据 4 个状态;prepare 缺陷 3 组安装对照;上游 24 个文件逐字符比对 |
欢迎加入 CPF-RN 鸿蒙社区:https://atomgit.com/CPF-RN
React Native for OpenHarmony 组织:https://atomgit.com/oh-react-native
RN 三方库鸿蒙适配清单:https://atomgit.com/oh-react-native/rn-ohos-adaptation-overview
更多推荐




所有评论(0)