React Native for OpenHarmony 实战:三方库 react-native-animated-ellipsis 的鸿蒙化适配指南

发布时间:2026/10/19 2:13:21
React Native for OpenHarmony 实战:三方库 react-native-animated-ellipsis 的鸿蒙化适配指南
本文记录把react-native-animated-ellipsis加载中的动画省略号适配到 HarmonyOS 的完整过程。它做的事很专一渲染 N 个点让它们逐个渐亮形成加载中……的循环动画。结论可用而且是判据最漂亮的一轮—— 我用点文本节点计数和字号比两把尺子把它的行为精确核对到了小数点后两位。另外它有一个值得记下来的前置条件它引用了 RN 已移除的Text.propTypes本来会 import 即崩—— 而工程里已有的一个 polyfill 正好把它也救了。★★★★★ 一、2179 B 的全部实现dist/AnimatedEllipsis.js去掉注释后就是这么多export default class AnimatedEllipsis extends Component { constructor(props) { super(props); this._animation_state { dot_opacities: this.initializeDots(), // ★ 只在 constructor 跑一次 target_opacity: 1, should_animate: true, }; } initializeDots() { const opacities []; for (let i 0; i this.props.numberOfDots; i) { opacities.push(new Animated.Value(this.props.minOpacity)); } return opacities; } componentDidMount() { this.animate_dots.bind(this)(0); } componentWillUnmount() { this._animation_state.should_animate false; } animate_dots(which_dot) { if (!this._animation_state.should_animate) return; if (which_dot this._animation_state.dot_opacities.length) { which_dot 0; const min this.props.minOpacity; this._animation_state.target_opacity this._animation_state.target_opacity min ? 1 : min; // ★ 来回翻转 } const next_dot which_dot 1; Animated.timing(this._animation_state.dot_opacities[which_dot], { toValue: this._animation_state.target_opacity, duration: this.props.animationDelay, }).start(this.animate_dots.bind(this, next_dot)); // ★ 链式一个结束再动下一个 } render() { const dots this._animation_state.dot_opacities.map((o, i) Animated.Text key{i} style{[this.props.style, {opacity: o}]}{ }{.}/Animated.Text ); return View style{styles.container}{dots}/View; // container: {flexDirection: row} } }两个要点每个点是一个Animated.Text文本内容是 .一个空格 一个点⇒在布局 dump 里可以数出来✓initializeDots()只在构造函数里调用一次⇒运行中改numberOfDots不会重建点⚠️这是使用限制所以我用三个不同实例做对照defaultProps/propTypes整块读出来的原文AnimatedEllipsis.propTypes{numberOfDots:PropTypes.number,animationDelay:PropTypes.number,minOpacity:PropTypes.number,style:Text.propTypes.style,// ★ L74 —— 就是这一行会崩};AnimatedEllipsis.defaultProps{numberOfDots:3,animationDelay:300,minOpacity:0,style:{color:#aaa,fontSize:32},};★★★★★ 二、前置条件它引用了已移除的Text.propTypesAnimatedEllipsis.jsL74style:Text.propTypes.style,RN 从 0.62 起移除了Text.propTypes我在 RN 0.84 上确证过Libraries/Text/Text.js里propTypes命中 0所以这一行会在模块加载期抛Cannot read property style of undefined⇒库 import 即崩✗但这次不用再写一遍补丁—— 工程里已有的polyfills/text-prop-types.js已经在入口文件的第 28 行引入了// index.js第 28 行在所有页面 import 之前import./polyfills/text-prop-types;实测页面判据确认它还生效NOTE | Text.propTypes 是否存在应为 object object PASS | ★★★ polyfill 生效Text.propTypes 存在否则本库 import 即崩⇒⇒ 一个 20 行的兼容补丁修好了两个库✓✓ ——这类只在propTypes声明里引用已移除 API的老库补丁是通用的。★★★★★ 三、判据一点文本的计数精确等于numberOfDots我放了三个实例numberOfDots 3 / 5 / 2然后数布局 dump 里内容是 .的Text节点 . w49 h113 x296 y1331 ← E1 第 1 个点 . w49 h113 x344 y1331 ← E1 第 2 个点 . w49 h113 x392 y1331 ← E1 第 3 个点 . w49 h113 x197 y1601 ← E2 第 1 个点 . w49 h113 x245 y1601 ← E2 第 2 个点 . w49 h113 x293 y1601 ← E2 第 3 个点 . w49 h113 x341 y1601 ← E2 第 4 个点 . w49 h113 x389 y1601 ← E2 第 5 个点 . w37 h84 x145 y1871 ← E3 第 1 个点 . w37 h84 x181 y1871 ← E3 第 2 个点 ⇒ 点文本节点数 10预期 10 352 ✓✓三组各自的数量完全对上实例numberOfDots实测点数y 坐标每个点的宽度E13✅3133149E25✅5160149E32✅2187137而且同一组内的 x 间隔恰好等于点宽度344-29648≈49、245-19748≈49⇒ 点是一个紧挨一个排的 ✓★★★★★ 四、判据二字号比的三重一致这一条最有说服力E3 传了style{{fontSize: 24}}其余两个用默认fontSize: 32。于是可以做一个三路交叉验证量E1/E2fontSize 32E3fontSize 24比值点的宽度49370.755点的行高113840.743字号本身32240.750⇒三个比值全部落在 0.74~0.76 之间与24/32 0.75吻合✓✓✓这说明三件事同时成立styleprop 里的fontSize确实覆盖了defaultProps的 32 ✓这个字号确实影响了字形度量宽度与行高都按比例缩小✓RNOH 的文本测量是准确的不是看起来差不多而是比例能对上✓⇒这种用一个已知比值去核对多个独立测量值的方法比单点断言强得多✓★★★ 五、动画部分只能证明结构对 不崩它用Animated.timing(...)逐个改变opacityAnimated.timing(this._animation_state.dot_opacities[which_dot],{toValue:this._animation_state.target_opacity,duration:this.props.animationDelay,}).start(this.animate_dots.bind(this,next_dot));⚠️两点必须写清opacity不进布局⇒ 布局 dump 里看不到哪个点更亮⇒动画过程本轮无法用 dump 验证只能看截图四张截图互异说明画面在变✓Animated.timing没传useNativeDriver⇒ 按经验会有Animated: useNativeDriver was not specified警告 ——但实测屏上无 LogBox✓⇒ 所以只能记未观察到该警告不能写没有这个警告 ✓LogBox 实测屏上无通知✓★★★ 六、其余预检结论预检项结果ListView/createClass/React.PropTypes/ViewPropTypes/ 字符串ref/this.refs./mixins/componentWillReceiveProps/componentWillMount/getDefaultProps✅✅全部为空唯一的老写法就是那行Text.propTypesisRequired✅空无必填 prop 陷阱peerDependencies✅空连react/react-native都没声明但实际需要Platform.OS/measure()/onLayout/NativeModules✅全空dependencies⚠️prop-types宿主已有babel-preset-stage-2Babel 6 时代包只用于构建scripts.prepare❌npm run buildbabel⇒安装必须--ignore-scripts交付包形态⚠️只有dist/已编译的 ESM没有src/七、接入与预检要点# ★ 必须 --ignore-scripts它的 scripts.prepare 是 npm run buildbabelnpminstallgithttps://atomgit.com/oh-react-native/react-native-animated-ellipsis.git#2.0.0-ohos-1.0.0--ignore-scripts# ★ 还需要在入口最前面引入 Text.propTypes 的 polyfill否则 import 即崩八、交付包质量与基线说明类别结果交付包内容⚠️只有dist/AnimatedEllipsis.js2179 B已编译的 ESM.babelrc⇒没有src/spec.json⚠️缺upstreamCommitvalidation⚠️build-pass-device-pending⚠️ 声明的基线reactNative: 0.88.0rnoh: 0.84.4⇒两字段自相矛盾备注库代码零改动靠已有的 polyfill 兼容关于基线本次验证在RN0.84.1 RNOH0.84.3上完成。⚠️ 它的Text.propTypes是版本相关问题 ⇒ 换基线时需要 polyfill 才可用其余结论跨基线成立。九、已知限制与本次验证边界9.1 用这个库要知道的事项说明★★★★★ 必须补Text.propTypesL74 引用了 RN ≥0.62 已移除的 API ⇒ 不补就 import 即崩★★★★★ 每个点是内容为 .的Animated.Text所以点数完全可以数出来验证★★★★style.fontSize会影响点的宽度与行高实测 32→24 时宽度/行高按0.75比例缩小 ✓★★★★initializeDots()只在 constructor 跑运行中改numberOfDots不会重建点⇒ 要变点数请换实例或用key重挂载★★★Animated.timing没传useNativeDriver实测未观察到警告opacity不进布局 ⇒ 动画只能看截图★★scripts.prepare会跑 babel安装必须--ignore-scripts★★ 只有dist/没有src/想看源码得读已编译的 ESM★dependencies里有babel-preset-stage-2Babel 6 时代包只用于构建运行时不需要9.2 本次验证的边界★★★ 动画过程逐个渐亮没有数值级验证opacity不进布局只能靠截图四张互异间接说明画面在变★★★ 未验证运行中修改numberOfDots源码显示不会重建但没实测换 prop 的表现★★ 未验证animationDelay不同值的效果Animated.timing的时长无法从 dump 读出★★ 未验证minOpacity的实际视觉下限未验证在Text内部嵌套使用README 的推荐用法是放在Loading文字后面未验证多个实例并存时的动画同步/性能未验证numberOfDots很大的情况如 20 个点的排布未在RN 0.88.0/RNOH 0.84.4上复验未测其它屏幕宽度未做跨平台对照。库代码未改动。十、常见问题Q1它在 RN 0.84 上能跑吗加了Text.propTypes的 polyfill 之后能跑。三个实例的点数3/5/2与总点数10精确对上字号覆盖引起的宽度/行高变化比例0.755 / 0.743与字号比0.75吻合LogBox无通知。Q2一装就报Cannot read property style of undefined因为AnimatedEllipsis.jsL74 用了Text.propTypes.style而RN ≥0.62 已移除Text.propTypes。加 polyfill// polyfills/text-prop-types.jsconst{Text}require(react-native);if(!Text.propTypes)Text.propTypes{style:{}};// 入口文件最前面所有业务 import 之前import./polyfills/text-prop-types;Q3为什么改了numberOfDots没反应因为它们在constructor里一次性创建initializeDots()之后不会重建。要动态改点数换一个实例或给组件加key强制重挂载。Q4点的颜色/大小怎么改传styleAnimatedEllipsis style{{color: #D0021B, fontSize: 24}} /实测fontSize从 32 改成 24 后点的宽度从 49 变成 37、行高从 113 变成 84比例都约 0.75✓Q5为什么布局 dump 里看不到哪个点更亮动画改的是opacity而opacity不参与布局⇒ dump 里只有结构没有亮度。要看动画效果只能截图。Q6能放在一行文字中间吗可以README 的推荐用法就是Loading后面接它它外层是flexDirection: row的View。⚠️ 本轮没验证这种内联布局记为未验证。Q7npm install报babel相关错误它的scripts.prepare是npm run buildbabel src/ -d dist而包里没有src/只有dist/⇒ 安装时执行必然失败。加--ignore-scripts即可。小结react-native-animated-ellipsis是2179 B 的加载动画省略号渲染 N 个内容为 .的Animated.Text用链式Animated.timing让它们逐个渐亮。老写法九项全空唯一的历史包袱是引用了已移除的Text.propTypes。最值得记下来的是这几条★★★★★ 一个 polyfill 修好了两个库。它栽在Text.propTypes.style上 —— 而这次不用再写一遍补丁之前那份polyfills/text-prop-types.js已经在入口第 28 行生效 ⇒兼容层是可复用的✓✓★★★★★ 点文本计数3 5 2 10三组各自精确。因为每个点都是独立的一个Text所以点数这个抽象属性变成了可直接数的节点数✓⇒挑能被 dump 直接数出来的最小单元做判据是最省事的路子。★★★★★ 字号比的三重一致0.755 / 0.743 / 0.75。宽度比、行高比、字号比三者吻合⇒ 同时证明了style 覆盖生效、“字号确实影响字形度量”、“RNOH 文本测量准确”。⇒用一个已知比值去核对多个独立测量值比单点断言强得多✓★★★★ 又一次确认改 prop 不生效这类设计限制要提前读出来。initializeDots()只在constructor跑 ⇒ 改numberOfDots不重建 ⇒所以 A/B 必须用不同实例✓读源码 2179 B 的成本几乎为零收益是避免了改 prop 没反应的误判★★★opacity动画的验证边界要说清。opacity不进布局 ⇒ dump 看不到 ⇒只能用截图间接说明画面在变不能声称验证了渐亮顺序 ✓★★ 别把预期会出现的警告写成结论。Animated.timing确实没传useNativeDriver但实测屏上无 LogBox⇒ 只写未观察到 ✓★scripts.prepare与包结构不匹配会阻塞安装它的prepare是babel src/ -d dist而包内只有dist/⇒ 执行必失败 ⇒--ignore-scripts是唯一正确姿势✓本篇用到的库项内容三方库react-native-animated-ellipsis上游2.0.0的鸿蒙适配版适配仓库https://atomgit.com/oh-react-native/react-native-animated-ellipsis适配 TAG2.0.0-ohos-1.0.0需要 HAR / 权限 / ohpm✅都不需要库类型加载动画省略号N 个点逐个渐亮导出形态默认导出运行时依赖prop-types宿主已有⚠️ 必须的额外步骤入口最前面引入Text.propTypespolyfill否则 import 即崩⚠️scripts.preparenpm run buildbabel⇒安装需--ignore-scripts许可证MIT上游仓库https://github.com/adorableio/react-native-animated-ellipsis宿主工程RNOH084Demo测试页rnAppKey AnimatedEllipsisTestAppnpminstallgithttps://atomgit.com/oh-react-native/react-native-animated-ellipsis.git#2.0.0-ohos-1.0.0--ignore-scriptsimport AnimatedEllipsis from react-native-animated-ellipsis; // 基本用法默认 3 个点、delay 300、minOpacity 0、fontSize 32 TextLoadingAnimatedEllipsis //Text // 自定义点数 / 速度 / 样式 AnimatedEllipsis numberOfDots{5} animationDelay{500} style{{color: #D0021B, fontSize: 24}} / // ⚠️ 必须先在入口最前面引入 Text.propTypes 的 polyfillRN ≥0.62 已移除该 API // ⚠️ initializeDots 只在 constructor 跑 ⇒ 改 numberOfDots 不会重建点要变点数请换实例或加 key // ⚠️ Animated.timing 未传 useNativeDriveropacity 不进布局 ⇒ 动画只能看截图 // ⚠️ 安装必须 --ignore-scriptsprepare babel build而包里没有 src/# 换页启动测试页 —— force-stop 不能省hdc shell aa force-stop com.rnoh084.demo hdc shell aa start-bcom.rnoh084.demo-aEntryAbility--psrnAppKey AnimatedEllipsisTestApp判据的复现方式# ① 契约4 项 defaultProps polyfill 是否生效hdc shellhilog -x | grep aev-test|Select-StringPASS|FAIL|SUMMARY|NOTE# ② ★ 数点文本内容是 . 的 Text 节点devecocli ui layout|Select-StringText#Paragraph# ⇒ 三个实例的点数应分别为 3 / 5 / 2总数 10# ③ ★ 字号比fontSize 24 的实例宽度与行高应约为 fontSize 32 的 0.75 倍# 实测 宽度 37 vs 490.755、行高 84 vs 1130.743验证环境项版本React Native0.84.1React19.2.3RNOHnpm / ohpmreact-native-oh/react-native-harmony/rnoh/react-native-openharmony0.84.3Node.js / npmv24.14.0/11.9.0DevEco Studio26.0.0.621HarmonyOS SDKAPI 2626.0.0.32设备HarmonyOS 7.0.0(26.0.0) Beta2 模拟器Pura X Viewohos-x641320×2232density 3本机状态栏高度117px 39vp宿主依赖prop-types已有Text.propTypespolyfill宿主 HAP 产物entry-default-signed.hap本次构建assembleHap3 分 32 秒⇒ 一次成功打包与构建退出码都检查了验证规模契约 11/11 全 PASS点文本计数精确 10352字号比三重一致0.755 / 0.743 / 0.75LogBox 无通知4 张截图互异⚠️基线说明交付包spec.json声明RN 0.88.0 RNOH 0.84.4两字段自相矛盾本次验证在RN 0.84.1 RNOH 0.84.3上完成。⚠️ 它的Text.propTypes是版本相关问题需 polyfill其余结论跨基线成立。相关社区与项目地址项目地址React Native 鸿蒙官方社区CPF-RNhttps://atomgit.com/CPF-RNRNOH 框架仓库https://atomgit.com/openharmony-RN/ohos_react_nativeRNOH 三方库使用文档https://atomgit.com/OpenHarmony-RN/usage-docsReact Native for OpenHarmony 组织https://atomgit.com/oh-react-nativeRN 三方库鸿蒙适配清单https://atomgit.com/oh-react-native/rn-ohos-adaptation-overview欢迎加入 CPF-RN 鸿蒙社区https://atomgit.com/CPF-RNReact Native for OpenHarmony 组织https://atomgit.com/oh-react-nativeRN 三方库鸿蒙适配清单https://atomgit.com/oh-react-native/rn-ohos-adaptation-overview