温馨提示:本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦!
HarmonyOS NEXT 动画系统详解:构建流畅的用户体验
1. 动画系统概述
1.1 动画类型
类型 | 说明 | 适用场景 |
---|---|---|
属性动画 | 改变组件属性值 | 大小、位置、透明度变化 |
转场动画 | 页面切换效果 | 页面跳转、弹窗显示 |
手势动画 | 跟随手势变化 | 拖拽、滑动、缩放 |
1.2 动画属性
// 可动画化的属性
interface AnimatableProperties {
opacity?: number;
rotate?: number;
scale?: number;
translate?: [number, number];
width?: number;
height?: number;
// ... 其他属性
}
2. 基础动画实现
2.1 属性动画
@Component
struct BasicAnimationDemo {
@State private scale: number = 1;
build() {
Column() {
Button('Animate')
.animation({
duration: 300,
curve: Curve.EaseInOut,
delay: 0,
iterations: 1,
playMode: PlayMode.Normal
})
.scale(this.scale)
.onClick(() => {
this.scale = this.scale === 1 ? 1.5 : 1;
})
}
}
}
2.2 显式动画
@Component
struct ExplicitAnimationDemo {
@State private opacity: number = 1;
build() {
Column() {
Text('Fade Animation')
.opacity(this.opacity)
.onClick(() => {
animateTo({
duration: 500,
curve: Curve.Smooth,
}, () => {
this.opacity = this.opacity === 1 ? 0 : 1;
})
})
}
}
}
3. 复杂动画处理
3.1 组合动画
@Component
struct CompositeAnimationDemo {
@State private transform: {
scale: number,
rotate: number,
translate: [number, number]
} = {
scale: 1,
rotate: 0,
translate: [0, 0]
};
build() {
Column() {
Image('icon.png')
.transform(this.transform)
.animation({
duration: 500,
curve: Curve.Smooth
})
.onClick(() => {
this.transform = {
scale: 1.2,
rotate: 45,
translate: [100, 100]
};
})
}
}
}
3.2 序列动画
class AnimationSequence {
private animations: Array<() => Promise<void>> = [];
addAnimation(animation: () => Promise<void>) {
this.animations.push(animation);
}
async play() {
for (const animation of this.animations) {
await animation();
}
}
}
@Component
struct SequentialAnimationDemo {
private sequence = new AnimationSequence();
@State private position: number = 0;
aboutToAppear() {
this.sequence.addAnimation(() =>
this.animate(() => this.position = 100));
this.sequence.addAnimation(() =>
this.animate(() => this.position = 200));
this.sequence.addAnimation(() =>
this.animate(() => this.position = 0));
}
private animate(update: () => void): Promise<void> {
return new Promise(resolve => {
animateTo({ duration: 500 }, () => {
update();
resolve();
});
});
}
build() {
Column() {
Button('Play Sequence')
.onClick(() => this.sequence.play())
Circle()
.size({ width: 50, height: 50 })
.offset({ x: this.position })
.animation({
duration: 500,
curve: Curve.Smooth
})
}
}
}
4. 性能优化
4.1 动画性能优化
@Component
struct PerformanceOptimizedAnimation {
// 1. 使用transform代替位置属性
@State private transform: {
translate: [number, number]
} = {
translate: [0, 0]
};
// 2. 避免在动画中改变布局
build() {
Stack() {
// 使用固定大小的容器
Column()
.width('100%')
.height('100%')
// 动画元素使用绝对定位
Image('icon.png')
.position({
x: 0,
y: 0
})
.transform(this.transform)
.animation({
duration: 300
})
}
}
}
4.2 动画帧率优化
class AnimationOptimizer {
private static readonly TARGET_FPS = 60;
private static readonly FRAME_TIME = 1000 / this.TARGET_FPS;
// 使用requestAnimationFrame优化动画
static animate(
duration: number,
onFrame: (progress: number) => void,
onComplete?: () => void
) {
const startTime = Date.now();
const update = () => {
const currentTime = Date.now();
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
onFrame(progress);
if (progress < 1) {
requestAnimationFrame(update);
} else if (onComplete) {
onComplete();
}
};
requestAnimationFrame(update);
}
}
5. 实战案例
5.1 卡片翻转动画
@Component
struct CardFlipAnimation {
@State private isFlipped: boolean = false;
@State private rotateY: number = 0;
build() {
Stack() {
// 正面
Column()
.width('100%')
.height('100%')
.backgroundColor('#FF0000')
.opacity(this.rotateY <= 90 ? 1 : 0)
.rotate({ y: this.rotateY })
// 背面
Column()
.width('100%')
.height('100%')
.backgroundColor('#00FF00')
.opacity(this.rotateY > 90 ? 1 : 0)
.rotate({ y: this.rotateY - 180 })
}
.animation({
duration: 500,
curve: Curve.Smooth
})
.onClick(() => {
this.rotateY = this.isFlipped ? 0 : 180;
this.isFlipped = !this.isFlipped;
})
}
}
5.2 列表项动画
@Component
struct AnimatedListDemo {
@State private items: Array<any> = [];
build() {
List() {
ForEach(this.items, (item, index) => {
ListItem() {
Text(item.title)
.opacity(0)
.animation({
delay: index * 100, // 错开动画
duration: 300,
curve: Curve.EaseOut
})
.onAppear(() => {
animateTo({ duration: 300 }, () => {
item.opacity = 1;
})
})
}
})
}
}
}
5.3 路由转场动画
@Entry
@Component
struct PageTransitionDemo {
@State private currentPage: number = 1;
build() {
Stack() {
if (this.currentPage === 1) {
Page1()
.transition({
type: TransitionType.Push,
direction: TransitionDirection.Left
})
} else {
Page2()
.transition({
type: TransitionType.Push,
direction: TransitionDirection.Left
})
}
}
}
}
5.4 最佳实践建议
性能优化
- 使用transform代替位置属性
- 避免动画过程中改变布局
- 合理使用硬件加速
动画设计
- 保持动画简单明确
- 使用适当的缓动函数
- 控制动画时长
用户体验
- 提供适当的反馈
- 保持动画流畅
- 避免过度动画
代码组织
- 封装可复用的动画
- 使用动画管理器
- 实现动画配置系统
通过合理使用动画系统,可以显著提升应用的用户体验。在实际开发中,要注意平衡动画效果和性能消耗,确保动画流畅自然,同时不影响应用的整体性能。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。