HarmonyOS鸿蒙Next中怎样给组件添加显隐动画?
HarmonyOS鸿蒙Next中怎样给组件添加显隐动画?
如何实现流畅的页面转场动画?
如何给组件添加显隐动画?
如何实现数字变化的动画效果?
4 回复
实现代码
/**
* 组件显隐动画
*/
@Component
struct AnimationDemo {
[@State](/user/State) visible: boolean = false;
build() {
Column({ space: 20 }) {
Button('切换显示')
.onClick(() => {
animateTo({
duration: 300,
curve: Curve.EaseInOut
}, () => {
this.visible = !this.visible;
})
})
if (this.visible) {
Column() {
Text('动画内容')
.fontSize(18)
}
.width(200)
.height(100)
.backgroundColor('#ff6b6b')
.borderRadius(12)
.transition({
type: TransitionType.Insert,
opacity: 0,
translate: { y: -50 }
})
.transition({
type: TransitionType.Delete,
opacity: 0,
translate: { y: 50 }
})
}
}
.padding(20)
}
}
/**
* 数字变化动画
*/
@Component
struct NumberAnimationDemo {
[@State](/user/State) number: number = 0;
[@State](/user/State) displayNumber: number = 0;
private timer: number = -1;
animateNumber(target: number) {
const start = this.displayNumber;
const diff = target - start;
const duration = 1000;
const steps = 60;
const stepValue = diff / steps;
let currentStep = 0;
if (this.timer >= 0) {
clearInterval(this.timer);
}
this.timer = setInterval(() => {
currentStep++;
if (currentStep >= steps) {
this.displayNumber = target;
clearInterval(this.timer);
this.timer = -1;
} else {
this.displayNumber = start + stepValue * currentStep;
}
}, duration / steps);
}
aboutToDisappear() {
if (this.timer >= 0) {
clearInterval(this.timer);
}
}
build() {
Column({ space: 20 }) {
Text(`¥${this.displayNumber.toFixed(2)}`)
.fontSize(48)
.fontWeight(FontWeight.Bold)
.fontColor('#ff6b6b')
Row({ space: 12 }) {
Button('增加1000')
.onClick(() => {
this.number += 1000;
this.animateNumber(this.number);
})
Button('减少500')
.onClick(() => {
this.number -= 500;
this.animateNumber(this.number);
})
}
}
.padding(20)
}
}
/**
* 列表项动画
*/
@Component
struct ListItemAnimation {
[@State](/user/State) items: string[] = ['项目1', '项目2', '项目3'];
build() {
Column({ space: 12 }) {
Button('添加项目')
.onClick(() => {
animateTo({ duration: 300 }, () => {
this.items.push(`项目${this.items.length + 1}`);
})
})
List({ space: 8 }) {
ForEach(this.items, (item: string, index: number) => {
ListItem() {
Row() {
Text(item)
.fontSize(16)
.layoutWeight(1)
Button('删除')
.fontSize(14)
.backgroundColor('#ff6b6b')
.onClick(() => {
animateTo({ duration: 300 }, () => {
this.items.splice(index, 1);
})
})
}
.width('100%')
.padding(16)
.backgroundColor(Color.White)
.borderRadius(8)
}
.transition({
type: TransitionType.All,
opacity: 0,
translate: { x: -100 }
})
})
}
.layoutWeight(1)
}
.padding(16)
.width('100%')
.height('100%')
}
}
/**
* 旋转加载动画
*/
@Component
struct RotateAnimation {
[@State](/user/State) angle: number = 0;
private timer: number = -1;
startRotate() {
this.timer = setInterval(() => {
animateTo({ duration: 1000, curve: Curve.Linear }, () => {
this.angle += 360;
})
}, 1000);
}
stopRotate() {
if (this.timer >= 0) {
clearInterval(this.timer);
this.timer = -1;
}
}
aboutToAppear() {
this.startRotate();
}
aboutToDisappear() {
this.stopRotate();
}
build() {
Column() {
Image($r('app.media.icon'))
.width(50)
.height(50)
.rotate({ angle: this.angle })
}
}
}
使用示例
// 使用组件动画
animateTo({ duration: 300, curve: Curve.EaseInOut }, () => {
this.visible = !this.visible;
});
// 使用数字动画
this.animateNumber(5000);
// 列表添加动画
animateTo({ duration: 300 }, () => {
this.items.push('新项目');
});
原理解析
1. animateTo闭包动画
animateTo({ duration: 300, curve: Curve.EaseInOut }, () => {
this.visible = !this.visible; // 状态变化会触发动画
})
- 闭包内的状态变化会产生动画
- duration指定动画时长(毫秒)
- curve指定动画曲线
2. transition转场
.transition({
type: TransitionType.Insert,
opacity: 0,
translate: { y: -50 }
})
- Insert:组件插入时的动画
- Delete:组件删除时的动画
- All:插入和删除都使用相同动画
3. 数字动画原理
- 使用setInterval逐帧更新数字
- 计算每帧的增量(目标值-当前值)/帧数
- 达到目标值后清除定时器
- 组件销毁时必须清除定时器
4. 常用动画曲线
- Curve.Linear:线性,匀速
- Curve.EaseInOut:先加速后减速,最自然
- Curve.Friction:摩擦力,有弹性
- Curve.Sharp:快速开始和结束
最佳实践
- 动画时长: 通常使用300ms,过长会显得拖沓
- 动画曲线: EaseInOut最自然,Friction有弹性效果
- 性能: 避免同时执行大量动画
- 状态管理: 动画相关状态用@State
- 清理资源: 组件销毁时清除定时器
避坑指南
- 忘记animateTo: 直接修改状态不会有动画
- transition位置: transition要放在组件上,不是容器
- 定时器泄漏: 忘记clearInterval导致内存泄漏
- 动画冲突: 同一属性不要同时执行多个动画
- 性能问题: 列表项过多时避免使用transition
在HarmonyOS Next中,使用显隐动画可通过组件的transition方法实现。通过设置组件的opacity属性,结合animateTo函数,可以控制组件的淡入淡出效果。例如,使用animateTo改变opacity从0到1实现显示动画,从1到0实现隐藏动画。
在HarmonyOS Next中,给组件添加显隐动画主要通过ArkUI的显示动画接口实现,核心是使用animateTo函数与组件状态绑定。
1. 显隐动画实现方式:
// 通过状态控制显隐并添加动画
@State isVisible: boolean = true
animateTo({
duration: 300, // 动画时长
curve: Curve.EaseInOut // 动画曲线
}, () => {
this.isVisible = !this.isVisible // 切换状态
})
2. 组件代码示例:
if (this.isVisible) {
Text('显示/隐藏的文本')
.opacity(this.isVisible ? 1 : 0) // 透明度变化
.scale({ x: this.isVisible ? 1 : 0.5, y: this.isVisible ? 1 : 0.5 }) // 缩放效果
}
3. 页面转场动画:
使用Navigation或自定义转场:
// 页面跳转带动画
router.pushUrl({
url: 'pages/NextPage',
params: { data: 'test' }
}, router.RouterMode.Standard, (err) => {
// 转场完成回调
})
4. 数字变化动画:
@State count: number = 0
// 数字递增动画
animateTo({
duration: 1000
}, () => {
this.count += 100
})
关键点:
- 使用
@State装饰器管理动画状态 animateTo支持duration、curve、delay等参数配置- 可组合opacity、scale、translate等变换属性
- 系统提供多种预置曲线:Ease、Linear、Spring等
通过状态驱动UI更新的机制,配合animateTo动画函数,能够实现流畅的组件显隐、页面转场和数值动画效果。

