[鸿蒙2025领航者闯关] 动画与转场效果实现
如何实现流畅的页面转场动画?如何给组件添加显隐动画?如何实现数字变化的动画效果?: animateTo、transition、页面转场、组件动画、数字动画。
·
问题描述
如何实现流畅的页面转场动画?如何给组件添加显隐动画?如何实现数字变化的动画效果?
关键字: animateTo、transition、页面转场、组件动画、数字动画
解决方案
完整代码
/**
* 组件显隐动画
*/
@Component
struct AnimationDemo {
@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 number: number = 0;
@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 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 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
效果展示
- 组件动画:淡入淡出 + 位移,从上方滑入,向下方滑出
- 数字动画:平滑递增/递减,1 秒内完成变化
- 列表动画:添加/删除带动画效果,从左侧滑入
- 旋转动画:持续旋转,用于加载提示
更多推荐


所有评论(0)