0
点赞
收藏
分享

微信扫一扫

HarmonyOS 5 高级动效实战:粒子系统、路径动画与物理动效开发

一、粒子系统动画开发

粒子动画通过大量微小元素的运动创造复杂视觉效果,如雨雪、火焰、爆炸等自然现象。

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应用中创建电影级的视觉体验,从简单的粒子效果到复杂的物理模拟,为用户提供沉浸式的交互体验。

需要参加鸿蒙认证的请点击 鸿蒙认证链接

举报

相关推荐

0 条评论