当前位置: 首页 > news >正文

HarmonyOS 6.1 开发者盛宴|《灵犀厨房》实战(二十三):【交互动效】转场、列表动画与趣味反馈——让每一次点击都有温度

HarmonyOS 6.1 开发者盛宴|《灵犀厨房》实战(二十三):【交互动效】转场、列表动画与趣味反馈——让每一次点击都有温度

摘要:前面 22 篇我们完成了《灵犀厨房》的核心功能——推荐、菜谱、流转、语音、视频。但功能齐全 ≠ 体验优秀。用户在第一秒就会用「眼睛」和「手指」投票:页面切换卡不卡?列表加载生不生硬?按钮有没有反馈?本篇将用 HarmonyOS 6.1.0 的 **属性动画(.animation)**和变换(.scale / .translate / .opacity),为《灵犀厨房》注入「灵气」——让页面有呼吸感、让列表有节奏感、让每一次点击都有温度。我们不增加任何新功能,只改动约 80 行动效代码,让 App 从“能用”跃升到“好用”。


一、引言:功能齐全 ≠ 体验优秀

一个有趣的实验:把《灵犀厨房》第 22 篇的版本拿给一个没用过的人,让他从首页点进菜谱详情。他会顺利完成任务——但不会觉得“好用”。他会觉得页面“突然就出现了”,列表“一下子就全出来了”,按钮“点了好像没点”。

这不是 Bug,但比 Bug 更致命——用户不会抱怨,他们只会不再打开

动效不是“炫技”。好的动效像空气——你感觉不到它的存在,但一旦缺失,一切都变得生硬。本篇要做的,就是用最少的代码,给《灵犀厨房》注入这种“空气感”。

设计目标

核心技法

动效设计的三个维度

转场动画
Hero 卡片上滑入场

列表动画
购物清单错落入场

趣味反馈
收藏按钮爆发动画

.opacity() + .translate()
透明度 + 位移

.animation({ delay })
错落延迟

.scale() + onTouch
缩放 + 触摸事件

让页面有呼吸感

让列表有节奏感

让点击有温度

图一解读:三个维度对应三个核心技法,每个技法服务一个明确的设计目标。转场动画用透明度+位移让元素“滑”进来而非“闪现”;列表动画用延迟制造节奏感,像“翻开书页”一样逐项展开;趣味反馈用缩放模拟物理按压,让屏幕上的按钮有了“弹性”。三者互不依赖,可以单独应用,也可以组合使用。


二、核心原理:ArkUI 属性动画的“三段式”模型

在进入具体实现之前,先理解 HarmonyOS 属性动画的工作原理:

初始状态 ──→ 触发条件(状态变量变化) ──→ 目标状态 │ └── .animation({ duration, curve, delay }) │ └── ArkUI 自动插值计算中间帧

你只需要做两件事:

  1. 声明初始状态和目标状态(如opacity: 0 → 1
  2. 告诉 ArkUI 变化发生时用什么曲线、多久完成.animation({...})

中间的所有插值计算、帧渲染、硬件加速——ArkUI 全部自动完成。这就是声明式动画的核心优势:你描述“从哪到哪”,框架负责“怎么过去”

2.1 动画曲线的选择

曲线视觉效果适用场景本篇使用
Curve.EaseOut先快后慢,像“轻轻落下”入场动画、弹回动画
Curve.EaseInOut两端慢中间快,像“呼吸”循环动画
Curve.Linear匀速进度条❌(太机械)

设计决策:为什么本篇几乎全部使用Curve.EaseOut?因为自然界中没有“匀速运动”——物体总是先快后慢地停下。EaseOut模拟了这种物理惯性,让动画看起来“自然”而非“机械”。

2.2 动画触发的“黄金窗口”

所有入场动画都有一个微小的setTimeout延迟(80~150ms):

setTimeout(()=>{this.heroReady=true;},150);

这不是为了“炫技”,而是为了避开首次布局的帧丢失。组件首次渲染时,ArkUI 需要完成布局计算、绘制、合成——如果在这期间触发动画,可能被系统跳帧,导致动画“凭空出现”而非“滑入”。150ms 的延迟确保布局完成后再启动动画,用户看到的是完整的入场过程。


三、实战一:转场动画——Hero 卡片上滑入场

首页的 AI 食材识别卡片是用户打开 App 第一眼看到的焦点区域。让它“滑”进来,比直接出现更有吸引力。

3.1 实现

@LocalheroReady:boolean=false;aboutToAppear():void{setTimeout(()=>{this.heroReady=true;},150);}buildHeroCard(){Column(){// ...卡片内容...}.opacity(this.heroReady?1:0).translate({y:this.heroReady?0:40}).animation({duration:500,curve:Curve.EaseOut})}

3.2 设计决策:为什么是 40vp 和 500ms?

参数选择如果更大如果更小
位移 40vp屏幕高度约 5%动画太“跳”,用户注意力被分散几乎感觉不到滑入
时长 500ms刚好感知,不等待用户觉得 App 慢动画一闪而过

这是一个平衡点:足够让用户感知到“滑入”,但不至于让他们等待Curve.EaseOut的减速收尾让卡片像“轻轻落下”而非“匀速滑入”,这是物理直觉在 UI 中的投射。


四、实战二:列表动画——购物清单错落入场

购物清单是分组列表。一次性全部显示会显得“生硬”;每一行错落 60ms 依次出现,则像“被轻轻翻开”。

4.1 核心实现

@LocallistReady:boolean=false;aboutToAppear():void{setTimeout(()=>{this.listReady=true;},80);}ListItem(){Row({space:10}){Circle().width(8).height(8).fill(categoryColor)Text(item).fontSize(15)}}.opacity(this.listReady?1:0).translate({x:this.listReady?0:-20}).animation({duration:350,curve:Curve.EaseOut,delay:80+itemIndex*60// ← 关键:延迟递增})

4.2 品类分组的双层延迟

品类(ListItemGroup)也需要自己的入场节奏:

.opacity(this.listReady?1:0).animation({duration:400,curve:Curve.EaseOut,delay:100+categoryIndex*120// 品类间延迟 120ms})

整体效果:

品类「肉类」 ← 延迟 100ms 出现 牛腩 500g ← 延迟 180ms 出现 排骨 500g ← 延迟 240ms 出现 品类「蔬菜」 ← 延迟 220ms 出现 番茄 3个 ← 延迟 300ms 出现

4.3 趣味点缀:呼吸圆点

每个食材项末尾加了一个微小的彩色圆点,用PlayMode.Alternate做无限呼吸动画:

Circle().width(5).height(5).fill(categoryColor).opacity(0.35).animation({duration:2000+itemIndex*300,// 每项错开周期,避免同步闪烁curve:Curve.EaseInOut,iterations:-1,// 无限循环playMode:PlayMode.Alternate// 来回播放})

设计考量:为什么每个圆点要错开 300ms 起始周期?如果所有圆点同时明灭,会产生“集体闪烁”的视觉疲劳。错开周期后,它们像各自在“呼吸”,整体视觉效果柔和且不死板。


五、实战三:趣味反馈——按压缩放与爆发动画

5.1 RecommendCard 按压缩放

用户手指按下卡片时,给出一个微小的“陷下去”的感觉——这是触觉反馈的视觉替代。

@StateisPressed:boolean=false;build(){Column(){/* ... */}.scale({x:this.isPressed?0.97:1,y:this.isPressed?0.97:1}).animation({duration:150,curve:Curve.EaseOut}).onTouch((event:TouchEvent)=>{if(event.type===TouchType.Down){this.isPressed=true;}elseif(event.type===TouchType.Up||event.type===TouchType.Cancel){this.isPressed=false;}})}

为什么用onTouch而非onClick

事件触发时机能否感知 Down
onClick手指抬起时
onTouchDown / Up / Cancel 均可

按压反馈的关键是“按下去的瞬间”——TouchType.DownonClick只能感知抬起,无法实现这个效果。

5.2 收藏按钮爆发动画

@LocalheartScale:number=1;@LocalheartLiked:boolean=false;Button(){if(this.heartLiked){SymbolGlyph($r('sys.symbol.heart_fill')).fontSize(16).fontColor(['#FF2D55'])}else{SymbolGlyph($r('sys.symbol.heart')).fontSize(16).fontColor([Color.White])}}.scale({x:this.heartScale,y:this.heartScale}).animation({duration:300,curve:Curve.EaseOut}).onClick(()=>{this.heartLiked=!this.heartLiked;this.heartScale=1.3;// 瞬间放大到 130%setTimeout(()=>{this.heartScale=1;},150);// 150ms 后弹回})

动画时序

点击瞬间: scale 1→1.3, liked false→true 150ms 后: scale 1.3→1(弹回) 300ms 后: 动画完成

整个爆发动画在 300ms 内完成——这是用户感知“反馈”的最佳窗口。再短则无感,再长则拖沓。

5.3 RecommendCard 错落入场

每张卡片按索引延迟触发入场,在双列瀑布流中自然形成“Z 字形”视觉流:

@PropitemIndex:number=0;@StatecardReady:boolean=false;aboutToAppear():void{setTimeout(()=>{this.cardReady=true;},80+this.itemIndex*70);}.opacity(this.cardReady?1:0).translate({y:this.cardReady?0:30}).animation({duration:400,curve:Curve.EaseOut,delay:this.itemIndex*70})

六、视频进度 → 步骤同步(续上篇)

在上篇 AVPlayer 集成的基础上,新增syncStepWithVideoProgress()方法,使视频播放进度与下半部步骤展示保持同步:

privatesyncStepWithVideoProgress():void{if(this.videoDuration<=0||this.recipe.steps.length<=1)return;constprogress:number=this.videoCurrentTime/this.videoDuration;constnewStepIndex:number=Math.min(Math.floor(progress*this.recipe.steps.length),this.recipe.steps.length-1);if(newStepIndex!==this.currentStepIndex){this.currentStepIndex=newStepIndex;}}

生产环境中,可由后端返回每步的精确时间戳,替换均分逻辑。


七、代码增删改清单

文件新增/修改动效变更
pages/Index.ets修改Hero 卡片上滑入场
components/RecommendCard.ets重大修改新增isPressed按压缩放 97%、错落入场上滑、接受itemIndex属性
pages/ShoppingListPage.ets重构错落入场(双层延迟)、呼吸圆点、品类逐级出现
pages/RecipeDetailPage.ets修改收藏按钮爆发动画、视频进度→步骤同步

八、设计决策

决策选择理由
入场动画触发时机setTimeout(80~150ms)等待首次布局完成,避免动画在帧丢失中被吞掉
缩放比例97%(按下)/ 130%(爆发)97% 微妙但可感知;130% 明显但不溢出
列表错落延迟60ms/项10 项列表 ≈ 600ms 完成,用户不等待
呼吸动画PlayMode.Alternate+ 错开周期避免所有圆点同步闪烁,减少视觉疲劳
按压反馈 APIonTouch而非onClickonClick只有 Up 事件,无法感知 Down 的按压瞬间
动画曲线Curve.EaseOut先快后慢的减速曲线模拟物理惯性,比线性更自然

九、本阶段总结与下篇预告

本篇是《灵犀厨房》系列中“投入最少、感知最强”的一篇。我们没有新增任何功能,但用约 80 行动效代码,让整个 App 从“能用”跃升到“好用”:

  • 转场动画:Hero 卡片和推荐瀑布流有了“呼吸感”——不是凭空出现,而是“滑”进来的
  • 列表动画:购物清单从生硬的静态列表变成了有节奏的“翻开”效果
  • 趣味反馈:收藏按钮的“啵”一下、卡片的“陷进去”,让每次点击都有了温度

好的动效不是“炫技”,而是让用户感觉不到技术在运作——一切如此自然,就像 App 本来就该这样。

下篇预告:第 24 篇《手势操作:滑动调整“火力大小”》。我们要让用户在智慧厨电模拟器上,像拧燃气灶旋钮一样,用手指滑动来调节火力。这是动效与交互的结合——手势驱动的动画,比定时器驱动的动画更贴近直觉。


📚 本系列持续更新中:下一篇《手势操作-滑动调整火力大小》将为 App 注入体验控制快感,让交互如丝般顺滑。

🔗专栏入口:[《HarmonyOS6.1全场景实战》合集]

📦 获取基线版本源码包:包括第1-15篇所有代码 + 架构文档 + Flask 后端

如果你觉得这篇文章对您有所帮助,麻烦您动动发财之手点赞 👍、收藏 ⭐ 和评论 💬。谢谢大家!!

纯血鸿蒙,用心造厨。我们下一篇见!

http://www.cnnetsun.cn/news/2723019.html

相关文章:

  • 告别官方数据集:手把手教你将 YOLO 格式数据适配 SuperYOLO 训练流程
  • 从误报到修复:实战复盘一次AppScan扫描引发的‘虚惊一场’与优化配置
  • 别只知道UDP Flood了:2026年黑客最爱用的4种新型DDoS手法
  • DamaiHelper:告别黄牛票!Python自动化大麦网抢票脚本终极指南
  • 谱算符演算:解耦复杂系统交互,揭示经典谱理论盲区
  • MATLAB小波图像去噪工具包:含BaysShrink、Chang等自适应阈值算法及测试图与评估脚本
  • 11_Java集合框架概述
  • HoloLens混合现实应用开发实战:从工业设计到远程协作的四大核心场景
  • AI产品设计:从可用到好用的系统性设计思维与实践
  • 全栈开发硬核命题,拒绝CRUD男孩
  • UE5 VR开发避坑指南:Interaction组件里的Component Identification到底怎么用?
  • 类别不平衡问题
  • SNAP 9.0处理Sentinel-1 SLC数据:一个简化流程的避坑实践(跳过Split/Merge)
  • Redis中间件综合技术分析
  • 保姆级避坑指南:手把手教你用mmWave Studio 2.0搞定AWR1843雷达数据采集(从接线到.bin文件生成)
  • 配置存储卷
  • 别再只会用默认字符集了!Kali Linux中crunch的-f参数实战:调用内置字符库生成高命中率字典
  • 大模型如何提升代码质量与数据洞察:微软前沿研究解析与实践指南
  • FreeRTOS 任务调度机制剖析:优先级抢占、时间片轮转与上下文切换的汇编实现
  • [ACTF2020 新生赛]Exec
  • 杰理工程师日志2: 杰理蓝牙芯片音箱类方案开发添加提示音的具体操作指导说明
  • 树莓派玩家的后悔药:用Balena Etcher一键克隆和备份你的完整系统(含SD卡扩容后备份技巧)
  • Azure云上构建弹性HPC集群:从InfiniBand网络到Slurm调度的超级计算实践
  • GEE Assets权限管理详解:如何安全共享你的数据,以及调用他人公开Assets的正确姿势
  • 【AI笔记】短时纯音时长对音高感知偏移效应研究综述
  • 从‘通才’到‘专精’:聊聊大语言模型(LLM)微调中的终身学习困境与实战策略
  • YOLOv8魔改笔记:把C2f换成CSPStage,再加个检测头,我的GC10-DET缺陷识别项目效果起飞了
  • 从大数据到深数据:云计算与交互技术如何赋能文化遗产数字化
  • Verilog边沿检测电路实战:从原理到仿真,手把手教你搞定上升沿、下降沿和双沿检测
  • YOLOv11红外+可见光双路检测工具包:开箱即用的多模态目标识别方案