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

HarmonyOS ArkUI 动画完全指南:属性动画、显式动画与组件动画

文章目录

    • 前言
    • 一、属性动画:.animation()
      • 1.1 最简单的动画写法
      • 1.2 常用缓动曲线
    • 二、显式动画:animateTo()
      • 2.1 animateTo 与 .animation() 的区别
    • 三、MapKit ScaleAnimation:项目核心动画
      • 3.1 源码分析
      • 3.2 ScaleAnimation 参数详解
    • 四、转场动画:组件进出场
      • 4.1 if/else 触发的进出场动画
      • 4.2 常用转场效果组合
    • 五、综合实战:带动画的加油站卡片列表
    • 总结

前言

一个没有动画的 App 像一本静态图册,而合理的动画能传递状态变化、引导用户视线、提升操作反馈感。HarmonyOS ArkUI 提供了多层次的动画系统:属性动画.animation())、显式动画animateTo())、转场动画transition)以及MapKit 专属的 ScaleAnimation

本项目中 Marker 被点击时会放大 1.5 倍,就是通过map.ScaleAnimation实现的——本篇将深入讲解这个动画的原理,以及 ArkUI 各类动画的实战写法。

一、属性动画:.animation()

1.1 最简单的动画写法

只需在会改变的属性后面加.animation(),属性变化时自动产生动画:

@Entry@Componentstruct AttributeAnimationDemo{@Statescales:number=1.0;@Stateopacitys:number=1.0;@StatebgColor:string='#1A6FF5';@StateisExpanded:boolean=false;build(){Column({space:32}){// 缩放动画Image($r('app.media.startIcon')).width(80).height(80).scale({x:this.scales,y:this.scales}).animation({// ← 这里!duration:300,// 动画时长(毫秒)curve:Curve.EaseOut,// 缓动曲线iterations:1,// 执行次数(-1 = 无限循环)playMode:PlayMode.Normal}).onClick(()=>{this.scales=this.scales===1.0?1.5:1.0;})// 透明度动画Text('点击我淡入淡出').fontSize(16).opacity(this.opacitys).animation({duration:500,curve:Curve.Linear}).onClick(()=>{this.opacitys=this.opacitys===1.0?0.2:1.0;})// 颜色动画Button('切换颜色').backgroundColor(this.bgColor).animation({duration:400,curve:Curve.EaseInOut}).onClick(()=>{this.bgColor=this.bgColor==='#1A6FF5'?'#52C41A':'#1A6FF5';})// 尺寸动画(展开/收起)Column().width('80%').height(this.isExpanded?200:60).backgroundColor('#E8F0FE').borderRadius(12).animation({duration:400,curve:Curve.Linear}).onClick(()=>{this.isExpanded=!this.isExpanded;})}.padding(32).width('100%').height('100%').justifyContent(FlexAlign.Center)}}

1.2 常用缓动曲线

Curve 值效果适用场景
Linear匀速进度条、loading
EaseIn由慢到快元素消失
EaseOut由快到慢元素出现(自然)
EaseInOut先慢后快再慢通用,最自然
FastOutSlowInMaterial Design 标准曲线推荐通用

二、显式动画:animateTo()

2.1 animateTo 与 .animation() 的区别

特性.animation()animateTo()
触发方式属性变化时自动在闭包内手动触发
控制粒度单个属性闭包内所有状态变化
适用场景简单属性动画多属性同时动画
@Entry@Componentstruct AnimateToDemo{@StatecardX:number=0;@StatecardWidth:number=200;@StatecardColor:string='#1A6FF5';@StatecardRadius:number=8;build(){Column({space:32}){// 卡片Text('加油站卡片').width(this.cardWidth).height(80).backgroundColor(this.cardColor).borderRadius(this.cardRadius).fontColor('#FFFFFF').fontSize(16).textAlign(TextAlign.Center).offset({x:this.cardX})// 触发多属性同时动画Button('展开卡片').onClick(()=>{// animateTo 包裹的所有状态变化都会产生动画animateTo({duration:500,curve:Curve.EaseOut},()=>{this.cardX=0;this.cardWidth=320;// 宽度变化this.cardColor='#52C41A';// 颜色变化this.cardRadius=16;// 圆角变化// 所有这些变化同时动画!});})Button('收起卡片').onClick(()=>{animateTo({duration:500,curve:Curve.Spring},()=>{this.cardX=0;this.cardWidth=200;this.cardColor='#1A6FF5';this.cardRadius=8;});})}.padding(32).width('100%').height('100%').justifyContent(FlexAlign.Center)}}

三、MapKit ScaleAnimation:项目核心动画

3.1 源码分析

MapUtil.ets中的imageAnimation方法:

asyncimageAnimation(marker:map.Marker,imageScale:number):Promise<void>{// 创建缩放动画:X轴从1倍到imageScale倍,Y轴同样letanimation=newmap.ScaleAnimation(Constants.ONE,// fromX: 起始X缩放(1倍)imageScale,// toX: 目标X缩放Constants.ONE,// fromY: 起始Y缩放(1倍)imageScale// toY: 目标Y缩放);animation.setDuration(100);// 动画时长:100msanimation.setFillMode(map.AnimationFillMode.FORWARDS);// 动画结束后保持最终状态marker.setAnimation(animation);// 给 Marker 设置动画marker.startAnimation();// 启动动画}

调用时机:

// 点击 Marker → 放大this.mapController.on('markerClick',(marker)=>{this.imageScale=1.5;mapUtil.imageAnimation(marker,this.imageScale);// 放大到1.5倍});// 关闭弹窗 → 恢复原始大小onWillDismiss:((dismissSheetAction:DismissSheetAction)=>{if(this.curMarker){this.imageScale=1;mapUtil.imageAnimation(this.curMarker,this.imageScale);// 恢复1倍}dismissSheetAction.dismiss();})

3.2 ScaleAnimation 参数详解

// map.ScaleAnimation(fromX, toX, fromY, toY)// fromX/fromY: 动画起始缩放比例(通常为1,即当前大小)// toX/toY: 动画目标缩放比例// 放大效果:1 → 1.5newmap.ScaleAnimation(1,1.5,1,1.5);// 缩小效果:1.5 → 1newmap.ScaleAnimation(1.5,1,1.5,1);// 只横向拉伸:newmap.ScaleAnimation(1,2,1,1);// X轴放大2倍,Y轴不变// AnimationFillMode 说明:// FORWARDS: 动画结束后保持最终状态(常用)// BACKWARDS: 动画开始前应用初始帧// BOTH: 两者都应用// NONE: 动画结束后恢复原始状态

四、转场动画:组件进出场

4.1 if/else 触发的进出场动画

@Entry@Componentstruct TransitionDemo{@StateshowCard:boolean=false;build(){Column({space:24}){Button(this.showCard?'隐藏卡片':'显示卡片').onClick(()=>{// 需要用 animateTo 包裹,才能触发转场动画animateTo({duration:400,curve:Curve.EaseInOut},()=>{this.showCard=!this.showCard;});}).backgroundColor('#1A6FF5').fontColor('#FFFFFF').borderRadius(20)if(this.showCard){Column({space:8}){Text('加油站详情').fontSize(18).fontWeight(FontWeight.Bold)Text('中国石化望京站').fontSize(14).fontColor('#666666')Text('距您 1.2km · 营业中').fontSize(13).fontColor('#52C41A')}.padding(20).width('90%').backgroundColor('#FFFFFF').borderRadius(16).shadow({radius:8,color:'#20000000',offsetX:0,offsetY:4})// 进入动画:从下方滑入 + 透明度从0到1.transition(TransitionEffect.asymmetric(TransitionEffect.OPACITY.animation({duration:300}).combine(TransitionEffect.translate({y:40}).animation({duration:300,curve:Curve.EaseOut})),TransitionEffect.OPACITY.animation({duration:200}).combine(TransitionEffect.translate({y:20}).animation({duration:200}))))}}.padding(32).width('100%').height('100%').justifyContent(FlexAlign.Center)}}

4.2 常用转场效果组合

// 淡入淡出.transition(TransitionEffect.OPACITY.animation({duration:300}))// 从上滑入.transition(TransitionEffect.translate({y:-50}).animation({duration:300}))// 从右滑入(配合页面切换).transition(TransitionEffect.translate({x:100}))// 缩放 + 淡入(适合卡片弹出).transition(TransitionEffect.OPACITY.combine(TransitionEffect.scale({x:0.8,y:0.8})).animation({duration:300,curve:Curve.EaseOut}))

五、综合实战:带动画的加油站卡片列表

interfaceAnimStation{id:string;name:string;distance:number;isSelected:boolean;}@Entry@Componentstruct AnimatedStationList{@Statestations:AnimStation[]=[{id:'1',name:'望京石化',distance:0.8,isSelected:false},{id:'2',name:'朝阳石油',distance:1.5,isSelected:false},{id:'3',name:'国贸壳牌',distance:2.1,isSelected:false},];selectStation(id:string):void{animateTo({duration:300,curve:Curve.EaseInOut},():void=>{this.stations=this.stations.map((s:AnimStation):AnimStation=>{constupdated:AnimStation={id:s.id,name:s.name,distance:s.distance,isSelected:s.id===id};returnupdated;});});}build(){Column({space:12}){Text('附近加油站').fontSize(20).fontWeight(FontWeight.Bold).alignSelf(ItemAlign.Start)ForEach(this.stations,(station:AnimStation)=>{Row({space:16}){Text('⛽').fontSize(station.isSelected?32:24)// 选中时图标变大.animation({duration:300,curve:Curve.EaseOut})Column({space:4}){Text(station.name).fontSize(station.isSelected?18:15)// 选中时字体变大.fontWeight(station.isSelected?FontWeight.Bold:FontWeight.Normal).fontColor(station.isSelected?'#1A6FF5':'#333333').animation({duration:300})Text(`${station.distance}km`).fontSize(13).fontColor(station.isSelected?'#1A6FF5':'#999999').animation({duration:300})}.alignItems(HorizontalAlign.Start).layoutWeight(1)if(station.isSelected){Text('✓').fontSize(18).fontColor('#1A6FF5').fontWeight(FontWeight.Bold).transition(TransitionEffect.OPACITY.animation({duration:200}))}}.padding(16).width('100%').backgroundColor(station.isSelected?'#E8F0FE':'#FFFFFF').borderRadius(12).border({width:station.isSelected?2:0,color:'#1A6FF5'}).animation({duration:300,curve:Curve.EaseInOut}).onClick(()=>{this.selectStation(station.id);}).shadow(station.isSelected?{radius:12,color:'#201A6FF5',offsetX:0,offsetY:4}:{radius:0,color:'#00000000',offsetX:0,offsetY:0})},(s:AnimStation)=>s.id)}.padding(24).width('100%').height('100%').backgroundColor('#F5F7FA')}}

总结

ArkUI 动画分三层:.animation()最简单(属性自动动画),animateTo()最灵活(多属性同时动画),transition处理组件进出场。MapKit 的ScaleAnimation是地图专属 API,用于让 Marker 在点击时产生放大缩小的视觉反馈。合理运用动画能让应用体验从"可用"升级到"好用",是 HarmonyOS 开发中不可忽视的细节。

http://www.rkmt.cn/news/1486984.html

相关文章:

  • 2026 重庆包包回收市场实测:六大平台横向对比,正规高价首选添价收 - 薛定谔的梨花猫
  • 太原靠谱的搬家公司推荐 - 资讯纵览
  • 计算机毕业设计之基于 Python 的校园超市进销存系统的设计与实现
  • i.MXRT系列MCU USB2.0认证预测试实战指南:从原理到调优
  • 计算机毕业设计之基于AES加密的医院信息管理系统的设计与实现
  • 5分钟快速上手FF14国际服中文补丁:从语言障碍到母语畅玩
  • 2026手机Word转PDF详细教程:微软Office、WPS、小程序三步搞定
  • 营销短信发送接口有哪些?批量推广短信服务商解析选购指南 - Qqinqin
  • RT6xx AES加密实战:从软件密钥到PUF的嵌入式安全密钥管理
  • 2026关节模组轴承厂家哪家值得长期合作?从口碑、性价比到服务一次讲透 - 品牌2026
  • 广州花都化妆品公司想整改历史不规范账务,这3个处理顺序搞错了会越搞越乱 | 3个顺序坑 - 欢欢在创业
  • 大润发购物卡回收全流程拆解,3步操作实时到账不踩坑 - 京顺回收
  • 26个高质量阅读APP书源配置终极指南:解锁海量小说资源
  • SelfCheckGPT黑盒幻觉检测:大型语言模型事实性验证的零资源技术架构
  • 5分钟掌握Cat-Catch:浏览器资源嗅探的终极免费指南
  • Qt5写的本地电子商城桌面程序,带登录页、商品管理与MySQL数据库全套源码
  • 拒绝写代码!Web浏览器里调PID、控位置、控转速——这款FOC无刷电机模块,强得离谱_99个联控 磁编码器+无刷电机 BLDC+CAN总线一体控制器
  • 小白程序员必看:收藏这份大模型学习指南,轻松入门AI Agent世界!
  • MPC7450 L3缓存采样点设置与延迟计算实战指南
  • 爽翻!输入关键词,这几款AI写作辅助网站直接生成毕业论文!
  • MPC8260最小系统设计实战:从SDRAM配置到PCB布局要点解析
  • 高性价比PVC卡片打印机厂商选型参考及落地全流程指南 - 资讯速览
  • SD-PPP终极指南:5分钟在Photoshop中实现AI绘图革命
  • 广州包包回收实体门店全攻略!2026最新行情解析,爱马仕LV香奈儿一站式高价变现 - 薛定谔的梨花猫
  • 如何在虚幻引擎5中高效导入VRM角色:VRM4U插件完整实战指南
  • # 2026衡阳免砸砖漏水维修全攻略|卫生间/阳台/厨房/屋顶根治方法+避坑指南|苏易修缮 - 苏易修缮
  • MPC8245与CF卡接口设计:时序匹配与握手模式实战解析
  • Rust模块系统与crate发布实践:从私有项目到开源分享
  • 南京SEO优化公司|本地企业获客优化,南京搜索引擎优化公司口碑推荐 - 招财兔数字员工
  • 《富爸爸巴比伦最富有的人》金句