本文记录把 react-native-animated-spinkit(12 种 CSS 风格加载动画)适配到 HarmonyOS 的完整过程。

这个库的特殊之处在于:它没有任何返回值可以断言。它是纯视觉效果——12 个会转、会跳、会缩放的加载指示器。所以本轮的核心问题是:"动画在跑"这件事,怎么验?

答案是把屏幕当被测对象:连拍两帧,逐像素比对。

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


一、先说结论

项结果
上游最新版1.5.2(npm gitHead = aa452f78…)
是否需要原生适配不需要——纯 JS,12 个组件全靠 Animated 驱动,无原生模块、无 dependencies
与 npm 上游的差异98 / 99 个文件去掉 CR 后逐字符一致,唯一差异是 package.json
自动链接信号linked 10 libraries, skipped 1 libraries,计数未增加
编译assembleHap 6 分 55 秒;HAP 81,516,680 → 81,614,985 字节(+98,305 ≈ 96 KB)
设备侧断言✅ 35 / 35 全部通过
原生动画驱动✅ useNativeDriver: true 真的生效(RNOH 提供 NativeAnimatedTurboModule,无回退告警)
帧率requestAnimationFrame 实测 63.7 fps
像素级证据动画中两帧 6.17% 像素显著变化(复测 6.16%);停下后 0.00%(一个像素都不变);隐藏后对照区整片纯白
交付包缺陷⚠️ 4 处,其中 npm install 与 npm pack 退出码都是 1
一个重要事实这个库没有 default 导出——只有 12 个具名导出

一句话结论:功能完全可用,12 个动画在鸿蒙上都能正常渲染并驱动(走的是真正的原生动画驱动,满帧率)。但交付包在工程配置与文档上有 4 处问题,其中 npm install 失败会直接影响 CI。


二、判定过程:这个库不需要原生适配

判断一个 RN 库要不要做原生适配,我固定走三步:

步做法这个库的结果
①npm view <包名> harmony --json 看有没有 harmony.autolinking没有
②仓库里有没有 harmony/(以及 android/、ios/)都没有
③代码里有没有 NativeModules / Platform.OS / requireNativeComponent有 Platform.OS,但只用来决定 useNativeDriver 的取值

第 ③ 步命中的那处 Platform.OS 值得看一下,因为它恰好是这个库在鸿蒙上最大的运行时风险点:

// src/utils.ts
const timing = Animated.timing(value, {
  duration,
  easing: createKeyframeEasingFunction(keyframes, easing),
  toValue,
  useNativeDriver: Platform.OS !== 'web',   // ← 鸿蒙上是 true
})

stagger() 里更直接,非 web 分支写死了 useNativeDriver: true。

为什么这是风险点:useNativeDriver: true 要求平台提供原生动画模块。如果平台没有,React Native 会打印回退告警并改用 JS 驱动——动画照跑,但驱动方式、性能和语义都变了。所以"它能不能跑"这个问题,必须换成"它的原生驱动到底有没有生效"。

这一点留到第五节和第七节解决。

公开 API(12 个组件,全部具名导出):

Plane  Bounce  Pulse  Swing  Chase  Wave
Flow   Circle  CircleFade  Grid  Fold  Wander

⚠️ 这个库没有 default 导出。 三处入口文件(src/index.ts、lib/commonjs/index.js、lib/module/index.js)都只有具名导出,我在设备上专门断言了 typeof default === 'undefined'——通过。

组件属性(SpinnerProps):

属性默认值说明
size48单位是 vp
color'#000'填充色
animatingtrue是否播放
hidesWhenStoppedtrueanimating=false 时是否整体隐藏(opacity 0)

三、这个交付包长什么样:与 npm 上游逐字节一致

核对方法:与 react-native-animated-spinkit@1.5.2 的 npm tarball 逐文件比对(先去 CR 再逐字符比较)。

结果:98 / 99 个文件完全一致,包含 src/ 下 15 个源文件与 lib/ 下全部构建产物。唯一不同的是 package.json:

字段变化
description追加 " for React Native for OpenHarmony"
scripts新增 test,其余(含 prepare: bob build)原样保留
repository.url改为 AtomGit 地址
homepage改为 AtomGit 地址
files删除了上游的白名单(上游为 ["src","lib","!**/__tests__",...])
peerDependencies收窄为 react: >=19 / react-native: >=0.84 <1(上游是 *)

实现文件零改动,所以后面发现的问题都属于工程配置与文档层面,与动画实现无关。


四、接入宿主与构建运行

纯 JS 库的接法只有三处:

// package.json
"react-native-animated-spinkit": "file:../react-native-animated-spinkit"
// metro.config.js —— file: 装进来是 junction,必须让 Metro 找得到源码
watchFolders: [path.resolve(__dirname, '../react-native-animated-spinkit')],
// index.js
AppRegistry.registerComponent('SpinnerTestApp', () => SpinnerTestApp);

不要动 harmony/entry/oh-package.json5(不需要 HAR),不要动 module.json5(不需要权限)。信号是自动链接计数不增加:

info updated 4 file(s), linked 10 libraries, skipped 1 libraries
项数值
首次 assembleHap6 分 55 秒
二次重编(改了两条断言)6 分 28 秒
HAP 变化81,516,680 → 81,614,985 字节(+98,305 ≈ 96 KB)
bundle 产物bundle.harmony.js 6,454,621 字节

⚠️ 这里有个陷阱:npm install 实际上是失败的(退出码 1),但因为编译不检查 npm 退出码,本地"一切正常"。详见第八节。


五、验证设计:纯视觉效果怎么验才可信

这类库没有返回值、没有系统状态可读。唯一能验的对象是屏幕上的真实产物。

5.1 手段:连拍两帧 + 逐像素比对

设备侧连拍两帧(间隔 260 ms),把图拉回 PC,用 System.Drawing 逐像素比较:

hdc shell "snapshot_display -f /data/local/tmp/tag-a.jpeg"
# 等 260 ms
hdc shell "snapshot_display -f /data/local/tmp/tag-b.jpeg"
hdc file recv /data/local/tmp/tag-a.jpeg E:\rnoh-work\frames\tag-a.jpeg
hdc file recv /data/local/tmp/tag-b.jpeg E:\rnoh-work\frames\tag-b.jpeg

判据用每通道最大差 > 24 的像素占比。选 24 是因为:两张完全相同的 JPEG 截图之间也会有几个灰阶的压缩噪声,而 24 能干净地把"噪声"和"真的变了"分开。

5.2 页面要提供一个"只有一个会动的东西"的区域

测试页里放了一个固定的 240×240 vp 白色对照区,里面只放一个 spinner;12 个组件的展示网格可以一键隐藏。这样整个页面只剩一个动画,裁剪区的像素变化就只可能来自它。

裁剪区不能写死坐标——必须从每一帧里自动定位那个白色方块。我用的办法是找"包含长连续白色游程"的行(白框约 720 物理像素宽,而页面上的白字游程远短于此),取这些行的外接矩形。

5.3 三个(其实是四个)场景构成闭环

只证明"画面在变"是不够的:变的原因可能是噪声、可能是别的东西在动。所以必须有对照组:

场景预期作用
animating=true两帧明显不同动画在跑
animating=false + hidesWhenStopped=false两帧完全相同,且区域仍有非白像素动画真的停了;且被测对象仍可见
animating=false + hidesWhenStopped=true两帧相同,且区域整片纯白隐藏是有效的
恢复 animating=true两帧再次明显不同可重复

第二个场景里"区域仍有非白像素"这一条不能省。 否则第三个场景的"整片纯白"有可能只是因为根本没渲染出来——那就不是"隐藏生效",而是"东西不在"。


六、实测发现一:动画真的在跑,停也真的停

设备:Pura X View 模拟器,1320×2232(density 3)。对照区实测为 720×720 物理像素(即 240 vp 见方),裁剪时向内缩 14 像素取 692×692。

场景状态显著变化(>24)任意差异(>0)非白像素区域平均色
wave_onanimating=true6.17%9.22%——
wave_frozenanimating=false, hides=false0.00%0.00%14.66%(224,235,252)
wave_hiddenanimating=false, hides=true0.00%0.00%0.00%(255,255,255)
wave_on2恢复 animating=true6.16%9.21%——

四条结论,逐条都有关键意义:

  1. 动画在跑:260 ms 间隔的 6.17% 像素变化,而且复测得到 6.16%——可重复;
  2. 停下是真的停:animating=false 后,两帧之间任意差异 0.00%——连一个像素都没变。这比"变化变小了"强得多,是"动画被真正停止"的直接证据;
  3. hidesWhenStopped 生效:整片纯白,平均色精确为 (255,255,255);
  4. 静止时 spinner 确实还画在屏幕上(14.66% 非白像素)⇒ 证明第 3 条的"全白"是真的被隐藏,而不是对照区空了。

6.1 换 spinner 复验

spinner结构显著变化(>24)
Wave5 条竖条 + scaleY6.17% / 6.16%
Circle12 个圆点环形分布 + translate3.64%
Grid9 宫格 + 缩放/淡入淡出23.26%

三种完全不同的结构都在动,说明不是某个组件的偶然行为。

6.2 一个测出来的上游设计细节

12 个组件的根视图尺寸一起量了一遍(onLayout):

Plane=48x48  Bounce=48x48  Pulse=48x48  Swing=48x48
Chase=48x48  Wave=48x48    Flow=48x12   Circle=48x48
CircleFade=48x48  Grid=48x48  Fold=48x48  Wander=48x48

11 个是 size × size 的正方形,只有 Flow 是 size × size*0.25。 这是上游的设计(Flow 是 3 个横排圆点,容器只需 1/4 高度),不是缺陷——但如果有人写"所有 spinner 都是正方形"的通用布局代码,会在 Flow 上踩到。

(我第一版断言就写死了 size × size,于是在 Flow 上失败了一次——这是我自己按想象写断言,不是库的问题。)


七、实测发现二:useNativeDriver: true 在鸿蒙上真的生效

这是第二节留下的风险点。判断方法不能靠猜,要靠权威证据。

7.1 权威证据一:原生动画 TurboModule 存在

#RNOH_CPP: TurboModuleFactory.cpp:54> Creating Turbo Module: NativeAnimatedTurboModule
#RNOH_ARK: TM created: NativeAnimatedTurboModule

RNOH 提供了 NativeAnimatedTurboModule。页面内用 TurboModuleRegistry.get('NativeAnimatedTurboModule') 也能拿到非空对象。

7.2 权威证据二:日志里没有回退告警

如果原生动画模块缺失,React Native 会打印 useNativeDriver is not supported because the native animated module is missing 之类的告警并回退。整段运行日志里没有这类告警。

⇒ useNativeDriver: true 真的生效,没有回退。

7.3 ⚠️ 一个我自己用错的判据(值得记下来)

我最初在测试页里是这么判断驱动方式的:

「原生驱动下,JS 侧的 Animated.Value.addListener 不会被调用;如果 listener 有更新,说明回退到了 JS 驱动。」

实测把这个判据推翻了:JS 驱动时 listener 更新 1 次,原生驱动时 listener 更新 18 次。按我的判据,会得出"回退到 JS 驱动"这个完全相反的错误结论。

真实原因是:React Native 的原生驱动本身会通过 onAnimatedValueUpdate 事件把动画值回传给 JS 监听,所以两种驱动下 listener 都可能被调用。

教训:结论要建立在权威证据(模块是否存在、日志有没有告警)上,不能建立在自己推演出来的启发式上。启发式看起来很像证据,但它只是猜测。

7.4 帧率

页面内用 requestAnimationFrame 数帧:

requestAnimationFrame 计帧 = 64 帧 / 1005 ms ≈ 63.7 fps

满帧率。配合"原生驱动生效"的结论,说明动画在鸿蒙上是由原生侧逐帧驱动的,不是靠 JS 定时器凑出来的。

在这里插入图片描述


八、实测发现三:交付包的 4 处问题

8.1 npm install 与 npm pack 的退出码都是 1(最严重)

npm error code 1
npm error command C:\windows\system32\cmd.exe /d /s /c bob build
npm error 'bob' is not recognized as an internal or external command

根因:交付包的 scripts 与上游只差一个 test——也就是原样保留了 "prepare": "bob build"。而 react-native-builder-bob 是 devDependencies,在消费者侧和纯净检出里都不可得。

prepare 这个生命周期钩子在本地路径依赖(file:)与 git 依赖安装时都会执行,所以:

操作实测结果
宿主 npm install(file: 依赖)exit 1
npm packexit 1,无产物

为什么本地没炸:npm 在跑 prepare 之前就已经建好了 junction,所以 node_modules/react-native-animated-spinkit 存在、Metro 能解析、HAP 照样编译成功。“能编译过"掩盖了"安装已经失败”。

实际风险有两条:

  1. 任何以 npm install 退出码作为门禁的 CI/CD 会在这一步中断——这是最现实的影响;
  2. npm pack 打不出 tgz ⇒ README 里推荐的 npm install /path/to/react-native-animated-spinkit-1.5.2.tgz 这条路径无法从该仓库自行产出。

修复方向:交付包把 prepare 删掉或换成无害脚本即可(bob 只在需要重新构建 lib/ 时才有意义,而见下一条,RN 侧根本不加载 lib/)。

说明:README 里还有一条 git 安装写法 npm install https://atomgit.com/...#1.5.2-ohos-1.0.0。我实测这条路径时先败在归档下载(npm error code TAR_BAD_ARCHIVE),属环境问题,不作为证据。按 npm 的规则,git 依赖同样会执行 prepare。

8.2 README 说 Metro 用 lib/commonjs 入口,实测是 src

README 原文:「Metro 使用 lib/commonjs/index.js 入口。」

在构建产物里数引用:

react-native-animated-spinkit/src            出现 16 次
react-native-animated-spinkit/lib/commonjs   出现 0 次

根因:package.json 里 main 指向 lib/commonjs/index.js,但还有一个 react-native 字段指向 src/index.ts。React Native 的 Metro 默认 resolverMainFields = ['react-native', 'browser', 'main']——react-native 字段优先,所以加载的是 TypeScript 源码,由 Metro 现场转译。

影响:lib/(commonjs + module + typescript 三份产物,约 150 KB)对 RN 消费者而言是死代码;而 README 的入口说明与实际正好相反,会误导做入口排查的人。

8.3 README 的用法示例用了 default import,而库没有 default 导出

README 给的示例是:

import Library from 'react-native-animated-spinkit';
const Library = require('react-native-animated-spinkit');

实测:三个入口文件都没有 default 导出;设备侧断言 typeof default === 'undefined' 通过。

⇒ 照 README 抄会拿到 undefined。正确写法:

import {Plane, Wave, Circle} from 'react-native-animated-spinkit';

8.4 契约测试不测库

__tests__/harmony-contract.test.cjs 全文只断言四件事:

assert.equal(pkg.name, 'react-native-animated-spinkit');
assert.equal(pkg.version, '1.5.2');
assert.ok(require.resolve(path.resolve(__dirname, '..', pkg.main)));
assert.ok(fs.existsSync(path.resolve(__dirname, '..', 'README.OpenHarmony_CN.md')));

没有渲染任何组件、没有触发任何动画。 它验证的是"包文件存在",不是"库能用"。

这个测试没有虚假命名——README 就称它为"包级 smoke test",所以这是覆盖面不足,不是欺骗。但注意:require.resolve 的目标是 pkg.main,而实际被 Metro 加载的是 src/index.ts——也就是说这个测试连"真正的入口文件是否存在"都没覆盖到。

8.5 spec.json 的可追溯性偏弱

"upstream": {"type": "git", "url": "git+https://github.com/tienphaw/react-native-animated-spinkit.git"},
"native": false,
"validation": "pass"
  • upstream 是对象(同类交付包一般用字符串),形态不一致;
  • 没有 upstreamCommit——上游 gitHead(aa452f78…)我从 npm 查到了,但规格文件里没记;
  • validation 是裸字符串(同类交付包一般是含场景数、设备、HAP 哈希的对象)。

三个字段都偏弱,导致可追溯性只能靠人去读 README,无法脚本化核对。

8.6 另外两处小问题

  • README 的「能力一览」只有 2 行、表述空泛(“组件或工具 API 支持”),没有列出 12 个 spinner 的名字——而这个库的全部价值恰恰就是"有哪 12 个动画";
  • 上游 package.json 的 files 白名单被删掉了,发布内容不再受限。

图 4:构建产物里  出现 16 次、 出现 0 次;以及  的退出码 1


九、已知限制

9.1 库本身的

  1. 没有 default 导出,必须用具名导入。照错误文档写会拿到 undefined。
  2. Flow 的根视图是 size × size*0.25,其余 11 个是 size × size。写通用布局代码时要留意。
  3. animating=false 时动画被 reset()、值归 0,也就是相位会丢失——重新设回 true 是从起点重新开始,不是从暂停处继续。
  4. hidesWhenStopped 只改 opacity,不影响布局;所以"隐藏"后元素仍占位。
  5. 所有 spinner 都依赖 useNativeDriver: true(非 web)。如果某个平台的 RN 实现不支持原生动画驱动,会静默回退到 JS 驱动——动画仍可跑,但驱动方式与性能会变。在鸿蒙上这一点已确认没问题(第七节)。

9.2 本次验证的边界

  1. 只在一台模拟器上验证(Pura X View,1320×2232,density 3),没有真机。
  2. 12 个 spinner 只逐像素验证了 3 个(Wave、Circle、Grid)。其余 9 个只验证了"渲染成功 + 根尺寸正确",没有逐个确认动画观感与原版一致。
  3. 没有校验动画周期:源码里各 spinner 的 duration 从 1200 ms 到 2500 ms 不等,我只确认"在动",没有测周期是否精确。
  4. 没有断言相位:animating 中途切换的行为(是否从 0 重新开始)在协议里发生过,但没有做相位断言。
  5. 没有跨平台视觉对照:没在 Android/iOS 上跑同一份用例比对观感。
  6. 帧率只测了空闲状态(63.7 fps);高负载/低端设备未测。
  7. 组合场景未测:12 个动画同时播放时的帧率、以及页面滚动时的表现。
  8. 交付包的 git 安装路径未验证(本次败在归档下载,属环境问题)。

未改动库代码。 发现的 4 处问题全部属于工程配置与文档层面。


十、常见问题

Q1:这个库需要 HAR、权限或 ohpm 接线吗?

都不需要。它是纯 JS,12 个组件全靠 Animated 驱动,没有原生模块。判断信号是 link-harmony 的计数不增加(本次输出 linked 10 libraries, skipped 1 libraries)。

Q2:怎么引?

用具名导入——这个库没有 default 导出。

// ✅ 正确
import {Plane, Wave, Grid} from 'react-native-animated-spinkit';

// ❌ 拿到 undefined
import Spinkit from 'react-native-animated-spinkit';

Q3:有哪些 spinner?

import {
  Plane, Bounce, Pulse, Swing, Chase, Wave,
  Flow, Circle, CircleFade, Grid, Fold, Wander,
} from 'react-native-animated-spinkit';

共 12 个。组件属性是 size(默认 48)、color(默认 #000)、animating(默认 true)、hidesWhenStopped(默认 true),其余 ViewProps 会透传到根视图。

Q4:为什么要在意 useNativeDriver?

因为这个库在非 web 平台强制使用原生动画驱动(useNativeDriver: true)。如果平台不支持,React Native 会静默回退到 JS 驱动——动画照样动,但驱动方式、性能和语义都变了。

在鸿蒙上可以确认没问题:

hdc shell "hilog -x | grep -i 'NativeAnimatedTurboModule'"

看到 Creating Turbo Module: NativeAnimatedTurboModule 且日志里没有 useNativeDriver is not supported 这类告警,就说明原生驱动生效了。

Q5:npm install 报了 'bob' is not recognized,但编译却过了,要紧吗?

要紧。 这是交付包保留了上游的 prepare: "bob build" 脚本导致的,bob(react-native-builder-bob)只是库的 devDependencies,消费者侧没有。

它之所以没耽误编译,是因为 npm 在跑 prepare 之前就已经建好了软链接,Metro 照样能解析。但:

  • npm install 的退出码是 1 ⇒ 任何以它做门禁的 CI 会中断;
  • npm pack 也会失败,打不出 tgz。

临时绕过:用 tgz 安装(prepare 不会执行),或者忽略该步骤的退出码并把 prepare 从依赖的 package.json 里去掉。根修建议还是交付方删掉 prepare。

Q6:怎么自己确认动画真的在跑?

连拍两帧做像素差:

hdc shell "snapshot_display -f /data/local/tmp/a.jpeg"
# 等 ~260 ms
hdc shell "snapshot_display -f /data/local/tmp/b.jpeg"
hdc file recv /data/local/tmp/a.jpeg E:\tmp\a.jpeg
hdc file recv /data/local/tmp/b.jpeg E:\tmp\b.jpeg

然后在 PC 上逐像素比。记住要做一个对照:把 animating 设成 false 再拍两帧,这两帧应当完全相同。只有"动的时候有差异、停的时候没差异",差异才能归因到动画。

Q7:怎么判断"隐藏"是真的隐藏了?

看对照区里的非白像素占比:

  • hidesWhenStopped=true → 应该 0%,平均色是纯白 (255,255,255);
  • hidesWhenStopped=false(且已停止)→ 应该仍有一片非白像素(本次实测 14.66%)。

第二条不能省。 没有它,“0% 非白"有可能只是因为那个区域压根没渲染出来——那就不是"隐藏生效”。

Q8:animating 从 false 切回 true,会从原地继续吗?

不会。animating=false 时库调用的是 animation.reset() 并把值设为 0,所以再切回 true 是从起点重新开始。


小结

react-native-animated-spinkit 是个纯视觉库:12 个加载动画,没有原生模块、没有返回值、没有系统状态可读。

这轮真正要解决的问题是"怎么验一个只有视觉的东西"。做法和体会:

  1. 效果类输出只能靠真实产物 + 对照。 连拍两帧做像素差,一次能回答三件事:在不在动(差异 > 0)、停没停(差异 = 0)、该隐藏时是否真的不可见(整片纯白)。本次实测三档分别是 6.17% / 0.00% / 0.00%。

  2. "静止时仍然可见"这条对照不能省。 有它,才能把"隐藏生效"和"区域根本没渲染"区分开——前者是功能正常,后者是测量失败,但两者的数字长得一模一样。

  3. 裁剪区要在每个场景里重新定位,不能复用固定坐标。 我早期拿到过一个假的 0.00%:看着像"动画确实停了",实际是页面滚动后裁剪区落在了深色背景上。一个漂亮的结果,先确认被测对象还在画面里。

  4. 结论要建立在权威证据上,不要建立在自己推演的启发式上。 我一开始用"原生驱动时 JS 侧 listener 不更新"来判断驱动方式,实测把它推翻了——RN 的原生驱动本身会通过 onAnimatedValueUpdate 回调 JS 监听。真正的判据是"TurboModule 是否存在 + 日志里有没有回退告警"。启发式看起来很像证据,但它只是猜测。

  5. “能编译过"可能掩盖"安装已经失败”。 这个交付包的 npm install 退出码是 1,但因为软链接先建好了、编译又不看退出码,本地一切正常。把 npm install 的退出码当独立检查项,并顺手试一次 npm pack。

  6. 文档里的"入口"和"用法"都要实测一遍。 这份 README 说 Metro 用 lib/commonjs 入口(实测是 src),用法示例用 default import(实测没有 default 导出)。两条都会让照文档接入的人踩空。

关于交付包:实现文件与上游逐字节一致,动画表现也完全正常;问题集中在工程配置与文档——npm install/npm pack 失败、入口说法错误、用法示例错误、契约测试不测库。其中第一条影响 CI,建议优先修;后三条改文档即可。


本篇用到的库

项内容
三方库react-native-animated-spinkit(上游 1.5.2 的鸿蒙适配版)
适配仓库https://atomgit.com/oh-react-native/react-native-animated-spinkit
适配 TAG1.5.2-ohos-1.0.0
需要 HAR / 权限 / ohpm都不需要(纯 JS)
上游仓库https://github.com/tienphaw/react-native-animated-spinkit(基线 commit aa452f7898719a2acfa466dea4b215b21eddbb21,MIT)
宿主工程RNOH084Demo(测试页 rnAppKey = SpinnerTestApp)

接入方式(纯 JS,零原生接线):

// package.json
"react-native-animated-spinkit": "file:../react-native-animated-spinkit"
// metro.config.js —— file: 装进来是 junction,必须让 Metro 找得到源码
watchFolders: [path.resolve(__dirname, '../react-native-animated-spinkit')],
// 必须具名导入 —— 这个库没有 default 导出
import {
  Plane, Bounce, Pulse, Swing, Chase, Wave,
  Flow, Circle, CircleFade, Grid, Fold, Wander,
} from 'react-native-animated-spinkit';

// 基本用法
<Wave size={48} color="#1f6feb" />

// 停止并隐藏(默认 hidesWhenStopped = true)
<Wave size={48} color="#1f6feb" animating={false} />

// 停止但保留占位
<Wave size={48} color="#1f6feb" animating={false} hidesWhenStopped={false} />

// 其余 ViewProps 会透传到根视图
<Circle size={64} color="#333" style={{marginTop: 12}} />
# 换页启动测试页(force-stop 不能省,换页参数只在冷启动生效)
hdc shell aa force-stop com.rnoh084.demo
hdc shell aa start -b com.rnoh084.demo -a EntryAbility --ps rnAppKey SpinnerTestApp

验证环境

项版本
React Native0.84.1
React19.2.3
RNOH(npm / ohpm)@react-native-oh/react-native-harmony / @rnoh/react-native-openharmony 0.84.3
Node.jsv24.14.0
DevEco Studio26.0.0.621
HarmonyOS SDKAPI 26(26.0.0.32)
设备HarmonyOS 7.0.0(26.0.0) Beta2 模拟器 Pura X View(ohos-x64),1320×2232,density 3
动画驱动原生驱动(NativeAnimatedTurboModule 可用,无回退告警)
帧率requestAnimationFrame ≈ 63.7 fps
宿主 HAP 产物entry-default-signed.hap(81.61 MB)
本次增量构建assembleHap 6 分 55 秒,HAP +98,305 字节
验证规模设备侧 35 / 35 断言全部通过;像素比对逐个场景:动画中 6.17% / 6.16% / 3.64% / 23.26%,停止后 0.00%,隐藏后对照区非白像素 0.00%

欢迎加入 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

Logo

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

更多推荐