当前位置: 首页 > 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.jsqmd.com/news/975261/

相关文章:

  • FanControl终极指南:如何用免费软件实现Windows智能风扇控制与静音优化
  • Pearcleaner:macOS系统清理的终极解决方案,轻松释放磁盘空间
  • 2026年6月最新版唐山第三方CMACNAS甲醛检测治理口碑名单:万清CMA检测中心等5家深度测评 - 创达咨询
  • 计算机毕业设计之基于 Python 的校园超市进销存系统的设计与实现
  • 太原靠谱的搬家公司推荐 - 资讯纵览
  • 河南AI课程大揭秘:找到最适合你的那一款 - 品牌测评鉴赏家
  • 专业级生命周期评估:openLCA架构深度解析与高效应用指南
  • 终极指南:3步掌握Translumo实时屏幕翻译工具,打破游戏和视频的语言障碍
  • 2026 重庆包包回收市场实测:六大平台横向对比,正规高价首选添价收 - 薛定谔的梨花猫
  • 2026年滇西包车公司推荐:腾冲/芒市/怒江/保山/德宏一站式出行如何选择? - 品研笔录
  • 如何轻松清理Windows系统:Win11Debloat一键优化工具完全指南
  • 2026 年免费商用 AI,一站式搞定开发
  • 泸州龙马潭白酒OEM代工厂怎么选?2026年源头工厂与商超PB品牌定制完全对标指南 - 精选优质企业推荐官
  • i.MXRT系列MCU USB2.0认证预测试实战指南:从原理到调优
  • 2026年支架品牌厂家最新推荐榜单:抗震支架/综合支吊架/塑木护栏支架/数据中心支架源头实力厂家精选! - 企业推荐官【官方】
  • Cookie编辑器终极指南:浏览器Cookie管理神器完整教程
  • C++控制台版汽车站售票系统(含VS工程+数据文件+全程中文注释)
  • 2026 国产替代首选:RoHS 2.0 检测仪品牌排名,泓盛仪器实力上榜 -rohs2.0测试设备品牌测评:精度、性价比综合对比 - 品牌推荐大师1
  • pfam结构域批量注释方法总结
  • 2026 年 6 月最新|上海外贸营销推广公司哪家好,深挖海外精准客源 - 资讯纵览
  • 和田地区墨玉县上门做房屋屋顶楼顶补漏,墙面阳台防潮,阳光房彩钢地下室防水施工 - 天堂海洋
  • 营销短信发送接口有哪些?批量推广短信服务商解析选购指南 - Qqinqin
  • 2026手机Word转PDF详细教程:微软Office、WPS、小程序三步搞定
  • 解锁iOS系统限制:palera1n越狱工具的深度解析与实践指南
  • i.MX RT1170 CAAM模块实战:实现硬件级ECC密钥安全与ECDSA签名
  • 2026年百达翡丽中国区售后服务体系全面焕新:最新官方热线与全国网点指南 - 百达翡丽服务中心
  • 5分钟快速上手FF14国际服中文补丁:从语言障碍到母语畅玩
  • 计算机毕业设计之基于AES加密的医院信息管理系统的设计与实现
  • matplotlib Python 数据可视化的基石库
  • 告别ifconfig!Ubuntu 18.04+网络配置保姆级指南:从Netplan基础到双网卡实战