• 动画分类

1. 属性动画 .animation() —— 挂在组件上,属性一变自动平滑过渡
2. 显式动画 animateTo() —— 把改状态的代码包起来,主动触发动效
3. 页面间转场 NavDestination.systemTransition —— 页面切换动画
4. 组件内转场 .transition() —— 组件出现/消失时的特效
5. 关键帧动画 keyframeAnimateTo() —— 一个动画分好几段控制
6. 路径动画 .motionPath() —— 组件沿自定义路径移动

属性动画

• 能动画:宽高、背景色、透明度、缩放、旋转、位移
• 不能:zIndex、focusable 这种值变了画面不一定变

就是给 Column 加 .animation(),我的理解就是给它加一个动画效果.animation ({ })里加动画实现的效果条件比如动画时长以及运行次数等

.animation({
duration: 800, // 动画时长
curve: Curve.EaseInOut, // 曲线
iterations: 1 // 次数,-1 无限循环
})

曲线解释

• Linear:匀速
• EaseIn:慢到快
• EaseOut:快到慢
• EaseInOut:缓入缓出
• Spring:弹性回弹

学习成果

import { curves } from '@kit.ArkUI'

@Entry
@Component
struct AttributeAnimationDemo{
@State boxWidth:number=100
@State boxHeight:number=100
build() {
Column({space:20}){
Column()
.width(this.boxWidth)
.height(this.boxHeight)
.backgroundColor('#007AFF')
.borderRadius(20)
.animation({
duration:700,
curve:Curve.EaseInOut ,
iterations:1
})
Button('缩小')
.onClick( ()=>{
this.boxWidth=50
this.boxHeight=50
})
.width('20%')
.height('10%')

.padding(20)
Button('放大')
.onClick( ()=>{
this.boxWidth=200
this.boxHeight=200
})
.width('20%')


.height('10%')

.padding(20)
}
.justifyContent(FlexAlign.Center)
}
}

运行视频

属性

显式动画

注意事项

1. 别在 aboutToAppear / aboutToDisappear 里调动画——build 还没执行,属性没初值,动画出不来
2. 状态管理 V2 里用 animateTo 可能出异常效果,要留意
3. animateTo 能做宽高动画,但内容(文字、Canvas)默认直接跳到终点,要跟随变化得配 renderFit
4. 组件出现/消失的效果,显式动画不好做的话,就用 .transition()

animate to与箭头函数联用

1.控制状态
Button('切换卡片')
.onClick(() => {
animateTo({ duration: 500, curve: Curve.EaseInOut }, () => {
this.showCard = !this.showCard
})
})

2.一次修改多个属性

animateTo({ duration: 800, curve: Curve.EaseInOut }, () => {
this.boxWidth = 160
this.boxHeight = 160
this.opacity = 1
})

属性动画与显式动画的区别


• 属性动画是"这个组件以后属性变了都要动画"——声明一次,永久生效
• 显式动画是"这次修改要有动画"——每次都要主动写

属性动画:

Column()
.width(this.boxWidth)
.height(this.boxHeight)
.animation({ duration: 800, curve: Curve.EaseInOut })

Button('放大').onClick(() => {
this.boxWidth = 160
this.boxHeight = 160
})

显式动画

Column()
.width(this.boxWidth)
.height(this.boxHeight)

Button('放大').onClick(() => {
animateTo({ duration: 800, curve: Curve.EaseInOut }, () => {
this.boxWidth = 160
this.boxHeight = 160
})
})

转场动画

组内转场

if加transition ➕animate to


if (this.showCard) {
Text('卡片')
.transition(TransitionEffect.opacity(0).animation({ duration: 500 }))
}
animateTo({ duration: 500 }, () => {
this.showCard = !this.showCard
})

1.透明度过渡:TransitionEffect.opacity(0)

2.缩放:TransitionEffect.scale({ x: 0.5, y: 0.5 })
3.平移:TransitionEffect.translate({ x: 200, y: 0 })
4.旋转:TransitionEffect.rotate({ z: 1, angle: 180 })
5.边缘移入/出:TransitionEffect.move(TransitionEdge.BOTTOM)
6.非对称:TransitionEffect.asymmetric(进, 出)


六种动画对比

下面用一张表格汇总属性动画、显式动画、组件内转场、页面间转场、关键帧动画和路径动画的核心差异,方便快速查阅。

动画类型 触发方式 适用场景 典型 API 注意事项
属性动画 组件属性变化时自动触发,声明一次永久生效 宽高、背景色、透明度、缩放、旋转、位移等属性持续变化的场景 .animation() zIndex、focusable 等不影响画面的属性不会产生动画;不要在 aboutToAppear / aboutToDisappear 中调用
显式动画 把修改状态的代码包进 animateTo 回调,主动触发动效 需要精确控制某一次状态变更的动画效果,如按钮点击后的宽高变化 animateTo() 状态管理 V2 下可能出现异常效果;宽高动画中文字、Canvas 默认直接跳到终点,需配合 renderFit 跟随变化
组件内转场 组件出现或消失时,配合 if 条件渲染和 animateTo 触发 组件插入、移除时的过渡效果,如卡片显示与隐藏 .transition()、TransitionEffect 支持透明度、缩放、平移、旋转、边缘移入移出等效果;非对称转场用 asymmetric(进, 出) 分别设置进场和退场
页面间转场 页面切换时由系统或自定义配置触发 页面跳转、返回时的过渡动画 NavDestination.systemTransition 页面级动画,与组件内转场作用范围不同;自定义时需注意与系统默认转场的协调
关键帧动画 通过 keyframeAnimateTo 分段控制动画过程 一个动画需要分多段、每段使用不同曲线或时长的场景 keyframeAnimateTo() 适合复杂节奏的动画编排;每段关键帧的时长、曲线需单独配置
路径动画 通过 .motionPath() 指定路径,组件沿路径移动 组件沿自定义曲线、折线等路径运动的场景 .motionPath() 路径坐标需要提前规划好;路径动画通常只影响位置,不改变组件其他属性

简单来说:属性动画和显式动画解决的是「属性变化要不要动」的问题,前者声明一次、后者每次主动写;组件内转场和页面间转场解决的是「组件或页面出现/消失」的问题;关键帧动画和路径动画则偏向更精细的动画编排,分别控制分段节奏和运动轨迹。

对称转场和非对称转场

对称

Text('Hello')
.transition(
TransitionEffect.translate({ x: 200 })
.combine(TransitionEffect.opacity(0))
.animation({ duration: 500 })
)

进场与退场效果相同

.combine 的作用是将两个效果重叠起来

非对称

if (this.showCard) {
Text('卡片')
.transition(
TransitionEffect.asymmetric(
// 第一个参数:进场效果
TransitionEffect.opacity(0)
.combine(TransitionEffect.scale({ x: 0.5, y: 0.5 }))
.animation({ duration: 500 }),

// 第二个参数:退场效果
TransitionEffect.move(TransitionEdge.BOTTOM)
.animation({ duration: 400 })
)
)
}

1. 参数顺序:asymmetric(进, 出) 第一个是插入效果,第二个是退场效果,
2. 时长可以不一样:进场 500ms、退场 400ms,各自独立,互不影响

成果

@Entry
@Component
struct AnimateToDemo {
@State showCard: boolean = true

build() {
Column({ space: 20 }) {

if (this.showCard) {
Column() {
Text('我是卡片')
.fontSize(20)
.fontColor(Color.White)
}
.width(150)
.height(150)
.backgroundColor('#34C759')
.borderRadius(20)
.justifyContent(FlexAlign.Center)
.transition(
TransitionEffect.asymmetric(
TransitionEffect.opacity(0)
.combine(TransitionEffect.scale({ x: 0.5, y: 0.5 }))
.animation({ duration: 500, curve: Curve.EaseOut }),
TransitionEffect.move(TransitionEdge.BOTTOM)
.animation({ duration: 400, curve: Curve.EaseIn })
)
)
}

Button('隐藏')
.width(220)
.fontSize(16)
.backgroundColor('#FF3B30')
.onClick(() => {
animateTo({ duration: 500 }, () => {
this.showCard = false
})
})

Button('显示')
.width(220)
.fontSize(16)
.backgroundColor('#007AFF')
.onClick(() => {
animateTo({ duration: 500 }, () => {
this.showCard = true
})
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}

转场加

粒子动画

Particle:粒子组件,鸿蒙用来制作粒子动画的UI组件,可以把它想成装粒子的大盒子。
emitter:发射器,定义粒子从哪里产生、以何种方式生成,容易理解的想成喷泉的喷头。
particle:粒子本身,控制粒子长什么样子
color:粒子颜色,设置粒子的色彩
acceleration:加速度,控制粒子飞行方向与运动速度,
opacity:透明度变化,控制粒子透明程度的变化
updater:更新器,让粒子属性可以随时间自动变化
config:配置,给功能填写各项参数
disturbanceFields:扰动场,给粒子施加外力

代码骨架

@Entry
@Component
struct XxxEffect {
build() {
Stack() {
// 背景
Text().width('100%').height('100%').backgroundColor(Color.Black)

// 粒子组件
Particle({
particles: [
{
emitter: { /* 发射器 */ },
color: { /* 颜色 */ },
opacity: { /* 透明度 */ },
acceleration: { /* 加速度 */ }
}
]
})
.width('100%')
.height('100%')
}
.width('100%')
.height('100%')
}
}

1.发射器里面的具体内容代码骨架

emitter: {
particle: {
type: ParticleType.POINT, // 粒子类型:圆点
config: { radius: 4 }, // 圆点半径
count: 200, // 同屏粒子总数
lifetime: 8000, // 每个粒子存活毫秒数
lifetimeRange: 2000 // 存活时间浮动范围
},
emitRate: 30, // 每秒发射多少个
position: [0, -50], // 发射起点坐标
shape: ParticleEmitterShape.RECTANGLE, // 发射器形状
size: ['100%', 50] // 发射窗口大小
}

2.颜色代码骨架及注释

color: {
range: ['#ffffff', '#e0e8ff'] // 从这个范围里取色
}

3.透明度基本骨架

opacity: {
range: [0.0, 1.0],
updater: {
type: ParticleUpdater.CURVE,
config: [
{
from: 0.0, // 从透明
to: 1.0, // 到不透明
startMillis: 0,
endMillis: 1000,
curve: Curve.EaseIn
},
{
from: 1.0,
to: 0.0, // 到透明
startMillis: 6000,
endMillis: 8000,
curve: Curve.EaseOut
}
]
}
}

from/to = 从哪变到哪;startMillis/endMillis = 第几毫秒到第几毫秒完成。

4.加速度模版

acceleration: {
speed: {
range: [8, 18], // 速度范围 vp/秒
updater: {
type: ParticleUpdater.RANDOM, // 随机变化
config: [2, 8] // 每秒随机增减 2~8
}
},
angle: {
range: [80, 100] // 角度:0右 90下 180左 270上
}
}

关于粒子动画学习本人见解

骨架性比较强 有种套公式的感觉

学习成果

@Entry
@Component
struct SnowEffect {
build() {
Stack() {
Text()
.width('100%')
.height('100%')
.backgroundColor('#1a1a2e')

Particle({
particles: [
{
emitter: {
particle: {
type: ParticleType.POINT,
config: {
radius: 4
},
count: 200,
lifetime: 800000,
lifetimeRange: 2000
},
emitRate: 30,
position: [0, -50],
shape: ParticleEmitterShape.RECTANGLE,
size: ['100%', 50]
},
color: {
range: ['#ffffff', '#e0e8ff']
},
opacity: {
range: [0.0, 1.0],
updater: {
type: ParticleUpdater.CURVE,
config: [
{
from: 0.0,
to: 1.0,
startMillis: 0,
endMillis: 1000,
curve: Curve.EaseIn
},
{
from: 1.0,
to: 0.0,
startMillis: 6000,
endMillis: 8000,
curve: Curve.EaseOut
}
]
}
},
acceleration: {
speed: {
range: [8, 18],
updater: {
type: ParticleUpdater.RANDOM,
config: [2, 8]
}
},
angle: {
range: [80, 100]
}
}
}
]
})
.width('100%')
.height('100%')
}
.width('100%')
.height('100%')
}
}

粒子

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐