粒子系统模板化:HarmonyOS 天气粒子动画完整源码


实例:天气粒子动画(Weather Particle)|技术:全量代码解读、操作剧本、粒子系统模式抽象
一、本篇定位
前两篇分别讲了引擎层与 UI 层,本篇完成三件事:
- 全量源码逐段解读——WeatherParticlePage 从头到尾过一遍;
- 运行效果演示——五种天气的切换与效果;
- 模式抽象——"粒子系统"通用模板,附两个改写场景。
二、完整源码: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/stopLoop | 16ms 帧循环 | 60fps 近似 |
| switchMode | 清空重建重启 | 无过渡切换 |
| targetCount | 按模式粒子数 | 密度配置 |
| spawnParticle | 生成工厂 switch | 引擎+配置 |
| spawn* 五方法 | 各天气参数 | 物理差异 |
| update | 物理 + 生命周期 + 重生 | 自循环 |
| drawBackground | 模式背景渐变 | 氛围基调 |
| draw 分发 | type 分支绘制 | 一接口多形态 |
| drawRain/Snow/Cloud/Lightning/Sun | 各形态 | 几何拼合 |
三、接入工程三步
Step 1:放文件
WeatherParticlePage.ets→entry/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 | 标题栏:“🌦️ 天气粒子动画”,粒子数:~180 | switchMode(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 六个约定
- 统一接口 + type 分发:所有粒子同结构,绘制按 type 分支;
- 生成工厂 switch:spawnParticle 按模式分发,新增模式只加分支;
- 物理 = 速度向量积分:x += vx、y += vy 每帧;
- 死亡即重生:生命周期结束或出界 → 原地生成新粒子,数量自稳;
- 参数即配置:速度/尺寸/透明度/数量全在 spawn 方法里,改参数改天气;
- 离开即停:aboutToDisappear 停帧循环,防泄漏。
5.3 两个改写场景
场景 A:烟花秀
| 天气实例 | 烟花改写 |
|---|---|
| spawnRain | spawnRocket(上升 + 爆炸成碎片) |
| 生命周期 | 火箭升空 → 爆点生成碎片粒子 |
| 绘制 | 碎片带渐变色 + 拖尾 |
| 触发 | 点击画布放烟花 / 定时自动 |
场景 B:星空屏保
| 天气实例 | 星空改写 |
|---|---|
| spawnSun | spawnStar(闪烁: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 教你模式复用:引擎 + 配置模板,改参数即覆盖烟花、星空、萤火。
更多推荐

所有评论(0)