一、粒子系统动画开发
粒子动画通过大量微小元素的运动创造复杂视觉效果,如雨雪、火焰、爆炸等自然现象。
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应用中创建电影级的视觉体验,从简单的粒子效果到复杂的物理模拟,为用户提供沉浸式的交互体验。
需要参加鸿蒙认证的请点击 鸿蒙认证链接