本文记录把react-native-progress-bar-animated带动画的进度条适配到 HarmonyOS 的完整过程。它做的事很专一画一根进度条—— 外层是带边框的容器内层是一条Animated驱动的填充条宽度按百分比增长可选在走满时把底色渐变过去、并回调一个onComplete。结论可以用而且几何能做到像素级精确核对7 组预言全部命中。但它有两处**现在能跑、但有隐患的写法还有一个契约层面的运行时事实与源码不一致**这三点比能不能跑更值得写下来。★★★★★ 一、几何7 组像素级预言全部精确命中核心公式只有一行src/AnimatedProgressBar.js的animateWidthanimateWidth(){consttoValue((this.props.width*this.state.progress)/100)-this.props.borderWidth*2;Animated.timing(this.widthAnimation,{easing:Easing[this.props.barEasing],toValue:toValue0?toValue:0,duration:this.props.barAnimationDuration,}).start();}⇒内层填充条的宽度 width × 百分比 − 边框×2单位 vp⇒可以精确预言✓实测density 3我把三个实例放在同一屏用布局 dump 逐条核对实例参数公式预言实测布局 dumpE1 外层容器width300、height15、borderWidth1300vp / 15vp900 × 45px✅y1257 w900 h45E1 内层填充value100maxValue300×100/100 − 2 298vp894px✅y1260 **w894** h39E1 内层填充value50300×50/100 − 2 148vp444px✅w444E3 内层填充value75、height10300×75/100 − 2 223vp669px✅y1715 **w669** h24E2 外层容器height2424vp72px✅y1471 w900 h72E3 外层容器height1010vp30px✅y1712 w900 h30内层填充高度任意height − borderWidth×2 15−2 13vp39px✅h39⇒七组全部命中900/894/444/669/72/30/39这七个像素数与公式逐一对上✓✓⇒顺带验证了toValue 0 ? toValue : 0那个保护value0时内层宽度为 0它在 dump 里根本不会出现见第三节。★★★★ 二、两处现在能跑、但有隐患的写法2.1componentWillReceiveProps—— React 已废弃的旧生命周期但它【仍然被调用】// L29没有 UNSAFE_ 前缀componentWillReceiveProps(props){if(props.value!this.state.progress){if(props.value0props.valuethis.props.maxValue){this.setState({progress:props.value},(){if(this.state.progressthis.props.maxValue){constcallbackthis.props.onComplete;if(callback){setTimeout(callback,this.props.barAnimationDuration);}}});}}}这段代码承担了关键职责state.progress只在这里更新而animateWidth()在componentDidUpdate里调用读的正是this.state.progress。⇒ 所以它一旦不再被调用就会连锁出现**“宽度不跟着变 onComplete永不触发”**。实测页面周期性读实例的state并与props.value对比PROBE | instance.state.progress 0 / props.value 0 PROBE | instance.state.progress 50 / props.value 50 ← ★ 跟随了 PROBE | instance.state.progress 100 / props.value 100 EVENT | onComplete 触发第 1 次 ← ★ onComplete 也触发了 PROBE | instance.state.progress 0 / props.value 0⇒在这套 React / RNOH 上旧生命周期仍然被调用state正常跟随、onComplete正常触发✓✓⇒但它已被 React 废弃⇒ 记作迁移风险升级 React 大版本时要重点回归这条链路✓2.2Animated没有传useNativeDriver—— 必然告警Animated.timing(this.widthAnimation,{easing:Easing[this.props.barEasing],toValue:…,duration:this.props.barAnimationDuration,}).start();// ★ 没有 useNativeDriver⇒而它驱动的是width布局属性⇒ 本来就应该显式写useNativeDriver: false✓实测LogBox 通知原文Animated: useNativeDriver was not specified. This is a required option and must be explicitly set to true or false⇒ 与useNativeDriver是必填项这条规则完全一致✓⇒实践含义这类库在当前 RN 上会出现一条常驻通知如果你要自己封装进度条记得显式写上useNativeDriver: false✓⚠️ 有一点和预期不同没有出现旧生命周期已废弃的告警只有useNativeDriver这一条。所以用componentWillReceiveProps会被警告这个印象在当前 React 版本上并不成立 ——以实测为准✓★★★ 三、一个dump 盲区的补充宽度为 0 的节点不出现value0时toValue被保护成 0toValue:toValue0?toValue:0实测value0的两个相位P0 与 P3里高 39px 的条一个都找不到---- P0value0 → 高 39px 的条共 0 个 ---- ---- P3value0 → 高 39px 的条共 0 个 ---- ---- P1value50 → w444命中 ---- ---- P2value100 → w894命中 ----⇒宽度 0 的节点和opacity: 0的节点一样都不会出现在布局 dump 里✓⇒⇒ 结论扩充凡是用尺寸归零或透明度归零来隐藏的组件布局 dump 对它是盲区——判定到底渲染了没有必须用JS 侧证据实例state、日志不能只看 dump ✓★★★★ 四、契约反射12 项defaultProps 7 个原型方法全部命中4.1defaultProps12 项逐项精确NOTE | defaultProps {value:0,maxValue:100,barEasing:linear, barAnimationDuration:500,backgroundAnimationDuration:2500,height:15, backgroundColor:#148cF0,backgroundColorOnComplete:null,borderWidth:1, borderColor:#C8CCCE,borderRadius:6,onComplete:null} PASS × 12每一项都用 JSON 比对⇒ 注意backgroundColor是#148cF0源码里大小写就是这个样子F是大写✓4.2 原型方法7 个全在prototype上constPROTO[constructor,componentDidMount,componentWillReceiveProps,componentDidUpdate,animateWidth,animateBackground,render];⇒NOTE|原型方法命中数源码7个7PASS⇒这个库全用原型方法、没有箭头类字段⇒ 这种库必须用ref才能调内部方法而箭头类字段的库方法挂在实例上又不一样——写断言前先看源码是哪种写法✓4.3 ⚠️ 一个源码与运行时不一致的事实isRequired标记读不到源码里明明写着width:PropTypes.number.isRequired,但运行时读到的是NOTE | propTypes 的键数 13 NOTE | propTypes 的键 value, maxValue, barEasing, barAnimationDuration, backgroundAnimationDuration, width, height, backgroundColor, backgroundColorOnComplete, borderWidth, borderColor, borderRadius, onComplete FAIL | ★ width 是 isRequired | 期望true | 实际false⇒propTypes的 13 个键都在和源码一致 ✓但width.isRequired读出来是false✓⇒⇒ 记作实测事实不要用运行时读propTypes来判断某个 prop 是否必填它取决于prop-types的实现与构建模式要判断必填性看源码或.d.ts✓⚠️ 这一条也顺手纠正了我自己的一个错误我最初把propTypes的项数写成 15实际13——断言里的数量最好从源码里数清楚而不是凭印象写。五、接入与预检步做法结果①harmony.autolinking/harmony//ios//android/都没有②原生痕迹预扫✅无原生源文件NativeModules/requireNativeComponent全空③★ 老写法九项预扫⚠️命中 1 项componentWillReceiveProps不带UNSAFE_前缀其余 8 项全空④measure()/measureInWindow/onLayout/findNodeHandle✅全空没有用量容器宽度那套 ⇒ 少了一类环境坑⑤平台分支✅Platform.OS/Platform.select全空⑥useNativeDriver⚠️一处都没写⇒ 必然出现useNativeDriver was not specified通知⑦依赖✅ 只有prop-types ^15.6.0宿主已有⇒零外部原生依赖⑧scripts扫prepare家族✅无只有testlint⇒无脚本阻塞老写法九项逐项结果项结果React.createClass✅ 空React.PropTypes✅ 空从react具名导入PropTypes✅ 空ViewPropTypes/TextPropTypes✅ 空字符串refx/this.refs.x✅ 空mixins:✅ 空componentWillReceiveProps⚠️命中 1 处L29无UNSAFE_前缀componentWillMount✅ 空measure(/measureInWindow✅ 空它还用了正确的现代写法classcomponentDidMountcomponentDidUpdate✓只有那一个旧生命周期# 从适配仓库安装唯一依赖 prop-types宿主通常已有npminstallgithttps://atomgit.com/oh-react-native/react-native-progress-bar-animated.git#1.0.6-ohos-1.0.0--ignore-scripts六、交付包质量与基线说明类别结果交付包内容index.js101 Bre-exportsrc/AnimatedProgressBar.js3879 B⇒ 单体组件、源码可读 ✓spec.json⚠️缺upstreamCommitvalidation⚠️build-pass-device-pending⚠️ 声明的基线reactNative: 0.88.0rnoh: 0.84.4verification: evidence/batch50/…⇒两字段自相矛盾rnoh 0.84.4是配 RN0.84.x的且既不是当前宿主0.84.1/0.84.3也不是权威 0.88 基线⚠️ 许可证上游 GitHub 的license字段为空交付包与 npm 均记ISC交付包自带契约测试只校验包名/版本/入口文件存在/有中文鸿蒙说明四件事不渲染组件关于基线本次验证在RN0.84.1 RNOH0.84.3上完成。契约与几何结论跨基线成立但第二节那条旧生命周期是否还被调用必须随 React 大版本复验这是本轮唯一带时效性的结论。七、已知限制与本次验证边界7.1 用这个库要知道的事项说明★★★componentWillReceiveProps已被 React 废弃当前仍被调用实测state.progress跟随、onComplete触发⇒升级 React 时要重点回归这条链路★★★Animated未传useNativeDriver会常驻一条Animated: useNativeDriver was not specified…通知自己封装时应显式写useNativeDriver: false★★★★ 内层宽度公式width × 百分比 − borderWidth × 2vp⇒ density 3 下乘 3 得像素★★width是必填 prop没有默认值不传会导致NaN参与计算★height默认 15、borderWidth默认 1⇒ 内层高度 height − 2默认 13vp 39px★onComplete只在值达到maxValue时触发且是setTimeout(callback, barAnimationDuration)之后★backgroundColorOnComplete为空时不构造插值走this.props.backgroundColor直传★ 全用原型方法无箭头类字段要调内部方法必须用ref★barEasing有 6 个白名单值bounce/cubic/ease/sin/linear/quad取自Easing[...]⚠️ 宽度为 0 的节点不出现在布局 dump用 dump 判断有没有渲染会漏判7.2 本次验证的边界未做真机验证只在模拟器上未做跨平台对照未逐帧采集动画曲线只核对了各相位的终点宽度0 / 444 / 669 / 894未验证barEasing各取值的实际曲线E3 用了bounce只核对了终点宽度未验证barAnimationDuration/backgroundAnimationDuration的时序只知onComplete会在动画后触发未验证backgroundColorOnComplete的颜色插值结果dump 不反映颜色只验证了它不崩、几何正确未验证borderRadius/borderColor的视觉效果未验证maxValue取非 100 值时的行为未验证传入超出[0, maxValue]的value源码里有范围判断但只从源码推断未验证反复快速改value时的动画行为未在RN 0.88.0/RNOH 0.84.4上复验交付包声明的基线未测其它屏幕宽度。未改动库代码。八、常见问题Q1它在 RN 0.84 上能跑吗能。几何七组像素级预言全部命中900/894/444/669/72/30/39defaultProps12 项、原型方法 7 个全部核对一致state.progress正常跟随props.valueonComplete正常触发。Q2为什么控制台一直有一条useNativeDriver通知因为库的Animated.timing(...)没有传useNativeDriver。而它驱动的width是布局属性本来就该显式写false。⇒ 这条通知不影响功能自己封装进度条时记得写上。Q3我改value为什么宽度没变先确认两点①width是必填不传会算出NaN② 这个库把state.progress放在componentWillReceiveProps里更新 ——若将来 React 版本移除了该生命周期state就不会跟随本次实测仍可用。Q4怎么算内层条有多宽内层宽度(vp)(width*value/100)-borderWidth*2内层高度(vp)height-borderWidth*2默认borderWidth1、height15⇒ 内层高13vpdensity 3 ⇒ 39px。例width300, value50⇒148vp⇒444px实测一致 ✓。Q5onComplete什么时候触发只有value达到maxValue时且是setTimeout(callback, barAnimationDuration)之后。⚠️ 它写在componentWillReceiveProps的setState回调里 ⇒ 与 Q3 是同一处依赖。Q6可以只画一根静态条吗可以传value即可组件挂载时componentDidMount会animateWidth()。但注意只有value 0才会开跑value0时内层宽度为 0dump 里看不到它。Q7为什么我在布局 dump 里找不到进度条宽度为 0 的节点不会被列进布局 dump和opacity: 0的节点一样。要用 JS 侧证据实例state、日志判断它是否渲染。小结react-native-progress-bar-animated是单体组件、只依赖prop-types、零平台分支的进度条库几何可以做到像素级核对本轮 7 组预言全部命中设备侧表现正常。最值得记下来的是这几条★★★★★ 一行公式、七组像素级命中。内层宽度 width × value / 100 − borderWidth × 2⇒ 实测900 / 894 / 444 / 669外层、value100、value50、value75与72 / 30 / 39两个外层高度、内层高度全部对上✓✓⇒只要组件是按数值算尺寸几何就是最硬的判据。★★★★componentWillReceiveProps在 React 19 上仍被调用但已废弃。实测state.progress跟随props.value0→50→100→0、onComplete触发 ✓⇒ 我原本担心旧生命周期失效 ⇒ 宽度不变 onComplete永不触发实测推翻了这个担心✓⇒但它承担的是关键链路state.progress只在这里更新⇒升级 React 时必须回归。★★★★Animated未传useNativeDriver⇒ 必有通知。Animated: useNativeDriver was not specified. This is a required option…⇒ 自己封装时要显式写useNativeDriver: false驱动的是width不能用原生驱动✓★★★ 又一处dump 盲区宽度为 0 的节点不出现。value0的两个相位里高 39px 的条一个都找不到 ⇒ 与opacity: 0同类 ✓⇒用尺寸归零/透明度归零来隐藏的组件布局 dump 都是盲区判定渲染要用 JS 侧证据。★★★ 契约反射很便宜defaultProps12 项 原型方法 7 个全用JSON比对。且要注意这个库全用原型方法无箭头类字段⇒ 与方法挂在实例上的库写法相反断言前先看源码是哪种✓★★propTypes的isRequired标记运行时读不到。源码是width: PropTypes.number.isRequired但运行时pt.width.isRequired false13 个键却都在⇒判断必填性请看源码或.d.ts别靠运行时反射✓★ 一条自我纠正断言的数量要从源码数清楚。我一开始把propTypes写成 15 项实际13项 ⇒ 自造的 FAIL ✓★ 判据逐条直写日志PASS|描述|期望|实际分相位驱动value⇒采集端grep即得全证据且相位号打屏让采样时机可事后对齐 ✓本篇用到的库项内容三方库react-native-progress-bar-animated上游1.0.6的鸿蒙适配版适配仓库https://atomgit.com/oh-react-native/react-native-progress-bar-animated适配 TAG1.0.6-ohos-1.0.0需要 HAR / 权限 / ohpm都不需要纯 JS库类型带动画的进度条运行时依赖✅ 只有prop-types ^15.6.0宿主通常已有prepare/postinstall✅都没有许可证ISC上游 GitHub license 字段为空上游仓库https://github.com/rafaelmotta/react-native-progress-bar-animated★52宿主工程RNOH084Demo测试页rnAppKey ProgressBarTestApp# 从适配仓库安装唯一依赖 prop-typesnpminstallgithttps://atomgit.com/oh-react-native/react-native-progress-bar-animated.git#1.0.6-ohos-1.0.0--ignore-scriptsimport AnimatedProgressBar from react-native-progress-bar-animated; AnimatedProgressBar width{300} // ★ 必填无默认值 value{50} // 0..maxValue默认 maxValue100 height{15} // 默认 15 ⇒ 内层高 15-2 13vp 39px borderWidth{1} // 默认 1 ⇒ 参与宽度计算 backgroundColor#148cF0 // 默认值注意源码里 F 是大写 backgroundColorOnComplete#0A7D2C // 走满时把底色渐变过去 barEasinglinear // bounce | cubic | ease | sin | linear | quad barAnimationDuration{500} backgroundAnimationDuration{2500} onComplete{() console.log(走满了)} // 仅在 value 达到 maxValue 时动画后触发 / // 内层宽度(vp) width * value / 100 - borderWidth * 2 // 内层高度(vp) height - borderWidth * 2 // ⚠️ 组件用的是【已废弃的 componentWillReceiveProps】(无 UNSAFE_ 前缀) // 且 state.progress 只在那里更新 ⇒ 升级 React 时重点回归 // ⚠️ 库的 Animated.timing 没写 useNativeDriver ⇒ 会有常驻通知自己封装请写 false // ⚠️ 宽度为 0 的节点不会出现在布局 dump 里和 opacity:0 同类 // ⚠️ propTypes 的 isRequired 标记运行时读不到判断必填请看源码/.d.ts# 换页启动测试页 —— force-stop 不能省hdc shell aa force-stop com.rnoh084.demo hdc shell aa start-bcom.rnoh084.demo-aEntryAbility--psrnAppKey ProgressBarTestApp判据的复现方式# ① 契约 / 几何预言逐条 PASS/FAIL 日志hdc shellhilog -x | grep pbar-test|Select-StringPASS|FAIL|SUMMARY|NOTE|PROBE|EVENT# ⇒ defaultProps 12 项、原型方法 7 个、colSize 类比公式、state.progress 是否跟随# ② 几何内层条高 39px13vp×3按宽度核对devecocli ui layout|Select-String\[(\d),(\d),(\d),(\d)\]# ⇒ value100 → w894value50 → w444value75E3→ w669外层 h45/72/30# ③ LogBoxuseNativeDriver 未指定的通知devecocli ui layout|Select-Stringlogbox_notification_message_text# ④ 注意value0 时内层宽度 0 ⇒ dump 里看不到它验证环境项版本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宿主已有宿主 HAP 产物entry-default-signed.hap本次构建assembleHap3 分 51 秒⇒ 一次成功打包与构建退出码都检查了验证规模几何 7 组像素级预言全部命中契约 12 项 defaultProps 7 个原型方法旧生命周期链路取证state.progress跟随 4 个相位 onComplete触发LogBox 1 条与预言一致4 张截图互异判据 2 条 FAIL 均为我自己断言写错已如实记录⚠️基线说明交付包spec.json声明RN 0.88.0 RNOH 0.84.4两字段自相矛盾本次验证在RN 0.84.1 RNOH 0.84.3上完成。契约与几何结论跨基线成立旧生命周期是否仍被调用这一条带时效性需随 React 大版本复验。相关社区与项目地址项目地址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