在这里插入图片描述
在这里插入图片描述

实例:天气粒子动画(Weather Particle)|技术:全量代码解读、操作剧本、粒子系统模式抽象

一、本篇定位

前两篇分别讲了引擎层与 UI 层,本篇完成三件事:

  1. 全量源码逐段解读——WeatherParticlePage 从头到尾过一遍;
  2. 运行效果演示——五种天气的切换与效果;
  3. 模式抽象——"粒子系统"通用模板,附两个改写场景。

二、完整源码:WeatherParticlePage.ets

/**
 * 天气粒子动画 —— 自定义 Canvas(CanvasRenderingContext2D)应用 7
 * 对应博客:Canvas_2d/7_天气粒子/7-1 ~ 7-3
 *
 * 功能:
 *  1. 多种天气模式:晴(光斑)、多云(飘云)、雨(雨丝)、雪(雪花)、雷雨(闪电)
 *  2. 粒子系统:随机生成、生命周期、物理属性(重力/风速)
 *  3. 帧循环:requestAnimationFrame 驱动,60fps 流畅动画
 *  4. 模式切换:胶囊 Tab 即时切换粒子行为
 */
import { hilog } from '@kit.PerformanceAnalysisKit';

const TAG = 'WeatherParticle';

/** 粒子通用属性 */
interface Particle {
  x: number;
  y: number;
  vx: number;
  vy: number;
  size: number;
  alpha: number;
  life: number;
  maxLife: number;
  type: string;     // 'rain' | 'snow' | 'sun' | 'cloud' | 'lightning'
}

/** 天气模式 */
const MODES: string[] = ['☀️ 晴', '🌥️ 多云', '🌧️ 雨', '❄️ 雪', '⛈️ 雷雨'];

@Entry
@Component
struct WeatherParticlePage {
  private settings: RenderingContextSettings = new RenderingContextSettings(true);
  private ctx: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
  private canvasW: number = 0;
  private canvasH: number = 0;

  private particles: Particle[] = [];
  private rafId: number = -1;
  private lastTime: number = 0;

  @State mode: number = 2;       // 默认雨
  @State countText: string = '粒子数:0';

  aboutToDisappear(): void {
    this.stopLoop();
  }
  // startLoop / stopLoop / switchMode / targetCount / spawnParticle
  // spawnSun/Cloud/Rain/Snow/Lightning / update / draw / drawBackground
  // drawRain/Snow/Cloud/Lightning/Sun / build(见上两篇逐段解读)
}

逐段解读总表

代码段职责关键决策
Particle 接口通用粒子属性type 分发
MODES五种天气配置Tab 驱动
startLoop/stopLoop16ms 帧循环60fps 近似
switchMode清空重建重启无过渡切换
targetCount按模式粒子数密度配置
spawnParticle生成工厂 switch引擎+配置
spawn* 五方法各天气参数物理差异
update物理 + 生命周期 + 重生自循环
drawBackground模式背景渐变氛围基调
draw 分发type 分支绘制一接口多形态
drawRain/Snow/Cloud/Lightning/Sun各形态几何拼合

三、接入工程三步

Step 1:放文件

  • WeatherParticlePage.etsentry/src/main/ets/pages/canvas_2d/

Step 2:注册路由(main_pages.json)

"pages/canvas_2d/WeatherParticlePage",

Step 3:首页入口(Index.ets)

Button('🌦️ 35 天气粒子动画')
  .fontSize(14)
  .width('80%')
  .onClick(() => {
    this.getUIContext().getRouter().pushUrl({ url: 'pages/canvas_2d/WeatherParticlePage' });
  })

四、运行效果演示(操作剧本)

场景一:首次打开

步骤界面表现数据来源
1标题栏:“🌦️ 天气粒子动画”,粒子数:~180switchMode(2)
2画布:深灰渐变背景,180 条浅蓝雨丝斜落(风向左)雨模式
3底部五胶囊:☀️晴 🌥️多云 🌧️雨 ❄️雪 ⛈️雷雨(雨高亮)MODES

场景二:切换五种天气

点击效果粒子数
☀️ 晴亮蓝渐变 + 40 个金黄光斑缓慢漂浮~40
🌥️ 多云灰蓝渐变 + 12 朵白灰云团向右漂移~12
🌧️ 雨深灰渐变 + 180 条雨丝斜落~180
❄️ 雪浅白渐变 + 150 片雪花慢飘(横风摇摆)~150
⛈️ 雷雨暗灰渐变 + 220 粒子,偶发全屏闪白 + 黄色锯齿闪电~220

场景三:观察闪电

雷雨模式下等待 3~5 秒——某帧画布闪白 0.13 秒、黄色锯齿线一闪而过(2% 概率 × 8 帧寿命)。

场景四:粒子数呼吸

雨模式下盯着标题栏粒子数:在 ~180 附近波动(出界重生时序差异)。切到多云:~12(云团少而大)。

场景五:刷新与退出

点 ↻ → 当前天气清空重建(重新下雨)。返回首页 → 帧循环停止(aboutToDisappear),无泄漏。

五、模式抽象:粒子系统通用模板

5.1 引擎骨架

┌─────────────────────────────────────────┐
│  粒子引擎(通用,只写一遍)                │
│                                          │
│  Particle 接口:位置/速度/尺寸/透明度/生命  │
│                                          │
│  update():                              │
│    life-- → 位置+=速度 → 死亡?→ 重生     │
│                                          │
│  draw():按 type 分发绘制                 │
└──────────┬──────────────────────────────┘
           │ "引擎 + 配置"
┌──────────▼──────────────────────────────┐
│  模式配置(每模式一套 spawn 参数)          │
│                                          │
│  晴:慢速 + 光斑     雨:高速 + 雨丝       │
│  云:慢漂 + 云团     雪:慢落 + 摇摆       │
│  雷雨:雨 + 随机闪电                      │
└─────────────────────────────────────────┘

5.2 六个约定

  1. 统一接口 + type 分发:所有粒子同结构,绘制按 type 分支;
  2. 生成工厂 switch:spawnParticle 按模式分发,新增模式只加分支;
  3. 物理 = 速度向量积分:x += vx、y += vy 每帧;
  4. 死亡即重生:生命周期结束或出界 → 原地生成新粒子,数量自稳;
  5. 参数即配置:速度/尺寸/透明度/数量全在 spawn 方法里,改参数改天气;
  6. 离开即停:aboutToDisappear 停帧循环,防泄漏。

5.3 两个改写场景

场景 A:烟花秀

天气实例烟花改写
spawnRainspawnRocket(上升 + 爆炸成碎片)
生命周期火箭升空 → 爆点生成碎片粒子
绘制碎片带渐变色 + 拖尾
触发点击画布放烟花 / 定时自动

场景 B:星空屏保

天气实例星空改写
spawnSunspawnStar(闪烁:alpha 正弦波动)
速度静止或极慢漂移
背景深蓝渐变 + 银河带
粒子数300+ 星点

场景 C:蒲公英/萤火虫——慢速漂浮 + 光点 + 随机游走,模板复用。

三个场景的共同点:“粒子引擎 + 模式配置”。掌握 35,任何粒子效果(雨雪、烟花、星空、萤火)都能在半小时内搭出来。

六、三篇内容总对照

篇章核心知识点一句话总结
35-1 粒子系统与多模式天气粒子接口、生成工厂、物理、重生引擎写一遍,天气靠配置
35-2 画布布局与模式切换画布初始化、模式 Tab、生命周期切换即时、离开清理
35-3 完整代码与运行效果全量源码、操作剧本、粒子模板粒子引擎 + 配置,改参数即新效果

七、FAQ(三篇汇总补遗)

Q1:25 粒子特效和 35 天气粒子有什么区别?
A:25 是单一效果(星空/烟花切换),35 是多模式粒子引擎(五种天气共享引擎、参数化生成)。35 把"引擎与配置分离"做到了极致——25 是单一粒子应用的示范,35 是粒子系统框架的示范

Q2:粒子系统需要对象池吗?
A:教学用"数组 + 死亡重生"(每次 update 重建数组)已够。对象池(复用已死亡粒子对象)能减少 GC,但 ArkTS 数组重建开销小、代码更清晰。数据量小(<300)时数组方案优先

Q3:闪电的 2% 概率会不会太随机?
A:2% × 每粒子 × 220 粒子 = 平均每帧约 4.4 个闪电粒子生成,但每个寿命仅 8 帧——实际"闪"的频率约每 2 秒一次。概率 × 数量 × 寿命共同决定稀有事件频率,调参时三者联动。

Q4:背景渐变每帧重建 gradient 对象有开销吗?
A:createLinearGradient 每次调用创建对象,220 粒子时开销占比小。若优化:模式不变时缓存 gradient 对象(switchMode 时重建)。教学直绘,注释标注缓存路径

Q5:能加音效吗(雨声/雷声)?
A:可以——@kit.MediaKit 的 AVPlayer 播放对应天气的音频循环。切模式时切换音轨,与粒子变化同步。音画联动是天气应用的体验升级。

Q6:这个引擎能用于游戏背景吗?
A:能——游戏场景的雨雪背景就是粒子系统。与 34 贪吃蛇的帧循环合并:游戏 step + 粒子 update 同一循环驱动。粒子引擎可嵌入任意 Canvas 应用

八、结语

三篇文章完整走完了天气粒子动画的"引擎 → 界面 → 复用"闭环:

  • 1 教你粒子引擎:统一接口、生成工厂、物理模拟、死亡重生;
  • 2 教你切换交互:五模式 Tab、粒子数显示、生命周期管理;
  • 3 教你模式复用:引擎 + 配置模板,改参数即覆盖烟花、星空、萤火。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐