HarmonyOS APP《画伴梦工厂》开发第14篇:画与进度反馈——Progress 与帧动画
2026/7/1 17:55:36 网站建设 项目流程

第2.6篇:动画与进度反馈——Progress 与帧动画

难度:⭐⭐ 进阶 |前置知识:1.3 @State 与状态管理 |涉及源文件products/default/src/main/ets/pages/RecognitionWaitingPage.ets


一、引言

当用户完成了涂鸦或照片采集,点击"生成动画"后,应用进入RecognitionWaitingPage。在这个页面上,AI 正在后台处理图像、识别内容、生成视频帧。整个过程可能持续数秒到数十秒。

对儿童用户而言,一个静止的"加载中…"页面是不可接受的。没有视觉反馈,小朋友会认为应用卡住了,进而反复点击或关闭应用。

"画伴梦工厂"的等待页面通过三层进度反馈机制来解决这个问题:

  1. Progress 进度条—— 展示整体进度百分比
  2. 帧动画气泡—— 动态变化的 Circle 组件产生"跳动"感
  3. 步骤条—— 分阶段展示任务进度,让用户知道"现在在做什么"

本文将深入分析这三层机制的设计与实现。


二、状态设计

@Stateprivateprogress:number=12;@StateprivateactiveStep:number=0;@StateprivatestatusText:string='正在准备生成任务';@Stateprivatefailed:boolean=false;@Stateprivatecompleted:boolean=false;@StateprivateanimationFrame:number=0;@StateprivatewaitingTip:string=WAITING_TIPS[0];
状态类型初始值作用
progressnumber12进度百分比 0-100
activeStepnumber0当前激活的步骤索引(0-3)
statusTextstring‘正在准备生成任务’主要状态文本
failedbooleanfalse是否生成失败
completedbooleanfalse是否生成完成
animationFramenumber0帧动画的当前帧(0-3)
waitingTipstringWAITING_TIPS[0]底部提示文案

这些状态通过@State 装饰器驱动 UI 自动刷新。ArkUI 的响应式系统会检测状态变化,只更新受影响的 UI 部分,保证了动画性能。


三、Progress 组件详解

3.1 基础用法

Progress({value:this.progress,total:100,type:ProgressType.Linear}).width('84%').height(8).color(this.mint)// 前景色 #42CDA3.backgroundColor('#ECECF6')// 背景色.borderRadius(6).margin({top:12})

参数说明

参数说明
value当前进度值(需在 0 到 total 之间)
total总进度值
type进度条类型:ProgressType.Linear(线性)或ProgressType.Circle(圆形)

Progress 类型一览

类型说明适用场景
Linear水平线性进度条通用进度展示
Circle圆形进度环需要节省空间或强调百分比的场景

3.2 进度驱动逻辑

进度由setInterval定时器驱动,模拟 AI 生成进度:

privatestartWaitingTimer(){if(this.timerId>=0){clearInterval(this.timerId);}this.timerId=setInterval(()=>{if(this.failed||this.completed){clearInterval(this.timerId);this.timerId=-1;return;}this.animationFrame=(this.animationFrame+1)%4;this.waitingTip=WAITING_TIPS[this.animationFrame%WAITING_TIPS.length];if(this.progress<92){this.progress=Math.min(92,this.progress+3);}else{this.progress=Math.min(97,this.progress+1);}this.activeStep=Math.min(3,Math.floor(this.progress/28));},1200);}

进度条设计考量

  1. 起始值 12:不让用户看到"从 0 开始"的假象,让等待显得已经进行了一段时间。
  2. 分段速度progress < 92时每次 +3,>= 92时每次 +1。前端进度"慢下来"营造接近完成的真实感。
  3. 上限 97:预留最后的 3% 给真正的完成回调,不会出现"99% 卡住"的尴尬。
  4. 1200ms 间隔:约 1.2 秒更新一次,节奏舒缓不急促。

3.3 完成与失败的处理

当 AI 生成完成或失败时:

// 成功this.progress=100;this.completed=true;this.statusText='视频已生成并保存到作品';// 失败this.failed=true;this.statusText='生成失败:'+getErrorMessage(error);

定时器会检测failedcompleted状态,及时clearInterval停止更新。


四、帧动画气泡:LoadingBubbles

4.1 实现代码

@BuilderprivateLoadingBubbles(){Row(){Circle().width(this.animationFrame===0?24:16).height(this.animationFrame===0?24:16).fill(this.sunshine)// #FFB84D 黄色.opacity(this.animationFrame===0?1:0.55)Circle().width(this.animationFrame===1?24:16).height(this.animationFrame===1?24:16).fill(this.mint)// #42CDA3 绿色.opacity(this.animationFrame===1?1:0.55).margin({left:12})Circle().width(this.animationFrame===2?24:16).height(this.animationFrame===2?24:16).fill(this.brandPurple)// #7657F3 紫色.opacity(this.animationFrame===2?1:0.55).margin({left:12})}.height(34).alignItems(VerticalAlign.Center).justifyContent(FlexAlign.Center).margin({top:12})}

4.2 动画原理

这是一个无动画 API 的帧动画方案,通过定时器切换animationFrame状态,驱动 Circle 组件的大小和透明度变化:

animationFrame = 0 → 圆圈1 放大(24) + 高不透明度(1),圆圈2、3 缩小(16) + 低不透明度(0.55) animationFrame = 1 → 圆圈2 放大 + 高不透明度,圆圈1、3 缩小 + 低不透明度 animationFrame = 2 → 圆圈3 放大 + 高不透明度,圆圈1、2 缩小 + 低不透明度 animationFrame = 3 → 全部缩小 + 低不透明度(短暂"休息"帧)

为什么不用 animateTo 或 animator

  • 简单可靠:ArkTS 的@State+setInterval方案没有复杂的学习曲线,逻辑直观。
  • 完全可控:每一帧的状态都可以精确控制,不像 Property Animation 存在中间插值。
  • 性能友好:只变更尺寸和透明度,不涉及布局重排,ArkUI 可以高效地执行属性更新。

4.3 与外围动画的配合

除了气泡,还有背景圆环动画:

Circle().width(126+this.animationFrame*6).height(126+this.animationFrame*6).fill('#EAF8F0').opacity(0.72)

中央百分比数字下方的圆环会随animationFrame递增而周期性扩大(每次 +6),产生"呼吸"效果,与气泡形成视觉层次感。


五、步骤条:StepRow

5.1 实现代码

constWAITING_STEPS:string[]=['看看画里有什么','想一想怎么动','画出动画片段','保存到我的作品'];@BuilderprivateStepRow(step:string,index:number){Row(){Text((index+1).toString()).fontSize(12).fontWeight(FontWeight.Bold).fontColor(this.activeStep>=index?'#FFFFFF':'#8A8FA4').width(30).height(30).backgroundColor(this.activeStep>=index?this.mint:'#ECECF6').borderRadius(15)Text(step).fontSize(13).fontColor(this.activeStep>=index?this.ink:'#8A8FA4').layoutWeight(1).margin({left:12})Text(this.activeStep>index?'完成':(this.activeStep===index?'进行中':'等待')).fontSize(11).fontColor(this.activeStep>=index?this.mint:'#9AA0B5')}.width('100%').padding(12).backgroundColor('#FFFFFF').borderRadius(14).margin({top:10})}

5.2 三种状态设计

步骤条的每个步骤有三种视觉状态,由activeStepindex的比较决定:

条件状态圆形编号步骤文字右侧标签
activeStep > index已完成绿色底 + 白字深色“完成” + 绿色文字
activeStep === index进行中绿色底 + 白字深色“进行中” + 绿色文字
activeStep < index等待中灰色底 + 灰字灰色“等待” + 灰色文字

5.3 进度与步骤的映射

this.activeStep=Math.min(3,Math.floor(this.progress/28));

每个步骤覆盖约 28% 的进度区间:

进度范围activeStep步骤
0 - 270看看画里有什么
28 - 551想一想怎么动
56 - 832画出动画片段
84 - 1003保存到我的作品

这样用户的进度感知从"一个模糊的百分比"变成了"四个清晰的阶段",体验更加透明。


六、页面中央大进度指示

Stack(){Circle().width(168).height(168).fill('#FFF0D6')Circle().width(126+this.animationFrame*6).height(126+this.animationFrame*6).fill('#EAF8F0').opacity(0.72)Column(){Text(this.progress.toString()+'%').fontSize(32).fontWeight(FontWeight.Bold).fontColor(this.brandPurple)Text(this.completed?'完成啦':'制作中').fontSize(13).fontWeight(FontWeight.Bold).fontColor(this.mint).margin({top:4})}.alignItems(HorizontalAlign.Center)}

视觉层次(从底到顶):

  1. 底层圆#FFF0D6暖黄):固定大小 168×168,作为衬底
  2. 中层圆#EAF8F0浅绿):随animationFrame周期性缩放(126-144),半透明,产生呼吸感
  3. 顶层文字:大号百分比数字 + 状态文字

七、生命周期管理

aboutToAppear(){// ... 获取 Router 参数 ...this.startWaitingTimer();this.startGeneration();}aboutToDisappear(){if(this.timerId>=0){clearInterval(this.timerId);this.timerId=-1;}}

为什么必须清理定时器

如果不清理,会出现以下问题:

  1. 内存泄漏:定时器的回调持有组件引用,阻止 GC 回收。
  2. 状态泄漏:离开页面后定时器仍在运行,试图更新已销毁组件的 @State,导致 ArkUI 警告或异常。
  3. 重复定时器:用户反复进入该页面会创建多个定时器实例,进度更新速度翻倍。

最佳实践

  • aboutToAppear中创建定时器
  • aboutToDisappear中清理定时器
  • 使用timerId追踪定时器状态,创建前先清理旧实例

八、底部按钮的状态联动

Button(this.failed?'重试生成':(this.completed?'查看视频结果':'正在做动画...')).width('90%').height(46).fontSize(15).fontWeight(FontWeight.Bold).fontColor('#FFFFFF').backgroundColor((this.completed||this.failed)?this.brandPurple:'#A9A0D8').borderRadius(23).margin({top:20}).onClick(()=>{if(this.failed){// 重置所有状态,重新生成this.progress=12;this.activeStep=0;this.animationFrame=0;this.waitingTip=WAITING_TIPS[0];this.startWaitingTimer();this.startGeneration();}elseif(this.completed){// 跳转到结果页this.getUIContext().getRouter().pushUrl({url:'pages/RecognitionResultPage',params:{...}});}})

按钮的文本颜色均与状态联动:

状态按钮文字按钮颜色点击行为
进行中“正在做动画…”浅紫#A9A0D8不可点击
完成“查看视频结果”品牌紫#7657F3跳转结果页
失败“重试生成”品牌紫#7657F3重置并重新生成

这种设计让用户在任何状态下都知道"现在该做什么"。


九、状态与 UI 联动全景

┌─────────────────┐ │ setInterval │ │ 每 1200ms 触发 │ └────────┬────────┘ │ ┌──────────────┼──────────────┐ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │progress+3│ │frame+1%4 │ │activeStep│ │ (或+1) │ │ │ │ =p/28 │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │ │ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Progress │ │Bubbles │ │ StepRow │ │ 百分比显示│ │大小/透明度│ │三种状态 │ │ Linear │ │三色圆圈 │ │完成/进行/│ │ │ │呼吸动画 │ │等待 │ └──────────┘ └──────────┘ └──────────┘

三层进度反馈同时更新,但各自关注不同的状态变量,互不干扰。这体现了 ArkUI 响应式编程的优雅之处。


十、总结

本文深入分析了 RecognitionWaitingPage 中三层进度反馈机制的实现:

反馈层组件驱动方式视觉效果
进度条Progressprogress状态线性进度百分比
帧动画Circle× 3animationFrame状态气泡跳动 + 圆环呼吸
步骤条自定义@BuilderactiveStep状态完成/进行中/等待三态

设计原则

  1. 让等待有信息量:用户不仅知道"要等多久",还知道"等的是什么"。
  2. 动画给予安全感:动态的 UI 让用户确信应用仍在工作。
  3. 状态透明可预期:步骤条和百分比让进度可量化,减少焦虑。
  4. 生命周期安全:及时清理定时器,防止内存泄漏和状态异常。

对于儿童应用而言,这层等待体验的设计甚至比 AI 生成本身更重要——它直接决定了用户会不会在过程中放弃。


动手挑战:尝试在LoadingBubbles中加入第四种颜色圆圈,或将气泡动画改为沿弧形轨迹运动。也可以尝试使用animateTo隐式动画替代帧切换,体验两种方案的差异。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询