news 2026/7/24 22:32:55

HarmonyOS7 串联动画实战:用 setTimeout 编排动画序列

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HarmonyOS7 串联动画实战:用 setTimeout 编排动画序列

文章目录

    • 前言
    • 效果预览
    • 布局思路
    • 逐步实现
      • 定义独立的动画状态
      • 方块渲染与动画绑定
      • 核心:setTimeout 串联动画
      • 波浪效果的嵌套 setTimeout
    • 关键代码
    • 踩坑记录
      • 坑1:延迟时间太短会导致动画"吞掉"
      • 坑2:快速连续点击会出bug
      • 坑3:animation 和 animateTo 的区别
      • 坑4:临时数组写法的注意事项
    • 写在最后

前言

做 App 动效的时候,经常碰到一个需求——几个动画不能同时跑,得一个接一个地依次执行。比如四个方块依次弹入画面,或者一组卡片像多米诺骨牌一样波浪式出现。

这种"串联动画"在 iOS 上有UIView.animate的 completion 回调,在 Web 上有 CSS animation-delay。那在 HarmonyOS7 的 ArkUI 里怎么搞?答案可能比你想的要简单:setTimeout+animateTo就能搞定。

这篇文章就来拆解串联动画的实现思路,顺便把踩过的坑也捋一遍。

效果预览

最终实现的效果是这样的:页面上有四个彩色方块,点击"依次进场"按钮后,它们按照从左到右的顺序逐个从透明缩小状态变为正常显示。点击"波浪效果"则会出现更花哨的效果——每个方块先出现再消失,形成一个传递的波浪。

按钮效果描述动画时长
依次进场四个方块依次从 opacity=0 变为 opacity=1约 1s 全部完成
波浪效果逐个出现再消失,形成波浪传递约 1.2s 全部完成

| 重置 | 所有方块瞬间回到初始状态 | 无动画 |

布局思路

整个页面结构不复杂,一个纵向滚动容器里面套一个卡片:

  • 标题文字
  • 四个方块的横向排列(Row+ForEach
  • 底部按钮组

关键是每个方块需要绑定独立的动画状态值。这一点很重要——如果四个方块共享一个状态变量,那就只能同时动,没法分别控制。

逐步实现

定义独立的动画状态

给每个方块准备一个独立的状态变量:

@Entry@Componentstruct SequentialAnimationChain{@Statestep1:number=0@Statestep2:number=0@Statestep3:number=0@Statestep4:number=0build(){Column(){// ...}}}

为什么不用数组?说实话,ArkUI 的@State对数组内元素的变更检测有时候不够灵敏,直接改数组的某个索引值不一定能触发 UI 刷新。用独立的变量虽然写起来有点笨,但最可靠

方块渲染与动画绑定

四个方块通过ForEach渲染,每个方块的opacityscale绑定到对应的状态变量上:

Row({space:12}){ForEach([0,1,2,3],(idx:number)=>{Column().width(60).height(60).backgroundColor(PRESET_COLORS[idx].value).borderRadius(12).opacity(([this.step1,this.step2,this.step3,this.step4])[idx]).scale({x:([this.step1,this.step2,this.step3,this.step4])[idx],y:([this.step1,this.step2,this.step3,this.step4])[idx]}).animation({duration:400,curve:Curve.EaseOut})})}.width('100%').justifyContent(FlexAlign.Center)

这里用了个小技巧——把四个变量组成一个临时数组[this.step1, this.step2, this.step3, this.step4],然后通过索引idx取值。这样ForEach里面就不需要写if-else判断了。

核心:setTimeout 串联动画

"依次进场"的实现逻辑是最关键的部分:

Button('依次进场').onClick(()=>{// 先全部重置为 0this.step1=0;this.step2=0;this.step3=0;this.step4=0// 用不同的延迟时间依次触发动画setTimeout(()=>{animateTo({duration:400},()=>{this.step1=1})},0)setTimeout(()=>{animateTo({duration:400},()=>{this.step2=1})},200)setTimeout(()=>{animateTo({duration:400},()=>{this.step3=1})},400)setTimeout(()=>{animateTo({duration:400},()=>{this.step4=1})},600)})

核心原理:每个setTimeout的延迟时间递增 200ms,让四个animateTo调用在时间轴上错开,形成依次执行的视觉效果。

画个时间轴就清楚了:

时间轴: 0ms → step1 开始动画 (0→1, 持续400ms) 200ms → step2 开始动画 (0→1, 持续400ms) 400ms → step3 开始动画 (0→1, 持续400ms) 600ms → step4 开始动画 (0→1, 持续400ms)

波浪效果的嵌套 setTimeout

波浪效果更有意思——每个方块先出现再消失,而且是逐个传递的:

Button('波浪效果').onClick(()=>{for(leti=0;i<4;i++){setTimeout(()=>{// 第一阶段:出现animateTo({duration:300,curve:Curve.EaseOut},()=>{if(i===0)this.step1=1if(i===1)this.step2=1if(i===2)this.step3=1if(i===3)this.step4=1})// 第二阶段:延迟300ms后消失setTimeout(()=>{animateTo({duration:300},()=>{if(i===0)this.step1=0if(i===1)this.step2=0if(i===2)this.step3=0if(i===3)this.step4=0})},300)},i*150)}})

这段代码嵌套了两层setTimeout

  • 外层控制每个方块的启动时间,间隔 150ms
  • 内层控制方块出现后的消失时机,延迟 300ms

关键代码

把完整的核心逻辑整理一遍。先看重置操作:

Button('重置').onClick(()=>{this.step1=0;this.step2=0;this.step3=0;this.step4=0})

重置不需要animateTo,直接赋值就会瞬间跳回初始状态。如果你希望重置也有动画过渡,把赋值放进animateTo里就行。

完整的组件结构:

@Entry@Componentstruct SequentialAnimationChain{@Statestep1:number=0@Statestep2:number=0@Statestep3:number=0@Statestep4:number=0build(){Column(){Scroll(){Column(){Text('串联动画').fontSize(18).fontWeight(FontWeight.Bold).margin({bottom:8})Column(){Row({space:12}){ForEach([0,1,2,3],(idx:number)=>{Column().width(60).height(60).backgroundColor(PRESET_COLORS[idx].value).borderRadius(12).opacity(([this.step1,this.step2,this.step3,this.step4])[idx]).scale({x:([this.step1,this.step2,this.step3,this.step4])[idx],y:([this.step1,this.step2,this.step3,this.step4])[idx]}).animation({duration:400,curve:Curve.EaseOut})})}.width('100%').justifyContent(FlexAlign.Center)// 按钮组...}.width('100%').backgroundColor('#FFFFFF').borderRadius(12).padding(16)}.width('100%')}.layoutWeight(1)}.width('100%').height('100%').backgroundColor('#F5F6FA').padding(16)}}

踩坑记录

坑1:延迟时间太短会导致动画"吞掉"

如果两个setTimeout的延迟间隔太短(比如只有 50ms),前一个动画还没展开,后一个就开始了,视觉上看起来就像同时出现。建议间隔至少在150-200ms以上。

坑2:快速连续点击会出bug

用户疯狂点击"依次进场"按钮,会导致多组setTimeout叠加,方块的状态会乱。解决办法很简单,加个节流标记:

@StateisAnimating:boolean=falseButton('依次进场').onClick(()=>{if(this.isAnimating)returnthis.isAnimating=true// 动画逻辑...setTimeout(()=>{this.isAnimating=false},1200)})

坑3:animation 和 animateTo 的区别

.animation()是声明式动画,绑定在组件属性上,当属性变化时自动执行。而animateTo()是命令式调用,在闭包里改变状态变量来触发动画。串联动画场景下,两者配合使用效果最好——用.animation()定义动画参数,用animateTo()控制触发时机。

坑4:临时数组写法的注意事项

.opacity(([this.step1,this.step2,this.step3,this.step4])[idx])

这行代码每次build()都会创建一个新的临时数组。在ForEach场景下这是可以接受的,但如果列表很长(比如几十个元素),频繁创建数组会有性能问题,建议改用 Map 或者独立组件。

写在最后

串联动画的核心就一句话:setTimeout控制时间轴,用animateTo控制动画触发

这种写法虽然不像 iOS 的UIViewPropertyAnimator那么优雅,但胜在简单直观,不需要学习额外的 API。实际项目中,把延迟时间和动画参数抽成常量,维护起来也不会太痛苦。

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

WarcraftHelper:魔兽争霸3玩家的5大游戏体验优化解决方案

WarcraftHelper&#xff1a;魔兽争霸3玩家的5大游戏体验优化解决方案 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 还在为经典魔兽争霸3在现代电脑上…

作者头像 李华
网站建设 2026/7/24 22:30:36

WRF模型性能评估

目录 一、数据下载&#xff08;step1_data_download&#xff09; 1. 筛选出需要的站点 &#xff08;1&#xff09;ArcGIS Pro 中打开 isd-history.csv &#xff08;2&#xff09;按经纬度范围筛选&#xff08;比如按 WRF/CMAQ 域&#xff09; 2. 导出选中的站点 3. 下载I…

作者头像 李华
网站建设 2026/7/24 22:29:04

HarmonyOS7 多媒体能力怎么放进一个页面:MediaPanelSwitcher 组合式案例解析

文章目录前言这个案例适合学什么完整代码先看页面骨架&#xff1a;一个状态切三块内容切换头部为什么用 ForEachWeb 面板讲的是“控制感”Canvas 面板讲的是“自定义表达”Image 面板讲的是“资源展示效率”这三个组件到底怎么选真实业务里怎么改造成可用页面这类组合页容易踩的…

作者头像 李华
网站建设 2026/7/24 22:29:02

AI大模型实战指南:低成本高效应用方案

1. 项目概述&#xff1a;AI大模型平民化应用指南去年帮朋友公司优化客服系统时&#xff0c;我用三个不同的大模型搭建了智能应答矩阵&#xff0c;成本比原方案降低67%的同时响应速度提升3倍。这个经历让我意识到&#xff0c;现在正是普通人驾驭顶尖AI的最佳时机——就像2007年智…

作者头像 李华
网站建设 2026/7/24 22:28:47

让你的桌面活起来:呆啵宠物DyberPet打造个性化AI陪伴体验

让你的桌面活起来&#xff1a;呆啵宠物DyberPet打造个性化AI陪伴体验 【免费下载链接】DyberPet Desktop Cyber Pet Framework based on PySide6 项目地址: https://gitcode.com/GitHub_Trending/dy/DyberPet 你是否曾梦想过让你喜爱的动漫角色、游戏伙伴或原创角色真正…

作者头像 李华