资讯详情

HarmonyOS实战:ArkUI+状态机打造小学数学“凑十法”动画演示

📅 2026/10/5 11:04:37 | 华诺云谱 👁 阅读
HarmonyOS实战:ArkUI+状态机打造小学数学“凑十法”动画演示
凑十法这个事我一直觉得是小学数学里最值得做成动态演示的节点之一。孩子刚接触进位加法脑子里还没有“凑整”的概念你跟他讲“8加5把5拆成2和38加2等于10再加3等于13”他能复述但未必真懂。文字的抽象表述对低龄孩子来说太枯燥了而传统静态板书又无法呈现“拆、移、补”这个过程。正好我在做HarmonyOS应用系列开发就想着用ArkTS写一个20以内进位加法的凑十法动画演示把“凑十”的每一步拆成可视化的动画让数字自己动起来给孩子看。这个项目的核心价值不在算法而在交互和动画表现力。HarmonyOS的ArkUI声明式开发模型自带一套比较完整的动画API从隐式动画、显式动画到属性动画、转场动画都有适合做这种步骤化的教学演示。而且HarmonyOS NEXT SDKAPI 125.0.0(12)对Canvas、属性动画、组件转场的支持已经非常稳定完全可以支撑这类中小型教育应用。做这个应用的过程本质上也是把一套“教学流程”翻译成“状态机动画”的实践我做完之后最大的感觉是动画不是花哨的装饰而是认知过程的具象化。如果你也是HarmonyOS开发者或者正在给孩子做数学启蒙相关的数字化工具这篇实战拆解可以帮你少走不少弯路。我会从数据建模、状态机设计、动画实现到性能优化把每一步的思路和坑位都摆出来你可以直接照着写。1. 这个项目到底在做什么先明确应用范围。20以内进位加法指的是两个加数都在10以内、结果在20以内的加法核心场景是“和超过10”的算式比如76、85、94这类。为什么单独把“凑十法”拿出来做动画因为它是小学一年级数学里最经典的策略——通过拆分较小的加数把其中一个加数补成10剩下的数再加过去。它的认知过程有三个关键步骤看大数找它离10还差几。拆分小数拿相应的数量去补大数。把补成10的部分和剩余部分合并得到结果。这三个步骤如果只用静态文字孩子很难在头脑中形成图像。如果做成动画孩子能亲眼看到“小数分走了一部分大数变成了10多出来一块拼上去”的动态过程理解效率会高得多。这也是我做这个项目的初衷——用数字可视化降低认知门槛。1.1 凑十法的教学痛点传统教学里老师会让孩子在图上圈一圈或者用小棒摆一摆。小棒是实物孩子操作起来直观但问题是实物操作无法记录“每一步发生了什么”也很难自动化生成大量练习题。而APP里常见的算术练习软件多数只给出正确答案动画不会拆解凑十的完整思维过程。说白了市面上的工具要么只做结果验证要么只做静态图解缺少一个真正意义上的“过程动画”。我走访过几位低年级数学老师他们反馈的最核心痛点是凑十法最难教的不是“结果”而是“为什么拆这个数以及拆出来之后怎么办”。比如“76”为什么要把6拆成3和3而不是4和2因为7需要3才能凑成10。这个“需要多少”的逻辑孩子往往要反复看多次实物操作才能内化。而数字动画恰好可以反复演示同一操作甚至让孩子自己点击控制步骤对于课后复习和预习都非常有帮助。1.2 为什么选HarmonyOS做动画演示选择HarmonyOS而不选普通的Web页面或Android原生主要基于三个考虑。第一HarmonyOS NEXT SDK从API 12开始ArkUI的动画能力已经覆盖了属性动画、显式动画、路径动画、Spring动画等针对这类“小元素移动、缩放、透明度变化”的演示场景根本不需要引入第三方动画库原生API就能做得很顺。第二ArkTS的声明式UI范式非常适合把“画面状态”映射成“数据状态”。我可以定义一个步骤枚举界面根据当前步骤自动渲染不同状态动画由状态变化触发逻辑清晰不容易出错。第三我打算后续在这个系列里做更多数学教学演示比如退位减法、乘法口诀表统一基于鸿蒙生态有利于做一套可复用的教学组件所以我直接基于HarmonyOS NEXT SDKAPI 12 / 5.0.0(12)来开发。网上关于HarmonyOS NEXT的动画教程多半是转圈、淡入淡出这类基础效果真正针对“教学类分步动画”的案例并不多。这个项目是一个很好的补位把教学流程和动画状态结合起来的思路可以被很多教育类APP借鉴。2. 核心设计与数据建模动手写代码之前先把数据结构和运行状态模型想清楚。动画演示和静态页面最大的区别在于每一帧都不是孤立的你需要知道自己“现在处在流程的哪一步”才能决定下一步怎么动。这个应用我设计了三层数据模型算式模型、步骤模型、动画状态模型。2.1 表达式数据结构一个加法算式本质上包含两个加数和结果。为了支撑凑十过程还需要扩展出“大数”“小数”“差量”“拆分结果”等派生字段。我定义一个AdditionExpression类Observed export class AdditionExpression { operand1: number; // 加数1 operand2: number; // 加数2 sum: number; // 和 bigOperand: number; // 较大的加数凑十的基准数 smallOperand: number; // 较小的加数用于拆分 diffToTen: number; // 大数距离10的差值 splitFirst: number; // 从小数拆分出的第一部分补大数 splitSecond: number; // 拆分后的剩余部分 constructor(a: number, b: number) { this.operand1 a; this.operand2 b; this.sum a b; this.bigOperand Math.max(a, b); this.smallOperand Math.min(a, b); this.diffToTen 10 - this.bigOperand; // 如果大数小数不需要进位则不需要拆分 this.splitFirst this.bigOperand this.smallOperand 10 ? this.diffToTen : 0; this.splitSecond this.smallOperand - this.splitFirst; } }这里有几个设计细节。第一为什么要用diffToTen而不是直接写死因为不同的算式大数离10的差值不同比如76的diff是385的diff是294的diff是1。动画要根据diff决定“从小数挪几个点过去”。第二splitFirst可能为0说明这个算式不需要凑十直接相加就好。这类算式不应该出现在演示模式里但在随机出题时可以过滤掉。这个数据类用Observed装饰是为了让绑定的UI组件能在数字变化时自动刷新。ArkTS中被观察对象的变化会自动触发组件刷新省去了手动setState的麻烦。2.2 动画状态机设计动画演示的核心是“步骤推进”。我定义了一个枚举表示当前流程阶段export enum DemoStep { Initial, // 初始状态展示两个加数圆点 HighlightBig, // 高亮大数提示它与10的差距 SplitSmall, // 拆分小数一部分移向大数 ComposeTen, // 凑成10大数补满形成10 AddRemainder, // 加上剩余部分得出结果 ShowResult // 显示最终算式和结果 }状态机流转规则如下当前步骤用户操作/自动触发下一状态Initial点击“开始”按钮HighlightBigHighlightBig自动延迟 1.2 秒SplitSmallSplitSmall动画完成回调ComposeTenComposeTen动画完成回调AddRemainderAddRemainder动画完成回调ShowResult为什么要用状态机而不是直接写一个“顺序执行”的动画序列因为教学演示必须允许用户随时暂停、回放、分步查看。状态机可以把“当前进行到哪一步”作为唯一事实来源UI据此渲染按钮的可用态和文案逻辑非常清晰。否则动画一旦播完你想回到中间某一步就得重新算所有中间状态代码会非常难维护。我还在状态上绑定了步骤说明文案。比如到了SplitSmall阶段底部提示文字变成“把小数拆成3和3其中3去补7”这样孩子在看动画的同时能听到/看到对应解说理解更到位。3. 动画实现细节与代码解析有了数据模型和状态机接下来就是整个项目最花功夫的部分怎么用HarmonyOS ArkUI把“凑十过程”做成一个有说服力的动画。我采用的方案是用一组圆形组件Circle或自定义Shape表示数字单位每个圆有自己的坐标通过animateTo显式动画改变坐标和透明度。这样比用Canvas绘制全部内容更容易与ArkUI组件树融合也方便点击交互。3.1 小球数字点的布局与绑定每个加数用一组圆点表示例如“76”左边7个圆点右边6个圆点。圆点大小、间距、颜色都要考虑低龄儿童的视觉特点我用了较大的圆点直径40vp行间距约8vp避免密集排列造成视觉混淆。圆点组件使用ForEach循环生成ForEach(this.leftDots, (item: number) { Circle({ width: 40, height: 40 }) .fill(this.currentStep DemoStep.HighlightBig ? #FF8C00 : #FFB6C1) .opacity(this.dotOpacity(item)) .offset({ x: this.dotOffsetX(item), y: this.dotOffsetY(item) }) }, (item: number) item.toString() this.currentStep.toString())每个圆点有一个唯一ID其实就是索引号dotOpacity和dotOffset根据当前状态返回对应的值。这里最关键的点是第三个参数——keyGenerator。因为这个项目里同一个圆点会在不同步骤间改变自身坐标和透明度如果不把当前步骤拼进key里ArkUI可能会错误复用组件导致动画数据错乱。我踩过这个坑一开始用固定的索引作为key步骤切换后组件没有被重建offset计算总是不对。后来把currentStep拼进key强制状态变化时重新生成组件树才彻底解决了问题。3.2 凑十过程的显式动画驱动HarmonyOS的animateTo是最适合这种“点击/步骤触发”场景的动画接口。它的用法是在animateTo闭包内修改状态变量闭包外部的状态变化就不会触发动画只有闭包内的变化会被捕获并自动生成过渡动画。举个例子从SplitSmall阶段进入ComposeTen阶段时需要把从“小数”拆分出的圆点移动到“大数”区域使大数刚好变成10个圆点。这段逻辑如下this.currentStep DemoStep.ComposeTen; this.leftDotPositions this.composeTenPositions(); // 返回新坐标数组 this.rightDotPositions this.rightAfterSplit(); // 剩余圆点位置 this.animateTo({ duration: 800, curve: Curve.EaseInOut, onFinish: () { this.currentStep DemoStep.AddRemainder; } });这里面animateTo的duration我设置成800毫秒不是随口定的。太短比如300毫秒孩子看不清圆点移动轨迹太长超过1.5秒又容易注意力涣散。实际测试下来800毫秒配合EaseInOut曲线最舒服起步慢一点中间快一点最后再慢下来视觉上很像一只手在轻轻推动圆点。拆分的动画则稍微复杂一些。小数的6个圆点要分成两组一组2个保持原位不动另一组3个分别移动到7对应的空隙位置。这里我没法直接“整体移动一个容器”因为每个圆点的目标位置不同。所以我的实现方式是预计算每个圆点的最终x、y坐标然后在SplitSmall触发时通过animateTo统一更新所有圆点坐标。坐标计算具体来说我预设了一个基准坐标系。初始状态下左侧圆点放在屏幕左半区域x80~240y100~260右侧圆点放在右半区域x280~440y100~260。当右侧被拆分时这3个圆点的目标坐标会“插入”到左侧7个点之间的间隙中形成“补位”效果。为简化问题我提前写死了几个固定插槽位置例如第1个补位点x200, y100第2个补位点x200, y160第3个补位点x200, y220这些坐标对应大数7的圆点网格中最右列的空位。因为不同大数的空位数量不同我写了一个getTargetPositions函数根据diffToTen动态计算。没必要做得多精巧关键是让孩子看到“移动填充”的对应关系。3.3 高亮与提示逻辑凑十法动画里“高亮”不是简单的配色变化而是认知引导。当过程进行到HighlightBig阶段大数圆点不仅颜色变化还可以加上一个呼吸动画——透明度从1.0到0.6循环提醒孩子“注意看这个大数离10还差几个”。这个呼吸效果用repeat参数实现this.animateTo({ duration: 600, iterations: 3, playMode: PlayMode.AlternateReverse }) { this.systemBigHighlight false; }同时在界面上出现一个气泡提示“7还差3个就是10”。这个文本绑定了一个状态变量状态变化时文本以渐隐渐显的转场效果切换。为了让文字切换不突兀我给文字外层容器加了.transition(TransitionEffect.OPACITY)当文字内容变化时自动触发淡入淡出。这里有个细节儿童教育类应用一定要注意“高亮”本身不能太刺激饱和度过高的红色或闪烁太快的动画容易让孩子视觉疲劳。我查过一些儿童心理学资料低龄儿童对慢速、有节奏的视觉反馈接受度更好。所以动画统一用柔和的橙色#FF8C00做高亮避免刺眼的红色闪烁也控制在3次以内。3.4 算式展示与结果反馈当所有圆点移动完毕进入ShowResult阶段底部会显示完整的算式“7 6 13”。但这个展示不是一次性出现而是分步的先显示“7 6 ”十位和个位的“1”和“3”分别用两个数字卡片弹出辅以轻微的缩放动画。个位“3”是原来小数拆分后剩余的部分十位“1”则是由“7310”这一组圆点整体变化而来。我用了两种视觉帮助孩子理解把“补成10”的那组圆点用一个虚线圆框圈起来上方标出一个“10”的标签。剩余3个圆点下方标出“3”的标签。这样孩子就能直观看到“13”是由“10”组和“3”组拼装出来的。这个“部件拼合”的视觉隐喻其实就是位值制的基础。很多孩子计算进位加法出错根本原因就是没有建立“10个一组”的位值概念。动画演示能很好地补齐这个认知缺口。4. 踩坑记录与性能优化这个项目规模不大但涉及动画状态管理、组件复用、动态坐标计算等实际开发中还是遇到了几个值得记录的问题。我把它们整理出来给后面做同类应用的朋友做个参考。4.1 动画不同步多个 animateTo 嵌套的坑最早版本里拆分和凑成两个阶段是连续两个animateTo写在一个函数中但第二个动画总是迟一帧启动导致圆点会先跳到中间态再继续移动。排查后发现ArkUI的animateTo默认是异步执行的连续调用时第二个动画会在第一个动画的初始状态基础上计算。解决办法是把第二个动画的触发移到第一个动画的onFinish回调里形成严格的顺序执行而不是靠时间猜测。改造后的核心逻辑this.animateTo({ duration: 800, onFinish: () { this.animateTo({ duration: 500 }) { // 此处更新第二个步骤的状态变量 } } });这是动画序列的标准写法虽然嵌套看着有点丑但确实可靠。4.2 组件复用导致的坐标残留前面提到的keyGenerator问题我再展开讲讲。ArkUI中ForEach组件在数据更新时会尽量复用已有组件实例如果你只改了数据、不改变key组件实例会被保留。这本来是性能优化但在这个项目中圆点从一个坐标移动到另一个坐标如果组件实例被复用了它的内部状态尤其是offset可能不会被正确重置出现“上一个步骤残影”的视觉效果。我最后的解决方案是把currentStep作为ForEach的keyGenerator组成部分每次步骤切换时强制所有圆点重新创建。本身就是几十个圆点重建成本极低但画面稳定很多。这提醒我不要过分迷信组件复用“无脑复用”有时反而会让动画状态混乱。4.3 性能优化减少无关组件刷新在currentStep变化时所有绑定此状态变量的组件都会被刷新。如果整个页面由一个大的build方法承载那么每次步骤切换布局、圆点、提示文字全都会重新计算。虽然40个圆点不多但我仍然做了以下优化把圆点区域拆成独立组件DotArea只接收必要的坐标数组和颜色数组。步骤文字单独放在另一个子组件中避免和圆点相互干扰。使用Prop或ObjectLink精确传递需要的数据而不是把整个AdditionExpression实例传下去。在真机测试HarmonyOS NEXT API 12Pixel系列测试机下动效流畅度稳定在60帧动画过程中没有明显掉帧。说明这套方案对低算力设备也是友好的。4.4 尺寸适配与横竖屏因为是教学演示我优先考虑横屏体验——横屏能提供更宽的左右空间布局圆点避免竖屏下两边圆点太拥挤。我用了响应式布局横向宽度小于600vp时自动缩小圆点直径到32vp大于600vp时保持40vp并增加间距。这个适配通过MediaQuery实现使用起来也简单State private isLandscape: boolean true; build() { if (this.isLandscape) { this.MainPanel(); } else { this.MainPanelPortrait(); } }我在onPageShow里注册媒体查询监听当屏幕方向变化时重新加载布局。这里有一个小坑MediaQuery的监听回调不是立即触发的页面首次加载时可能拿到错误的方向。解决办法是在onPageShow时主动调用一次getCurrentBreakpoint获取当前窗口尺寸再决定使用哪种布局不能完全依赖监听。5. 一些提升演示效果的经验项目做完之后我又迭代了几个小功能虽然不是核心但对实际教学效果提升明显也顺手分享一下。5.1 步骤回放与手动控制自动演示虽然省心但孩子的学习节奏不一样有的需要反复看某一步。所以我加了一个“分步模式”用户可以点击“上一步/下一步”手动切换阶段。每次切换前先调用resetPositions()把圆点恢复到初始布局再通过状态机向前走一步。这样做的成本很低因为状态机本身已经定义了每个步骤手动切换只是重新赋值currentStep而已。如果继续扩展还可以做“单步循环”——让某一段动画比如拆分循环播放3遍强化记忆。这个功能实现也不复杂在onFinish里判断当前step是否等于循环目标若是则重置步骤重新触发。5.2 随机出题与难度自适应为了便于课后练习我加了一个随机出题功能基于AdditionExpression生成算式时过滤掉“不需要进位”的题目。目前的过滤规则是bigOperand smallOperand 10的直接跳过。这样保证每次出现的都是真正需要凑十的题目。难度分级上我简单做了两档初级只出9加几如95、97因为9离10最近孩子最容易看到补数高级再出8几、7几。对于不同基础的孩子家长可以手动切换。这个分级思路不是凭空想的是根据多位一年级老师的建议先从“补口”最显眼的数开始逐渐过渡到需要更多思考的数。5.3 动画与语音解说的同步目前版本里我用了文字提示没有接入语音。但如果要做成自主学习工具语音解说是刚需。简单方案是在每次状态切换时调用系统TTS接口读提示文本。HarmonyOS NEXT的 TTS 接口在API 12版本已经可用接入成本不高。不过注意TTS在朗读过程中如果切换步骤需要先stop()再speak()否则会排队朗读造成内容错乱。以上就是我做“20以内进位加法——凑十法动画演示”这个HarmonyOS应用实例的完整记录。从数据结构、状态机、动画实现到性能优化整体思路就是一句话“把教学过程拆成可控的状态用动画把每个状态过渡讲清楚。” 我个人在实际操作中的体会是教育类动画和产品类动画的取舍很不一样——不追求炫技而是要把抽象逻辑以最直观的方式展示给孩子这一点远比动画算法本身更重要。这套“状态机属性动画”的方案我已经复用到减法、乘法等系列里如果你也在做儿童教育工具可以按这个思路试试看。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑