news 2026/9/18 15:18:00

茶器艺科智造HarmonyOS应用实战-47-onPageEnd就把ArkWeb标成ready,Three.js异常谁来接住:建立loading、ready、error状态机

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
茶器艺科智造HarmonyOS应用实战-47-onPageEnd就把ArkWeb标成ready,Three.js异常谁来接住:建立loading、ready、error状态机

茶器艺科智造HarmonyOS应用实战-47-onPageEnd就把ArkWeb标成ready,Three.js异常谁来接住:建立loading、ready、error状态机

onPageEnd到达时,ArkWeb 主页面完成了一次加载;它并不等于茶杯三维引擎已经创建 Scene、WebGLRenderer、OrbitControls、Mesh,并且暴露了window.__cupSync。当前CupWeb3d.ets:489-492却在这个回调里直接执行webReady = true,随后推参数、补 STL、刷贴图、重置视角。原生侧的 ready 实际表达的是页面事件,而业务需要的是 Three.js 能力握手。

rawfilecup3d/index.html的顶层 IIFE 先访问THREE、创建渲染器和控制器,直到文件末尾才定义window.__cupSync。如果脚本缺失、WebGLRenderer 创建失败或前置初始化抛异常,后续函数可能根本没有注册;原生脚本又使用window.__cupSync && ...,函数不存在时只是短路,不会进入 catch。于是可能出现“原生认为 ready、画面空白、参数调用看似无错”的失败组合。

本文把网页加载、引擎握手、失败和重试建成显式状态机。内容来自静态源码与官方 API 语义,建议代码未写入只读项目,也未运行 ArkWeb、Three.js 或真机恢复流程。

一、当前webReady只有false和true,来源过于宽泛

组件只声明一个布尔值:

// 当前源码摘录 @State private webReady: boolean = false; .onPageEnd(() => { this.webReady = true; void this.onWebPageReadySequence(); })

参数、STL、贴图与视角重置都以它为门禁。onWebPageReadySequence的顺序设计得很清楚,但前提没有被证明:它只知道主页面回调到了,不知道__cupSync__cupEmitStlToNative、贴图函数和 Three 渲染循环是否可用。

当前 Web 组件没有附加onPageBeginonErrorReceiveonRenderExited状态处理;JavaScriptProxy 的 methodList 只有stlBeginstlAppendstlFinishcupTextureReport,没有引擎 ready/error 方法。源文件里也没有原生可观察的启动握手。

二、Three初始化异常可能发生在__cupSync定义之前

HTML 先加载本地three.min.jsOrbitControls.js,随后立即运行 IIFE。前部代码直接使用全局变量:

// 当前初始化次序摘要(function(){varcontainer=document.getElementById('c');varscene=newTHREE.Scene();varcamera=newTHREE.PerspectiveCamera(45,1,0.1,2000);varrenderer=newTHREE.WebGLRenderer({antialias:true,alpha:true});varcontrols=newTHREE.OrbitControls(camera,renderer.domElement);// 中间定义轮廓、贴图、STL、动画等大量逻辑tick();window.__cupSync=function(p){/* ... */};})();

只要THREE未定义、container 异常、WebGL 上下文不可用或某个早期语句抛错,执行会在函数发布前中断。源码没有包住整个 bootstrap 的顶层 try/catch,也没有window.onerrorunhandledrejection向 ArkTS 上报。__cupSync自己的 catch 只写console.error('[cup3d]', e),原生没有据此切换状态。

三、onPageEnd应只推进加载阶段,不直接宣布ready

华为官方 ArkWeb 说明中,onPageEnd是主 frame 的页面加载完成回调;官方 FAQ 还说明收到它不能保证下一帧已经反映 DOM 状态。对本地 HTML 来说,它适合发起能力探测,不适合作为 Three 业务能力成功的替代证据。

建议状态至少包含四种:

// 建议代码 export enum CupEngineState { IDLE = 'IDLE', LOADING = 'LOADING', READY = 'READY', ERROR = 'ERROR' } export interface CupEngineFailure { readonly stage: 'RESOURCE' | 'BOOT' | 'HANDSHAKE' | 'RENDER' | 'PROCESS'; readonly code: string; readonly detail: string; readonly retryable: boolean; }

状态转换应固定为:IDLE -> LOADING(开始加载),LOADING -> READY(收到匹配本轮 token 的 JS 握手),LOADING/READY -> ERROR(资源、引擎或进程错误),ERROR -> LOADING(用户重试)。onPageEnd仍保持 LOADING,只发握手请求并启动超时。

四、用带loadToken的握手证明函数已经发布

仅让 JS 页面启动时主动报 ready 仍可能遇到旧页面回调。建议每次加载生成loadTokenonPageEnd调用已经初始化完成后才会存在的window.__cupHandshake(token),Web 再把同一 token 回传。旧 token 一律忽略。

// 建议代码:放在成功完成 Three 初始化并发布业务函数之后window.__cupHandshake=function(loadToken){varhost=typeofCupNative!=='undefined'?CupNative:window.CupNative;if(!host||!host.cupEngineReady){return'NO_NATIVE_BRIDGE';}host.cupEngineReady(String(loadToken||''),'three-r128',JSON.stringify({sync:!!window.__cupSync,stl:!!window.__cupEmitStlToNative}));return'READY_REPORTED';};

握手应位于__cupSync、STL 和必要贴图入口定义之后。capabilities 让原生按能力放行,不要仅靠版本字符串猜函数存在。token 不需要是秘密,只用于区分加载代次。

五、JavaScriptProxy扩展ready与error回调

现有CupNativeStlBridge已证明 Web 可以回调 ArkTS。建议将引擎事件拆成小桥,或在现有对象中增加两个经过长度限制的方法。回调只传稳定 stage/code 与截断详情,不把完整堆栈直接展示给用户。

// 建议代码:桥接层不直接操作 WebviewController class CupEngineBridge { private onReady: (token: string, version: string, caps: string) => void = (): void => {}; private onError: (token: string, stage: string, code: string, detail: string) => void = (): void => {}; attach( ready: (token: string, version: string, caps: string) => void, error: (token: string, stage: string, code: string, detail: string) => void ): void { this.onReady = ready; this.onError = error; } cupEngineReady = (token: string, version: string, caps: string): void => { this.onReady(token, version, caps); }; cupEngineError = ( token: string, stage: string, code: string, detail: string ): void => { this.onError(token, stage, code, detail.substring(0, 240)); }; }

注册时把cupEngineReadycupEngineError加入 JavaScriptProxy methodList。若另建CupEngine代理对象,要用当前 SDK 实际支持的注册方式;本文示例不声称已经编译。

六、Native侧把页面事件、握手和超时接到状态机

开始加载时清理旧超时与错误,生成新 token;onPageEnd 只注入握手脚本。超时阈值应通过低端设备启动数据确定,下面的 5000ms 只是示例配置。

// 建议代码:省略安全随机token实现细节 @State private engineState: CupEngineState = CupEngineState.IDLE; private loadToken: string = ''; private handshakeTimer: number = -1; private onWebLoadBegin(): void { this.cancelHandshakeTimer(); this.loadToken = this.nextLoadToken(); this.engineState = CupEngineState.LOADING; this.engineFailure = undefined; } private onWebPageEnd(): void { const tokenJson = JSON.stringify(this.loadToken); void this.webController.runJavaScript( `window.__cupHandshake && window.__cupHandshake(${tokenJson});` ); this.armHandshakeTimeout(5000); } private acceptEngineReady(token: string, capsJson: string): void { if (token !== this.loadToken || !this.hasRequiredCapabilities(capsJson)) { return; } this.cancelHandshakeTimer(); this.engineState = CupEngineState.READY; this.scheduleParamsToWeb(); this.flushDeferredActions(); }

若脚本函数不存在,runJavaScript本身可能正常完成,但不会收到 ready;超时负责把这种静默短路变成 HANDSHAKE_ERROR。参数调度应沿用第 46 篇的 pending latest snapshot,而不是在超时前丢弃用户最新修改。

七、Web端顶层异常与运行期异常都要回传

应在危险初始化之前安装最小错误上报函数,并用顶层 try/catch 包住 bootstrap。不能等 Three 初始化完成后才定义错误桥,因为最需要接住的是启动早期异常。

// 建议代码:错误码需固定枚举,detail只用于诊断(functionbootCupEngine(){functionreportFatal(stage,code,error){try{varhost=typeofCupNative!=='undefined'?CupNative:window.CupNative;vardetail=String(error&&error.message?error.message:error||'');if(host&&host.cupEngineError){host.cupEngineError(window.__cupLoadToken||'',stage,code,detail.slice(0,240));}}catch(_){}}try{if(!window.THREE)thrownewError('THREE_UNAVAILABLE');initializeSceneRendererControls();publishCupCommands();}catch(error){reportFatal('BOOT','ENGINE_BOOT_FAILED',error);}window.addEventListener('unhandledrejection',function(event){reportFatal('RENDER','UNHANDLED_REJECTION',event.reason);});})();

上例中的 token 需要由握手请求先写入,或让 error 回调不依赖 token、由当前 LOADING 代次接收。两种协议只能选一种并写清顺序。WebGL context lost、同步重建失败和动画循环异常也应调用相同上报入口;普通 console 日志可保留,但不能作为唯一状态通道。

八、所有命令只在READY消费,失败时保留可恢复意图

参数、贴图、STL、重置视角的门禁应读取engineState === READY。LOADING 时保留最新参数和未消费 nonce;ERROR 时停止自动执行,让用户看到错误状态并决定重试。只有真正提交命令后才更新last...Handled,否则重试后无法补发。

// 建议代码 private canRunCupCommand(): boolean { return this.engineState === CupEngineState.READY; } private flushDeferredActions(): void { if (!this.canRunCupCommand()) { return; } this.scheduleParamsToWeb(); this.kickCupTextureFlushIfNeeded(); this.tryFlushCupViewReset(); this.tryFlushStlExport(); } private retryEngine(): void { if (this.engineState !== CupEngineState.ERROR) { return; } this.onWebLoadBegin(); this.webController.loadUrl($rawfile('cup3d/index.html')); }

loadUrl对 rawfile Resource 的具体重载形式要按当前 SDK 签名核对;也可以通过组件 key 重建 Web。重试必须有次数或用户动作门禁,避免渲染进程持续崩溃时形成自动循环。

九、验证矩阵覆盖加载、启动、运行和恢复

场景注入条件期望状态延迟动作
正常启动函数全部发布LOADING→READY只补发最新参数
three脚本不可用THREE缺失LOADING→ERROR不发 STL/贴图
renderer构造抛错可控 fakeBOOT ERROR显示重试入口
握手函数缺失onPageEnd 到达超时后 ERRORpending 保留
旧 token 回调重载后旧消息状态不变不消费新 pending
资源加载错误onErrorReceiveRESOURCE ERROR记录稳定错误码
渲染进程退出onRenderExitedPROCESS ERROR需要显式 reload
sync重建异常非法 profileRENDER ERROR不伪装参数成功
加载中改参数连续拖动仍为 LOADINGREADY 后发最终快照
ERROR后重试用户点击ERROR→LOADING→READYnonce 不重复消费

单元测试可用 fake bridge 和 fake timer断言转换;HTML 可在测试页面中让initializeSceneRendererControls主动抛错;最终还需真实 ArkWeb 与 WebGL 设备测试。仅看到 onPageEnd 日志不能作为 READY 证据。

十、故障排查、官方锚点与结论范围

一直停在 LOADING 时,先运行一个只返回typeof window.__cupHandshake的脚本,区分函数未发布、JavaScriptProxy 不可见和 token 不匹配;不要直接把超时延长。进入 READY 仍空白时,检查 capability 是否只验证了函数存在却没验证首帧,再加入 renderer、scene、cupMesh 的最小状态回报。

立即进入 ERROR 时,按 stage 分路:RESOURCE 看本地脚本路径和 onErrorReceive;BOOT 看 THREE、OrbitControls 与 WebGLRenderer;PROCESS 看 onRenderExited;RENDER 看最后 revision 与杯型参数。重试后收到旧回调,则核对 loadToken 生命周期和桥对象是否残留上轮 listener。

本次源码确认:onPageEnd直接置webReady=true;业务函数发布晚于多步 Three 初始化;参数脚本对函数不存在采用短路;现有 proxy 没有引擎状态方法。官方资料确认 onPageEnd 是主 frame 加载完成回调,资源错误与渲染进程退出另有事件。本文没有复现实际 Three 异常,也没有证明建议 API 在项目目标 SDK 编译通过,状态机、握手、超时和 reload 都属于待实现方案。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 15:16:51

实验报告格式PDF化:LaTeX与Markdown双流水线实践指南

简介:武汉理工大学实验报告格式PDF,面向该校理工科专业正在修读实验课程的学生及指导教师,用于统一实验预习、实验过程记录、结果分析三大部分的报告书写与成绩评定。文件包共1个pdf,约159KB,轻量便携,可打…

作者头像 李华
网站建设 2026/9/18 15:14:26

llama.cpp 跑通7B大模型:GGUF量化与llama-server

简介:围绕 llama.cpp 本地大模型推理整理的这份 PDF 文档,面向希望在消费级硬件上部署开源 LLM 的开发者、运维人员及技术选型者,重点回应云端算力依赖、数据外泄顾虑与推理成本偏高等问题,对医疗、金融等数据敏感场景尤具参考价值…

作者头像 李华
网站建设 2026/9/18 15:10:14

【ComfyUI】FluxRedux OOTD蒙版基础换装

今天展示的内容是一套基于 FluxRedux 基础换装工作流 的 ComfyUI 实例。这套流程通过图像输入、遮罩处理、模型条件控制与采样解码的组合,使得人物在原有姿态和场景保持不变的情况下快速实现服装替换。 工作流的设计强调灵活性,既支持半自动的高效换装,也兼顾全手动的精细化…

作者头像 李华
网站建设 2026/9/18 15:09:42

MySQL导出数据实战:SELECT INTO OUTFILE参数详解与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华