一、粒子系统动画开发
粒子动画通过大量微小元素的运动创造复杂视觉效果,如雨雪、火焰、爆炸等自然现象。
1. 基础粒子系统实现
import particle from '@ohos.graphics.particle';
import { BusinessError } from '@ohos.base';
@Component
struct ParticleAnimationExample {
@State particles: Array<ParticleData> = [];
private animationFrame: number = 0;
private readonly particleCount: number = 100;
aboutToAppear() {
this.initParticles();
this.startAnimation();
}
aboutToDisappear() {
this.stopAnimation();
}
// 初始化粒子数据
private initParticles(): void {
for (let i = 0; i < this.particleCount; i++) {
this.particles.push({
id: i,
x: Math.random() * 360,
y: Math.random() * 640,
size: Math.random() * 5 + 1,
color: this.getRandomColor(),
velocityX: (Math.random() - 0.5) * 2,
velocityY: Math.random() * 3 + 1,
rotation: Math.random() * 360,
life: 1.0,
decay: Math.random() * 0.01 + 0.005
});
}
}
// 获取随机颜色
private getRandomColor(): string {
const colors = ['#FF5252', '#FF4081', '#E040FB', '#7C4DFF', '#536DFE', '#448AFF', '#40C4FF', '#18FFFF'];
return colors[Math.floor(Math.random() * colors.length)];
}
// 启动动画循环
private startAnimation(): void {
const animate = () => {
this.updateParticles();
this.animationFrame = requestAnimationFrame(animate);
};
this.animationFrame = requestAnimationFrame(animate);
}
// 停止动画
private stopAnimation(): void {
if (this.animationFrame) {
cancelAnimationFrame(this.AnimationFrame);
}
}
// 更新粒子状态
private updateParticles(): void {
for (let i = 0; i < this.particles.length; i++) {
const particle = this.particles[i];
// 更新位置
particle.x += particle.velocityX;
particle.y += particle.velocityY;
// 更新生命周期
particle.life -= particle.decay;
// 重置死亡粒子
if (particle.life <= 0 || particle.y > 640) {
particle.x = Math.random() * 360;
particle.y = -10;
particle.life = 1.0;
particle.velocityX = (Math.random() - 0.5) * 2;
particle.velocityY = Math.random() * 3 + 1;
}
}
}
build() {
Canvas(this.getContext())
.width('100%')
.height('100%')
.onReady(() => {
this.drawParticles();
})
}
// 绘制粒子
private drawParticles(): void {
const context = this.getContext();
context.clearRect(0, 0, 360, 640);
this.particles.forEach(particle => {
if (particle.life > 0) {
context.globalAlpha = particle.life;
context.beginPath();
context.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2);
context.fillStyle = particle.color;
context.fill();
}
});
context.globalAlpha = 1.0;
}
}
interface ParticleData {
id: number;
x: number;
y: number;
size: number;
color: string;
velocityX: number;
velocityY: number;
rotation: number;
life: number;
decay: number;
}
2. 高级粒子发射器
创建可配置的粒子发射器系统:
class ParticleEmitter {
private particles: ParticleData[] = [];
private isRunning: boolean = false;
private emissionRate: number = 10; // 每秒发射粒子数
private lastEmissionTime: number = 0;
// 发射器配置
config: EmitterConfig = {
position: { x: 180, y: 320 },
spread: 45, // 发射角度范围
minVelocity: 1,
maxVelocity: 5,
minSize: 1,
maxSize: 8,
minLife: 0.5,
maxLife: 2.0,
colors: ['#FF5722', '#FF9800', '#FFEB3B', '#4CAF50', '#2196F3'],
emissionType: 'continuous' // continuous 或 burst
};
// 发射粒子
emit(): void {
const now = Date.now();
const elapsed = now - this.lastEmissionTime;
const particlesToEmit = Math.floor((elapsed / 1000) * this.emissionRate);
for (let i = 0; i < particlesToEmit; i++) {
const angle = (Math.random() * this.config.spread - this.config.spread / 2) * Math.PI / 180;
const velocity = Math.random() * (this.config.maxVelocity - this.config.minVelocity) + this.config.minVelocity;
this.particles.push({
id: Date.now() + i,
x: this.config.position.x,
y: this.config.position.y,
size: Math.random() * (this.config.maxSize - this.config.minSize) + this.config.minSize,
color: this.config.colors[Math.floor(Math.random() * this.config.colors.length)],
velocityX: Math.cos(angle) * velocity,
velocityY: Math.sin(angle) * velocity,
rotation: Math.random() * 360,
life: Math.random() * (this.config.maxLife - this.config.minLife) + this.config.minLife,
decay: 1 / (this.config.maxLife * 1000)
});
}
this.lastEmissionTime = now;
}
// 更新所有粒子
update(): void {
for (let i = this.particles.length - 1; i >= 0; i--) {
const particle = this.particles[i];
particle.x += particle.velocityX;
particle.y += particle.velocityY;
particle.life -= particle.decay;
// 移除死亡粒子
if (particle.life <= 0) {
this.particles.splice(i, 1);
}
}
if (this.isRunning && this.config.emissionType === 'continuous') {
this.emit();
}
}
// 爆发式发射
burst(count: number): void {
for (let i = 0; i < count; i++) {
this.emit();
}
}
start(): void {
this.isRunning = true;
this.lastEmissionTime = Date.now();
}
stop(): void {
this.isRunning = false;
}
getParticles(): ParticleData[] {
return this.particles;
}
}
二、路径动画高级应用
路径动画让元素沿预定轨迹运动,适合创建复杂的运动效果。
1. 贝塞尔曲线路径动画
@Component
struct BezierPathAnimation {
@State position: { x: number, y: number } = { x: 0, y: 0 };
@State progress: number = 0;
private animationController: animator.AnimatorResult | null = null;
// 计算贝塞尔曲线点
private calculateBezierPoint(t: number, p0: number, p1: number, p2: number, p3: number): number {
const u = 1 - t;
const tt = t * t;
const uu = u * u;
const uuu = uu * u;
const ttt = tt * t;
return uuu * p0 + 3 * uu * t * p1 + 3 * u * tt * p2 + ttt * p3;
}
// 获取路径上的点
private getPathPoint(progress: number): { x: number, y: number } {
// 定义贝塞尔曲线控制点
const p0 = { x: 50, y: 500 };
const p1 = { x: 150, y: 100 };
const p2 = { x: 250, y: 100 };
const p3 = { x: 350, y: 500 };
return {
x: this.calculateBezierPoint(progress, p0.x, p1.x, p2.x, p3.x),
y: this.calculateBezierPoint(progress, p0.y, p1.y, p2.y, p3.y)
};
}
// 开始路径动画
startPathAnimation(): void {
this.animationController = animateTo({
duration: 3000,
curve: Curve.EaseInOut,
iterations: -1, // 无限循环
playMode: PlayMode.Alternate // 往返运动
}, () => {
this.progress = 1;
});
}
build() {
Stack() {
// 运动轨迹可视化
Path()
.commands('M50 500 C150 100, 250 100, 350 500')
.strokeWidth(2)
.stroke(Color.Gray)
.fill(Color.Transparent)
// 运动物体
Circle()
.width(30)
.height(30)
.fill(Color.Red)
.position({ x: this.position.x - 15, y: this.position.y - 15 })
.onClick(() => {
this.startPathAnimation();
})
}
.onFrame(() => {
// 每帧更新位置
this.position = this.getPathPoint(this.progress);
})
.width('100%')
.height('100%')
}
}
2. 复杂路径动画系统
创建可配置的路径动画系统:
class PathAnimationSystem {
private paths: AnimationPath[] = [];
private activeAnimations: Map<string, PathAnimation> = new Map();
// 添加路径
addPath(id: string, path: AnimationPath): void {
this.paths.push({ ...path, id });
}
// 创建路径动画
createAnimation(pathId: string, target: any, property: string): PathAnimation {
const path = this.paths.find(p => p.id === pathId);
if (!path) {
throw new Error(`Path ${pathId} not found`);
}
const animation: PathAnimation = {
id: `${pathId}-${Date.now()}`,
path,
target,
property,
progress: 0,
speed: 0.01,
isPlaying: false
};
this.activeAnimations.set(animation.id, animation);
return animation;
}
// 更新动画
update(): void {
for (const [id, animation] of this.activeAnimations) {
if (animation.isPlaying) {
animation.progress += animation.speed;
if (animation.progress > 1) {
animation.progress = 0;
}
const point = this.calculatePathPoint(animation.path, animation.progress);
animation.target[animation.property] = point;
}
}
}
// 计算路径点
private calculatePathPoint(path: AnimationPath, progress: number): any {
switch (path.type) {
case 'linear':
return this.calculateLinearPath(path, progress);
case 'bezier':
return this.calculateBezierPath(path, progress);
case 'circle':
return this.calculateCircularPath(path, progress);
default:
throw new Error(`Unknown path type: ${path.type}`);
}
}
private calculateLinearPath(path: LinearPath, progress: number): { x: number, y: number } {
return {
x: path.start.x + (path.end.x - path.start.x) * progress,
y: path.start.y + (path.end.y - path.start.y) * progress
};
}
private calculateBezierPath(path: BezierPath, progress: number): { x: number, y: number } {
// 三阶贝塞尔曲线计算
const u = 1 - progress;
const tt = progress * progress;
const uu = u * u;
const uuu = uu * u;
const ttt = tt * progress;
return {
x: uuu * path.p0.x + 3 * uu * progress * path.p1.x + 3 * u * tt * path.p2.x + ttt * path.p3.x,
y: uuu * path.p0.y + 3 * uu * progress * path.p1.y + 3 * u * tt * path.p2.y + ttt * path.p3.y
};
}
private calculateCircularPath(path: CircularPath, progress: number): { x: number, y: number } {
const angle = progress * Math.PI * 2;
return {
x: path.center.x + Math.cos(angle) * path.radius,
y: path.center.y + Math.sin(angle) * path.radius
};
}
}
三、物理动效与高级交互
1. 弹簧物理动画
实现基于物理的弹簧动画效果:
@Component
struct SpringPhysicsAnimation {
@State position: number = 0;
@State velocity: number = 0;
@State targetPosition: number = 100;
private springConstant: number = 0.1;
private damping: number = 0.8;
private animationFrame: number = 0;
// 更新物理模拟
updatePhysics(): void {
const displacement = this.targetPosition - this.position;
const acceleration = displacement * this.springConstant - this.velocity * this.damping;
this.velocity += acceleration;
this.position += this.velocity;
// 当速度足够小时停止动画
if (Math.abs(this.velocity) < 0.1 && Math.abs(displacement) < 0.1) {
this.stopAnimation();
this.position = this.targetPosition;
}
}
startAnimation(): void {
this.velocity = 0;
const animate = () => {
this.updatePhysics();
this.animationFrame = requestAnimationFrame(animate);
};
this.animationFrame = requestAnimationFrame(animate);
}
stopAnimation(): void {
if (this.animationFrame) {
cancelAnimationFrame(this.animationFrame);
}
}
build() {
Column() {
// 弹簧动画演示
Rectangle()
.width(50)
.height(50)
.backgroundColor(Color.Red)
.translate({ x: this.position })
.onClick(() => {
this.targetPosition = this.targetPosition === 100 ? 200 : 100;
this.startAnimation();
})
Slider({ value: this.springConstant, min: 0.01, max: 0.5 })
.onChange((value: number) => {
this.springConstant = value;
})
.margin({ top: 20 })
Slider({ value: this.damping, min: 0.1, max: 0.95 })
.onChange((value: number) => {
this.damping = value;
})
.margin({ top: 10 })
}
.padding(20)
}
}
2. 碰撞检测与物理交互
实现简单的物理碰撞系统:
@Component
struct PhysicsInteractionExample {
@State balls: PhysicalBall[] = [];
private gravity: number = 0.2;
private animationFrame: number = 0;
aboutToAppear() {
this.initBalls();
this.startPhysicsSimulation();
}
aboutToDisappear() {
this.stopPhysicsSimulation();
}
private initBalls(): void {
for (let i = 0; i < 5; i++) {
this.balls.push({
id: i,
x: Math.random() * 300,
y: Math.random() * 200,
radius: Math.random() * 20 + 10,
velocityX: (Math.random() - 0.5) * 4,
velocityY: (Math.random() - 0.5) * 4,
color: this.getRandomColor()
});
}
}
private startPhysicsSimulation(): void {
const animate = () => {
this.updatePhysics();
this.animationFrame = requestAnimationFrame(animate);
};
this.animationFrame = requestAnimationFrame(animate);
}
private stopPhysicsSimulation(): void {
if (this.animationFrame) {
cancelAnimationFrame(this.animationFrame);
}
}
private updatePhysics(): void {
for (let i = 0; i < this.balls.length; i++) {
const ball = this.balls[i];
// 应用重力
ball.velocityY += this.gravity;
// 更新位置
ball.x += ball.velocityX;
ball.y += ball.velocityY;
// 边界碰撞检测
if (ball.x - ball.radius < 0) {
ball.x = ball.radius;
ball.velocityX *= -0.8;
} else if (ball.x + ball.radius > 360) {
ball.x = 360 - ball.radius;
ball.velocityX *= -0.8;
}
if (ball.y - ball.radius < 0) {
ball.y = ball.radius;
ball.velocityY *= -0.8;
} else if (ball.y + ball.radius > 600) {
ball.y = 600 - ball.radius;
ball.velocityY *= -0.8;
}
// 球体间碰撞检测
for (let j = i + 1; j < this.balls.length; j++) {
this.checkBallCollision(ball, this.balls[j]);
}
}
}
private checkBallCollision(ball1: PhysicalBall, ball2: PhysicalBall): void {
const dx = ball2.x - ball1.x;
const dy = ball2.y - ball1.y;
const distance = Math.sqrt(dx * dx + dy * dy);
const minDistance = ball1.radius + ball2.radius;
if (distance < minDistance) {
// 碰撞响应
const angle = Math.atan2(dy, dx);
const overlap = minDistance - distance;
// 位置修正
ball1.x -= overlap * Math.cos(angle) * 0.5;
ball1.y -= overlap * Math.sin(angle) * 0.5;
ball2.x += overlap * Math.cos(angle) * 0.5;
ball2.y += overlap * Math.sin(angle) * 0.5;
// 速度交换
const tempVx = ball1.velocityX;
const tempVy = ball1.velocityY;
ball1.velocityX = ball2.velocityX * 0.9;
ball1.velocityY = ball2.velocityY * 0.9;
ball2.velocityX = tempVx * 0.9;
ball2.velocityY = tempVy * 0.9;
}
}
build() {
Canvas(this.getContext())
.width('100%')
.height('100%')
.onReady(() => {
this.drawBalls();
})
}
private drawBalls(): void {
const context = this.getContext();
context.clearRect(0, 0, 360, 600);
this.balls.forEach(ball => {
context.beginPath();
context.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
context.fillStyle = ball.color;
context.fill();
context.strokeStyle = '#000';
context.lineWidth = 2;
context.stroke();
});
}
}
四、高级动画组合与序列
1. 复杂动画序列控制
创建可编排的动画序列系统:
class AnimationSequence {
private animations: AnimationStep[] = [];
private currentStep: number = 0;
private isPlaying: boolean = false;
private onComplete: (() => void) | null = null;
// 添加动画步骤
addStep(animation: AnimationStep): AnimationSequence {
this.animations.push(animation);
return this;
}
// 播放序列
play(): Promise<void> {
return new Promise((resolve) => {
this.onComplete = resolve;
this.isPlaying = true;
this.currentStep = 0;
this.playNextStep();
});
}
// 播放下一步
private playNextStep(): void {
if (this.currentStep >= this.animations.length) {
this.isPlaying = false;
if (this.onComplete) {
this.onComplete();
}
return;
}
const currentAnimation = this.animations[this.currentStep];
currentAnimation.animate().then(() => {
this.currentStep++;
this.playNextStep();
});
}
// 停止序列
stop(): void {
this.isPlaying = false;
this.currentStep = 0;
}
// 跳转到指定步骤
gotoStep(stepIndex: number): void {
if (stepIndex >= 0 && stepIndex < this.animations.length) {
this.currentStep = stepIndex;
}
}
}
// 使用示例
@Component
struct AnimationSequenceExample {
private sequence: AnimationSequence = new AnimationSequence();
aboutToAppear() {
// 构建动画序列
this.sequence
.addStep({
animate: () => this.fadeIn(),
description: '淡入动画'
})
.addStep({
animate: () => this.moveToCenter(),
description: '移动到中心'
})
.addStep({
animate: () => this.scaleUp(),
description: '放大效果'
})
.addStep({
animate: () => this.rotate(),
description: '旋转动画'
});
}
build() {
Column() {
Button('播放序列动画')
.onClick(() => {
this.sequence.play().then(() => {
console.info('动画序列完成');
});
})
}
}
}
2. 交互动画状态机
创建基于状态机的复杂交互动画系统:
class AnimationStateMachine {
private states: Map<string, AnimationState> = new Map();
private currentState: string = '';
private transitions: Map<string, TransitionRule[]> = new Map();
// 添加状态
addState(name: string, animation: () => Promise<void>): void {
this.states.set(name, { name, animation });
}
// 添加转场规则
addTransition(fromState: string, toState: string, condition: () => boolean): void {
if (!this.transitions.has(fromState)) {
this.transitions.set(fromState, []);
}
this.transitions.get(fromState)?.push({ fromState, toState, condition });
}
// 更新状态机
update(): void {
const currentTransitions = this.transitions.get(this.currentState);
if (!currentTransitions) return;
for (const transition of currentTransitions) {
if (transition.condition()) {
this.transitionTo(transition.toState);
break;
}
}
}
// 执行状态转场
private async transitionTo(newState: string): Promise<void> {
const targetState = this.states.get(newState);
if (!targetState) return;
console.info(`Transitioning from ${this.currentState} to ${newState}`);
this.currentState = newState;
try {
await targetState.animation();
} catch (error) {
console.error(`Animation failed for state ${newState}:`, error);
}
}
// 启动状态机
start(initialState: string): void {
this.currentState = initialState;
this.update();
}
}
// 使用示例
@Component
struct StateMachineExample {
private stateMachine: AnimationStateMachine = new AnimationStateMachine();
aboutToAppear() {
// 配置状态机
this.stateMachine.addState('idle', () => this.playIdleAnimation());
this.stateMachine.addState('hover', () => this.playHoverAnimation());
this.stateMachine.addState('active', () => this.playActiveAnimation());
this.stateMachine.addTransition('idle', 'hover', () => this.isHovering);
this.stateMachine.addTransition('hover', 'idle', () => !this.isHovering);
this.stateMachine.addTransition('hover', 'active', () => this.isPressed);
this.stateMachine.addTransition('active', 'idle', () => !this.isPressed);
this.stateMachine.start('idle');
}
build() {
// 组件实现
}
}
五、性能优化与最佳实践
1. 动画性能优化策略
确保复杂动画的流畅运行:
@Component
struct OptimizedAnimationSystem {
private readonly MAX_FPS: number = 60;
private readonly FRAME_TIME: number = 1000 / 60;
private lastUpdateTime: number = 0;
private animationFrame: number = 0;
// 优化的动画循环
startOptimizedAnimation(): void {
const animate = (timestamp: number) => {
const deltaTime = timestamp - this.lastUpdateTime;
// 控制帧率
if (deltaTime >= this.FRAME_TIME) {
this.updateAnimations(deltaTime);
this.render();
this.lastUpdateTime = timestamp;
}
this.animationFrame = requestAnimationFrame(animate);
};
this.animationFrame = requestAnimationFrame(animate);
}
// 批量更新动画
private updateAnimations(deltaTime: number): void {
// 使用对象池避免频繁内存分配
// 使用脏检查减少不必要的计算
// 使用空间分区优化碰撞检测
}
// 高效渲染
private render(): void {
// 使用脏矩形技术减少重绘区域
// 使用离屏Canvas缓存静态元素
// 使用GPU加速的变换和 opacity
}
// 内存管理
private setupMemoryManagement(): void {
// 对象池管理
// 纹理和资源缓存
// 垃圾回收优化
}
aboutToDisappear() {
if (this.animationFrame) {
cancelAnimationFrame(this.animationFrame);
}
}
}
2. 动画调试与分析
创建动画调试工具:
@Component
struct AnimationDebugger {
@State fps: number = 0;
@State memoryUsage: number = 0;
@State frameTime: number = 0;
private frameCount: number = 0;
private lastTime: number = 0;
build() {
Column() {
// 调试信息面板
Text(`FPS: ${this.fps}`)
.fontSize(14)
.fontColor(this.fps < 30 ? Color.Red : Color.Green)
Text(`帧时间: ${this.frameTime.toFixed(2)}ms`)
.fontSize(14)
Text(`内存使用: ${this.memoryUsage}MB`)
.fontSize(14)
// 性能图表
PerformanceChart()
.margin({ top: 20 })
}
.onAppear(() => {
this.startPerformanceMonitoring();
})
}
private startPerformanceMonitoring(): void {
const monitor = () => {
const now = Date.now();
this.frameCount++;
if (now - this.lastTime >= 1000) {
this.fps = Math.round((this.frameCount * 1000) / (now - this.lastTime));
this.memoryUsage = performance.memory ?
Math.round(performance.memory.usedJSHeapSize / 1048576) : 0;
this.frameCount = 0;
this.lastTime = now;
}
requestAnimationFrame(monitor);
};
this.lastTime = Date.now();
requestAnimationFrame(monitor);
}
}
通过掌握这些高级动画技术,你可以在HarmonyOS应用中创建电影级的视觉体验,从简单的粒子效果到复杂的物理模拟,为用户提供沉浸式的交互体验。
需要参加鸿蒙认证的请点击 鸿蒙认证链接