在HarmonyOS中,如何“平滑”从 POINT 切到 IMAGE 粒子且避免卡顿?

运行时想在 点状 与 图片 粒子间切换。如何通过“逐步降低 emitRate→切换→再升回”来避免瞬时峰值?另外,count/lifetime 应如何设以避免内存和绘制压力?

阅读 714
1 个回答

切换前把当前 emitRate 缓慢插值到 0,切模式后再缓慢插回目标值(避免瞬间粒子激增)。count 不要过大、lifetime 尽量不要设 -1(无限),并给 lifetimeRange 做离散。下面示例演示完整过程与防炸帧配置。

type Mode = 'point' | 'image';

@Entry
@Component
struct SafeTypeSwitch {
  @State mode: Mode = 'point';
  @State liveRate: number = 160;       // 实时发射率
  private targetRate = 160;
  private timer: number | null = null;

  // 线性插值发射率,避免骤变
  private tweenEmit(to: number, stepMs: number = 40) {
    if (this.timer) { clearInterval(this.timer as any); this.timer = null; }
    this.timer = setInterval(() => {
      const step = (to - this.liveRate) * 0.25;
      if (Math.abs(step) < 1) {
        this.liveRate = to;
        clearInterval(this.timer as any);
        this.timer = null;
      } else {
        this.liveRate += step;
      }
    }, stepMs) as any;
  }

  private particleOptions(): Particles<ParticleType, ParticleUpdater, ParticleUpdater, ParticleUpdater, ParticleUpdater, ParticleUpdater, ParticleUpdater> {
    const cfg = this.mode === 'point'
      ? { type: ParticleType.POINT, config: { radius: 2 }, count: 700, lifetime: 5000, lifetimeRange: 1500 }
      : { type: ParticleType.IMAGE, config: { src: $r('app.media.icon'), size: [8, 8] }, count: 700, lifetime: 5000, lifetimeRange: 1500 };

    return {
      particles: [{
        emitter: { particle: cfg as any, emitRate: this.liveRate, shape: ParticleEmitterShape.RECTANGLE },
        color: { range: [Color.White, Color.White] },         // 图片粒子不调整颜色;点粒子纯白
        opacity: { range: [0.5, 1.0] },
        scale: this.mode === 'point' ? { range: [0.9, 1.2] } : { range: [0.9, 1.4] },
        velocity: { speed: [60, 120], angle: [0, 360] }
      }]
    } as any;
  }

  private switchMode() {
    // 1) 先把 emitRate 平滑降到 0
    this.tweenEmit(0);
    // 2) 250ms 后切模式(避免正好在峰值期切换)
    setTimeout(() => {
      this.mode = this.mode === 'point' ? 'image' : 'point';
      // 3) 再把 emitRate 平滑升回目标
      this.tweenEmit(this.targetRate);
    }, 250);
  }

  build() {
    Column({ space: 10 }) {
      Particle(this.particleOptions())
        .width(320).height(200).backgroundColor(0x101214)

      Row({ space: 8 }) {
        Text(`当前:${this.mode === 'point' ? 'POINT' : 'IMAGE'}`)
        Button('平滑切换').onClick(() => this.switchMode())
      }

      // 提醒:count 不要太大、lifetime 避免 -1(无限),并设置 lifetimeRange
      Text('建议:count ≤ 1000,lifetime≠-1,使用 lifetimeRange 离散粒子生命。')
        .fontColor(0xB0FFFFFF).fontSize(12)
    }
    .padding(12)
  }
}
推荐问题