HarmonyOS鸿蒙Next中如何实现粒子动画跟手效果

HarmonyOS鸿蒙Next中如何实现粒子动画跟手效果

如何实现粒子动画跟手效果

4 回复

【背景知识】

  • 粒子动画 (Particle):粒子动画是在一定范围内随机生成的大量粒子产生运动而组成的动画。
  • PanGesture:滑动手势事件,当滑动的最小距离达到设定的最小值时触发滑动手势事件。

【解决方案】

  • 通过PanGesture监听拖动事件,实时更新偏移并在结束时保存最终位置。
    PanGesture()
    .onActionUpdate((event: GestureEvent) => {
      // 动态调整粒子发射器的位置
      if (event && event.fingerList.length > 0) {
        this.positionX = event.fingerList[0].localX;
        this.positionY = event.fingerList[0].localY;
      }
    })
    
  • 使用Particle提供的emitter接口动态更新发射器位置。
    this.emitterProperties[0] = {
      index: 0,
      emitRate: 100,
      position: { x: this.positionX, y: this.positionY },
      size: { width: 20, height: 20 }
    }
    
  • 完整示例代码如下:
    @Entry
    @Component
    export default struct ParticleExample {
      @State imageWidth: number = 40;
      @State imageHeight: number = 40;
      positionX: number = 100;
      positionY: number = 0;
      @State emitterProperties: Array<EmitterProperty> = [
        {
          index: 0,
          emitRate: 0, // 每秒发射粒子数
          position: { x: this.positionX, y: this.positionY }, // 设置粒子发射器位置
          size: { width: 20, height: 20 }
        }
      ]
    
      build() {
        Column() {
          Stack() {
            Particle({
              particles: [
                {
                  emitter: {
                    particle: {
                      type: ParticleType.IMAGE, // 粒子类型
                      config: {
                        src: $r("app.media.startIcon"),
                        size: [this.imageWidth, this.imageHeight],
                        objectFit: ImageFit.Contain
                      },
                      lifetime: 1400, // 单个粒子的生命周期
                      lifetimeRange: 200, // 粒子生命周期取值范围
                      count: -1, // 粒子总数
                    },
                    emitRate: 0,
                    shape: ParticleEmitterShape.CIRCLE, // 发射器形状
                    size: [20, 20],
                    position: [100, 100]
                  },
                  color: {
                    range: [Color.White, Color.White]
                  },
                  opacity: {
                    range: [1.0, 1.0],
                    updater: {
                      type: ParticleUpdater.CURVE,
                      config: [
                        {
                          from: 1.0,
                          to: .0,
                          startMillis: 0,
                          endMillis: 1600,
                          curve: Curve.Linear
                        }
                      ]
                    }
                  },
                  scale: {
                    range: [1.0, 1.0],
                    updater: {
                      type: ParticleUpdater.CURVE,
                      config: []
                    }
                  },
                  acceleration: {
                    speed: {
                      range: [300, 400],
                      updater: {
                        type: ParticleUpdater.RANDOM,
                        config: [0, 0]
                      }
                    },
                    angle: {
                      range: [0, 360],
                      updater: {
                        type: ParticleUpdater.RANDOM,
                        config: [0, 0]
                      }
                    }
                  },
                  spin: {
                    range: [0, 360],
                    updater: {
                      type: ParticleUpdater.CURVE,
                      config: [
                        {
                          from: 0,
                          to: 360,
                          startMillis: 0,
                          endMillis: 1500,
                          curve: Curve.EaseIn
                        }
                      ]
                    }
                  },
                },
              ]
            })
              .width('100%').height('100%')
              .emitter(this.emitterProperties)
              .gesture(
                PanGesture()
                  .onActionStart((event: GestureEvent) => {
                  })
                  .onActionUpdate((event: GestureEvent) => {
                    // 动态调整粒子发射器的位置
                    if (event && event.fingerList.length > 0) {
                      this.positionX = event.fingerList[0].localX;
                      this.positionY = event.fingerList[0].localY;
                      this.emitterProperties[0] = {
                        index: 0,
                        emitRate: 100,
                        position: { x: this.positionX, y: this.positionY },
                        size: { width: 20, height: 20 }
                      }
                    }
                  })
                  .onActionEnd((event: GestureEvent) => {
                    this.emitterProperties[0] = {
                      index: 0,
                      emitRate: 0,
                      position: { x: this.positionX, y: this.positionY },
                      size: { width: 20, height: 20 }
                    }
                  })
              )
          }.width("100%")
          .height("100%")
          .align(Alignment.Center)
        }.width("100%")
        .height("100%")
      }
    }
    

更多关于HarmonyOS鸿蒙Next中如何实现粒子动画跟手效果的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


您好。

这是粒子动画的官方文档指南,请参考:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-particle-animation

如果要在粒子动画上实现手势效果,可能需要再参考下手势事件:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-gesture-events

希望可以帮助到您。

在HarmonyOS Next中实现粒子动画跟手效果,可使用ArkUI的Canvas组件结合手势事件。通过@ohos.graphics.drawing创建粒子系统,利用@ohos.multimodalInput.gesture监听触摸移动事件,实时更新粒子位置。粒子属性(如坐标、速度)随手指移动动态计算,实现跟随效果。使用requestAnimationFrame进行逐帧渲染,确保动画流畅。关键步骤包括初始化粒子、绑定手势、更新粒子状态及绘制。

在HarmonyOS Next中实现粒子动画的跟手效果,核心是结合图形绘制能力手势事件处理。以下是关键实现步骤:

1. 核心思路

利用Canvas组件进行粒子绘制,通过PanGesture(拖动手势)监听手指移动,实时更新粒子系统参数(如发射器位置、受力方向),使粒子运动轨迹跟随手指。

2. 关键技术点

  • 粒子系统管理:使用ParticleSystem类(需自定义或使用三方库)管理粒子生命周期(位置、速度、大小、透明度)。
  • 手势交互:通过PanGestureonActionStartonActionUpdateonActionEnd事件获取手指坐标。
  • 实时绘制:在CanvasRenderingContext2D中逐帧绘制粒子,并通过requestAnimationFrame驱动动画循环。

3. 代码示例框架

// 自定义粒子类
class Particle {
  x: number;
  y: number;
  vx: number;
  vy: number;
  // ...其他属性
}

// 手势绑定与动画循环
@Entry
@Component
struct ParticleComponent {
  private particles: Particle[] = [];
  private touchX: number = 0;
  private touchY: number = 0;

  build() {
    Column() {
      // Canvas绘制区域
      Canvas(this.onDraw)
        .width('100%')
        .height('100%')
        .backgroundColor('#000')
        // 绑定拖动手势
        .gesture(
          PanGesture()
            .onActionStart((event: GestureEvent) => this.updateTouch(event))
            .onActionUpdate((event: GestureEvent) => this.updateTouch(event))
            .onActionEnd(() => this.resetTouch())
        )
    }
  }

  // 更新手指坐标
  private updateTouch(event: GestureEvent) {
    this.touchX = event.offsetX;
    this.touchY = event.offsetY;
  }

  // 绘制粒子
  private onDraw(ctx: CanvasRenderingContext2D) {
    // 1. 清空画布
    ctx.clearRect(0, 0, ctx.width, ctx.height);
    
    // 2. 根据touchX/touchY生成新粒子
    this.generateParticles();
    
    // 3. 更新并绘制所有粒子
    this.particles.forEach(particle => {
      // 物理计算(如速度衰减、重力)
      particle.x += particle.vx;
      particle.y += particle.vy;
      // 绘制粒子(圆形示例)
      ctx.beginPath();
      ctx.arc(particle.x, particle.y, 2, 0, Math.PI * 2);
      ctx.fillStyle = 'rgba(255, 100, 100, 0.7)';
      ctx.fill();
    });
    
    // 4. 移除生命周期结束的粒子
    this.particles = this.particles.filter(p => p.life > 0);
    
    // 5. 递归调用实现动画
    requestAnimationFrame(() => this.onDraw(ctx));
  }
}

4. 性能优化建议

  • 控制粒子数量(建议单次生成≤50个)。
  • 使用OffscreenCanvas预渲染静态元素。
  • 粒子消失时使用渐隐动画避免突兀感。

5. 扩展效果

  • 粒子样式:替换arcdrawImage可实现纹理粒子。
  • 物理效果:在粒子更新中加入阻力、引力或涡旋力场计算。
  • 多点触控:通过GestureGroup绑定多个PanGesture实现分触点粒子发射。

通过以上方法,可构建出流畅的粒子跟手动画。注意在aboutToDisappear生命周期中取消动画循环,避免内存泄漏。

回到顶部