目录
-
- 一、为什么需要粒子系统
- 二、开发环境与版本说明
- 三、原理分析:四层架构
-
- [3.1 ParticleSystem------容器与调度器](#3.1 ParticleSystem——容器与调度器)
- [3.2 Emitter------发射器](#3.2 Emitter——发射器)
- [3.3 ParticlePainter------渲染器](#3.3 ParticlePainter——渲染器)
- [3.4 Affector------影响器](#3.4 Affector——影响器)
- [3.5 粒子的完整生命周期](#3.5 粒子的完整生命周期)
- [四、代码实现:Concept_ParticleSystem.qml 逐段解析](#四、代码实现:Concept_ParticleSystem.qml 逐段解析)
-
- [4.1 完整代码](#4.1 完整代码)
- [4.2 页面结构:BaseRect + PageTitle](#4.2 页面结构:BaseRect + PageTitle)
- [4.3 粒子特效层](#4.3 粒子特效层)
- [4.4 信息卡片层:ListModel + Repeater](#4.4 信息卡片层:ListModel + Repeater)
- [4.5 入场动画:延迟渐入效果](#4.5 入场动画:延迟渐入效果)
- [4.6 动画生命周期管理](#4.6 动画生命周期管理)
- [4.7 文字渲染](#4.7 文字渲染)
- [4.8 项目骨架:导航与页面管理](#4.8 项目骨架:导航与页面管理)
- [4.9 通用组件复用](#4.9 通用组件复用)
- 五、运行效果
- 六、常见问题与边界条件
- 七、总结与下篇预告
一、为什么需要粒子系统
在 QML 开发中,我们经常遇到这样的需求:游戏中的爆炸火花、天气应用的雪花飘落、音乐播放器的频谱背景、按钮点击后的光点扩散。这些效果有一个共同特征------大量微小元素各自独立运动,形成整体的视觉表现。
如果用传统的 QML 动画实现,每个元素都需要独立的 NumberAnimation 或 Behavior,100 个元素就是 100 个动画对象,性能和代码量都会急剧恶化。而 Qt Quick 粒子系统模块 (QtQuick.Particles)正是为这类场景设计的:它在 GPU 层面批量管理成百上千个粒子,开发者只需声明发射规则和行为约束,系统自动处理生命周期、运动计算和渲染。
在正式学习之前,先对比三种常见方案的适用边界:
| 方案 | 适用场景 | 性能上限 | 开发复杂度 |
|---|---|---|---|
| QML 动画(NumberAnimation 等) | 单个元素的简单动效 | <20个对象(仅供参考) | 低 |
| Canvas 手绘 | 自定义绘制逻辑 | 中等 | 高 |
| ParticleSystem 模块 | 大量独立运动粒子 | 5000-10000粒子(仅供参考) | 中 |
| ShaderEffect | GPU 全自定义 | 最高 | 很高 |
本文是系列第一篇,目标是建立 Qt Quick 粒子系统的全局认知框架------理解它的四层架构,知道每个组件的职责和协作关系,最终用 10 行代码写出一个最小粒子系统。
二、开发环境与版本说明
本文所有代码基于以下环境验证(验证日期:2026-06-05):
- Qt 版本 :6.8.2(
QtQuick.Particles模块从 Qt 5 引入,Qt 6 中沿用,本文以 Qt 6 为准) - 编译器:MinGW 64-bit
- 操作系统:Windows 11
- 构建工具:CMake 3.29
如果你使用 Qt 6.5 及以上版本,代码无需修改即可运行。Qt 5.x 版本也可以使用粒子系统模块,但 import 路径和部分 API 存在差异(如 import QtQuick.Particles 2.15),本文统一以 Qt 6 为准。
项目的 CMake 配置中,关键部分如下:
cmake
# 启用 QML 模块
qt_add_qml_module(appqml_particlesystem
URI qml_particlesystem
VERSION 1.0
QML_FILES
Main.qml
Concept_ParticleSystem.qml
# ... 其他 QML 文件
RESOURCES res.qrc
)
粒子系统本身不需要额外的 C++ 代码,所有逻辑都在 QML 中完成。main.cpp 仅负责创建 QQmlApplicationEngine 并加载入口 QML 文件:
cpp
QQmlApplicationEngine engine;
engine.loadFromModule("qml_particlesystem", "Main");
三、原理分析:四层架构
Qt Quick 粒子系统的设计可以抽象为四层,每层各司其职:
#mermaid-svg-STORCdF62sy2O4oq{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-STORCdF62sy2O4oq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-STORCdF62sy2O4oq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-STORCdF62sy2O4oq .error-icon{fill:#552222;}#mermaid-svg-STORCdF62sy2O4oq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-STORCdF62sy2O4oq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-STORCdF62sy2O4oq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-STORCdF62sy2O4oq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-STORCdF62sy2O4oq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-STORCdF62sy2O4oq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-STORCdF62sy2O4oq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-STORCdF62sy2O4oq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-STORCdF62sy2O4oq .marker.cross{stroke:#333333;}#mermaid-svg-STORCdF62sy2O4oq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-STORCdF62sy2O4oq p{margin:0;}#mermaid-svg-STORCdF62sy2O4oq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-STORCdF62sy2O4oq .cluster-label text{fill:#333;}#mermaid-svg-STORCdF62sy2O4oq .cluster-label span{color:#333;}#mermaid-svg-STORCdF62sy2O4oq .cluster-label span p{background-color:transparent;}#mermaid-svg-STORCdF62sy2O4oq .label text,#mermaid-svg-STORCdF62sy2O4oq span{fill:#333;color:#333;}#mermaid-svg-STORCdF62sy2O4oq .node rect,#mermaid-svg-STORCdF62sy2O4oq .node circle,#mermaid-svg-STORCdF62sy2O4oq .node ellipse,#mermaid-svg-STORCdF62sy2O4oq .node polygon,#mermaid-svg-STORCdF62sy2O4oq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-STORCdF62sy2O4oq .rough-node .label text,#mermaid-svg-STORCdF62sy2O4oq .node .label text,#mermaid-svg-STORCdF62sy2O4oq .image-shape .label,#mermaid-svg-STORCdF62sy2O4oq .icon-shape .label{text-anchor:middle;}#mermaid-svg-STORCdF62sy2O4oq .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-STORCdF62sy2O4oq .rough-node .label,#mermaid-svg-STORCdF62sy2O4oq .node .label,#mermaid-svg-STORCdF62sy2O4oq .image-shape .label,#mermaid-svg-STORCdF62sy2O4oq .icon-shape .label{text-align:center;}#mermaid-svg-STORCdF62sy2O4oq .node.clickable{cursor:pointer;}#mermaid-svg-STORCdF62sy2O4oq .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-STORCdF62sy2O4oq .arrowheadPath{fill:#333333;}#mermaid-svg-STORCdF62sy2O4oq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-STORCdF62sy2O4oq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-STORCdF62sy2O4oq .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-STORCdF62sy2O4oq .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-STORCdF62sy2O4oq .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-STORCdF62sy2O4oq .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-STORCdF62sy2O4oq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-STORCdF62sy2O4oq .cluster text{fill:#333;}#mermaid-svg-STORCdF62sy2O4oq .cluster span{color:#333;}#mermaid-svg-STORCdF62sy2O4oq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-STORCdF62sy2O4oq .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-STORCdF62sy2O4oq rect.text{fill:none;stroke-width:0;}#mermaid-svg-STORCdF62sy2O4oq .icon-shape,#mermaid-svg-STORCdF62sy2O4oq .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-STORCdF62sy2O4oq .icon-shape p,#mermaid-svg-STORCdF62sy2O4oq .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-STORCdF62sy2O4oq .icon-shape .label rect,#mermaid-svg-STORCdF62sy2O4oq .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-STORCdF62sy2O4oq .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-STORCdF62sy2O4oq .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-STORCdF62sy2O4oq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ParticleSystem(容器/调度器)
管理所有粒子的生命周期、状态和全局调度
Emitter(发射器)
决定粒子从哪来、怎么来
ParticlePainter(渲染器)
决定粒子长什么样
Affector(影响器)
决定粒子发射后受什么力
ImageParticle
GPU 批量渲染
ItemParticle
QML 组件渲染
3.1 ParticleSystem------容器与调度器
ParticleSystem 是所有粒子元素的宿主容器。它本身不发射、不渲染、不影响粒子,而是负责:
- 生命周期管理:跟踪每个粒子的诞生、运动和消亡
- 全局调度:协调 Emitter、Painter、Affector 之间的数据流
- 状态控制 :通过
running、paused、empty等属性管理系统运行状态
一个粒子系统中可以包含多个 Emitter、多个 Painter 和多个 Affector,它们通过父子关系 或 system 属性关联到同一个 ParticleSystem:
qml
// 方式一:父子关系(推荐,简洁)
ParticleSystem {
ImageParticle { ... }
Emitter { ... }
}
// 方式二:显式 system 绑定(组件分散在不同层级时使用)
ParticleSystem { id: mySystem }
ImageParticle { system: mySystem }
Emitter { system: mySystem }
3.2 Emitter------发射器
Emitter 决定粒子从哪里来、以什么方式来。它的核心职责包括:
- 发射区域 :通过
shape属性限定粒子的出生位置(矩形、椭圆、线条等) - 发射频率 :
emitRate控制每秒发射的粒子数量 - 初始属性 :
lifeSpan(生命周期)、size(大小)、velocity(速度)、acceleration(加速度) - 运动方向 :粒子的扩散方向由
Direction子组件控制,包括AngleDirection(角度方向)、PointDirection(点方向)、TargetDirection(目标方向)等,后续文章将详细讲解 - 粒子分组 :
group属性将发射的粒子归入指定组
一个 ParticleSystem 可以有多个 Emitter,分别发射不同样式的粒子。
3.3 ParticlePainter------渲染器
ParticlePainter 决定粒子长什么样。Qt Quick 提供了两种实现:
- ImageParticle:使用图片作为粒子外观,GPU 批量渲染,性能最优。支持着色、旋转、变形、透明度等属性。
- ItemParticle:使用任意 QML 组件作为粒子外观,灵活度最高,支持交互(点击、拖拽),但每个粒子是独立的 Item,性能较低。
两者通过 groups 属性指定负责渲染哪些粒子组,可以同时使用。
3.4 Affector------影响器
Affector 在粒子发射后、消亡前,对其属性进行二次修改。常见效果包括:
Gravity:重力加速度Friction:速度衰减Attractor:向目标点吸引Wander:随机游走Turbulence:湍流扰动
Affector 同样通过 groups 属性限定作用范围,可以叠加使用。
3.5 粒子的完整生命周期
将四层串联起来,一个粒子从诞生到消亡的完整流程是:
#mermaid-svg-eQ6AqyENRIhA8WWl{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-eQ6AqyENRIhA8WWl .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-eQ6AqyENRIhA8WWl .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-eQ6AqyENRIhA8WWl .error-icon{fill:#552222;}#mermaid-svg-eQ6AqyENRIhA8WWl .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-eQ6AqyENRIhA8WWl .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-eQ6AqyENRIhA8WWl .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-eQ6AqyENRIhA8WWl .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-eQ6AqyENRIhA8WWl .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-eQ6AqyENRIhA8WWl .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-eQ6AqyENRIhA8WWl .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-eQ6AqyENRIhA8WWl .marker{fill:#333333;stroke:#333333;}#mermaid-svg-eQ6AqyENRIhA8WWl .marker.cross{stroke:#333333;}#mermaid-svg-eQ6AqyENRIhA8WWl svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-eQ6AqyENRIhA8WWl p{margin:0;}#mermaid-svg-eQ6AqyENRIhA8WWl .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-eQ6AqyENRIhA8WWl .cluster-label text{fill:#333;}#mermaid-svg-eQ6AqyENRIhA8WWl .cluster-label span{color:#333;}#mermaid-svg-eQ6AqyENRIhA8WWl .cluster-label span p{background-color:transparent;}#mermaid-svg-eQ6AqyENRIhA8WWl .label text,#mermaid-svg-eQ6AqyENRIhA8WWl span{fill:#333;color:#333;}#mermaid-svg-eQ6AqyENRIhA8WWl .node rect,#mermaid-svg-eQ6AqyENRIhA8WWl .node circle,#mermaid-svg-eQ6AqyENRIhA8WWl .node ellipse,#mermaid-svg-eQ6AqyENRIhA8WWl .node polygon,#mermaid-svg-eQ6AqyENRIhA8WWl .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-eQ6AqyENRIhA8WWl .rough-node .label text,#mermaid-svg-eQ6AqyENRIhA8WWl .node .label text,#mermaid-svg-eQ6AqyENRIhA8WWl .image-shape .label,#mermaid-svg-eQ6AqyENRIhA8WWl .icon-shape .label{text-anchor:middle;}#mermaid-svg-eQ6AqyENRIhA8WWl .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-eQ6AqyENRIhA8WWl .rough-node .label,#mermaid-svg-eQ6AqyENRIhA8WWl .node .label,#mermaid-svg-eQ6AqyENRIhA8WWl .image-shape .label,#mermaid-svg-eQ6AqyENRIhA8WWl .icon-shape .label{text-align:center;}#mermaid-svg-eQ6AqyENRIhA8WWl .node.clickable{cursor:pointer;}#mermaid-svg-eQ6AqyENRIhA8WWl .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-eQ6AqyENRIhA8WWl .arrowheadPath{fill:#333333;}#mermaid-svg-eQ6AqyENRIhA8WWl .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-eQ6AqyENRIhA8WWl .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-eQ6AqyENRIhA8WWl .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eQ6AqyENRIhA8WWl .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-eQ6AqyENRIhA8WWl .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eQ6AqyENRIhA8WWl .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-eQ6AqyENRIhA8WWl .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-eQ6AqyENRIhA8WWl .cluster text{fill:#333;}#mermaid-svg-eQ6AqyENRIhA8WWl .cluster span{color:#333;}#mermaid-svg-eQ6AqyENRIhA8WWl div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-eQ6AqyENRIhA8WWl .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-eQ6AqyENRIhA8WWl rect.text{fill:none;stroke-width:0;}#mermaid-svg-eQ6AqyENRIhA8WWl .icon-shape,#mermaid-svg-eQ6AqyENRIhA8WWl .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eQ6AqyENRIhA8WWl .icon-shape p,#mermaid-svg-eQ6AqyENRIhA8WWl .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-eQ6AqyENRIhA8WWl .icon-shape .label rect,#mermaid-svg-eQ6AqyENRIhA8WWl .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eQ6AqyENRIhA8WWl .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-eQ6AqyENRIhA8WWl .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-eQ6AqyENRIhA8WWl :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Emitter 发射粒子
赋予初始位置、速度、大小、生命周期等属性
ParticleSystem 调度粒子运动
根据速度和加速度更新位置
Affector 修改粒子属性
施加重力、摩擦、湍流等力场
ParticlePainter 渲染粒子
根据当前属性绘制到屏幕
生命周期结束,粒子消亡
理解这个流程是后续学习的基础------Emitter 控制"从哪来",Direction 控制"往哪飞",Affector 控制"受什么力",Painter 控制"长什么样"。
四、代码实现:Concept_ParticleSystem.qml 逐段解析
项目中 Concept_ParticleSystem.qml 是第一个示例页面,它同时做了两件事:展示四层架构的知识卡片,以及用粒子特效作为背景。下面先看完整代码,再逐段解析关键设计。
4.1 完整代码
qml
import QtQuick
import QtQuick.Particles
import QtQuick.Layouts
import "common"
BaseRect {
id: root
PageTitle { titleText: "ParticleSystem - 粒子系统" }
// ── 粒子特效层 ──────────────────────────────────
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.margins: 10
color: "transparent"
radius: 8
ParticleSystem {
id: particleSystem
anchors.fill: parent
running: root.isCurrentItem
ImageParticle {
source: "qrc:/images/star.png"
alpha: 0.5
}
Emitter {
anchors.centerIn: parent
width: infoColumn.width
height: infoColumn.height
emitRate: 30
lifeSpan: 3000
size: 10
sizeVariation: 10
}
Wander {
system: particleSystem
xVariance: 100
yVariance: 100
pace: 100
}
}
// ── 信息卡片层 ──────────────────────────────
ColumnLayout {
id: infoColumn
anchors.fill: parent
anchors.margins: 15
spacing: 15
visible: false
ListModel {
id: infoModel
ListElement {
title: "## ParticleSystem 是所有粒子元素的容器"
titleColor: "#4ECDC4"
content: "- 管理粒子生命周期、渲染和交互\n- 核心属性: running, paused, empty\n- 核心方法: start(), stop(), pause(), resume(), reset(), restart()"
}
ListElement {
title: "## Emitter - 发射器"
titleColor: "#FFE66D"
content: "- 发射逻辑粒子到 ParticleSystem\n- 核心属性: emitRate, lifeSpan, size, velocity, acceleration, shape, group\n- 核心方法: burst(count), pulse(duration)"
}
ListElement {
title: "## ParticlePainter - 渲染器"
titleColor: "#FF6B6B"
content: "- 指定如何绘制粒子\n- 子类: ImageParticle(图像渲染), ItemParticle(QML组件渲染)\n- 核心属性: groups, system"
}
ListElement {
title: "## Affector - 影响器"
titleColor: "#87CEEB"
content: "- 在粒子生命周期任意时刻修改其属性\n- 子类: Age, Attractor, Friction, Gravity, Turbulence, Wander, SpriteGoal\n- 核心属性: acceleration, velocity, position, relative"
}
}
// ── 入场动画 Repeater ────────────────────
Repeater {
id: infoRepeater
model: infoModel
ColumnLayout {
id: delegateItem
spacing: 5
Layout.fillWidth: true
opacity: 0
transform: Translate { id: trans; y: 20 }
NumberAnimation {
id: entryAnim
target: delegateItem
property: "opacity"
from: 0; to: 1
duration: 400
easing.type: Easing.OutCubic
}
NumberAnimation {
id: slideAnim
target: trans
property: "y"
from: 20; to: 0
duration: 400
easing.type: Easing.OutCubic
}
Timer {
id: delayTimer
interval: 500 + index * 120
onTriggered: { entryAnim.start(); slideAnim.start() }
}
Connections {
target: root
function onIsCurrentItemChanged() {
if (root.isCurrentItem) {
delegateItem.opacity = 0
trans.y = 20
delayTimer.restart()
} else {
entryAnim.stop()
slideAnim.stop()
delayTimer.stop()
}
}
}
Component.onCompleted: {
if (root.isCurrentItem) delayTimer.start()
}
// ── 文字渲染 ──────────────────────
Text {
Layout.fillWidth: true
color: model.titleColor
text: model.title
textFormat: Text.MarkdownText
wrapMode: Text.WordWrap
}
Text {
Layout.fillWidth: true
color: "#aaa"
text: model.content
textFormat: Text.MarkdownText
wrapMode: Text.WordWrap
}
Rectangle {
Layout.fillWidth: true
Layout.topMargin: 5
height: 1
color: "#333"
visible: index < infoModel.count - 1
}
}
}
Item { Layout.fillHeight: true }
}
}
}
4.2 页面结构:BaseRect + PageTitle
页面继承 BaseRect 而非直接使用 Rectangle。BaseRect 提供了两个关键能力:
- 自动布局 :内部使用
ColumnLayout,子元素自动纵向排列 - 页面激活感知 :暴露
isCurrentItem属性,由 StackLayout 自动赋值
PageTitle 是通用标题组件,18px 白色加粗居中显示。
4.3 粒子特效层
粒子特效层的核心是 ParticleSystem,包含三个子组件:
running: root.isCurrentItem ------这是整个项目的核心模式。当用户切换到其他页面时,StackLayout 会将 isCurrentItem 设为 false,粒子系统自动停止,避免不可见时浪费 GPU。切换回来时自动恢复。
Emitter 尺寸绑定 infoColumn ------发射区域不是固定大小,而是绑定到下方文字区域的尺寸。这样粒子从文字区域的范围内涌出,形成"文字背后有粒子"的视觉效果。这是一个常见的设计技巧:让发射区域跟随内容自适应。
alpha: 0.5 ------粒子半透明,避免遮挡前景文字。粒子系统和 UI 内容共存时,透明度控制是关键。
Wander 随机漂移 ------粒子不仅从中心扩散,还带有布朗运动般的漂移效果。xVariance: 100 和 yVariance: 100 控制漂移幅度,pace: 100 控制漂移频率。
4.4 信息卡片层:ListModel + Repeater
ColumnLayout(infoColumn)设为 visible: false,它本身不显示------只为 Emitter 提供尺寸参考(width: infoColumn.width)。实际的文字显示由 Repeater 的 delegate 完成。
ListModel 定义了四层架构的描述数据,每条包含标题颜色和内容。Repeater 为每条数据生成一个 ColumnLayout delegate,包含标题 Text、内容 Text 和分割线 Rectangle。
4.5 入场动画:延迟渐入效果
每张卡片都有一个精心设计的入场动画,分三个阶段:
- 延迟 :
Timer的interval: 500 + index * 120,第一张卡片延迟 500ms,第二张 620ms,第三张 740ms......形成逐张依次出现的效果 - 渐入 :
entryAnim将opacity从 0 动画到 1 - 上滑 :
slideAnim将Translate.y从 20 动画到 0,卡片从下方滑入
Easing.OutCubic 缓动曲线让动画有"快速进入、缓慢停下"的质感,比线性动画更自然。
4.6 动画生命周期管理
动画必须和页面激活状态同步。Connections 监听 root.isCurrentItemChanged 信号:
- 页面激活时:重置 opacity 和位置,重启延迟 Timer,动画从头播放
- 页面离开时:停止所有动画和 Timer,避免后台无意义执行
Component.onCompleted 处理首次加载的边界情况------如果页面在组件创建时就已经是当前页(比如应用启动时默认显示第一页),直接启动动画。
4.7 文字渲染
Text.MarkdownText 让标题中的 ## 语法被正确渲染为二级标题样式。每张卡片之间用 1px 的深灰分割线隔开,最后一张卡片不显示分割线(visible: index < infoModel.count - 1)。
4.8 项目骨架:导航与页面管理
所有示例页面都在 Main.qml 中通过 ListView + StackLayout 组织:
qml
Window {
RowLayout {
ListView {
id: navList
Layout.preferredWidth: 180
model: ListModel {
ListElement { name: "系统概述"; category: "基本概念" }
ListElement { name: "系统控制"; category: "基本概念" }
// ... 更多页面
}
}
StackLayout {
id: contentStack
Concept_ParticleSystem {} // 第 0 页
Concept_SystemControl {} // 第 1 页
// ... 更多页面
}
}
}
ListView 提供左侧分类导航,StackLayout 管理右侧内容区。点击导航项时 contentStack.currentIndex 切换,BaseRect 的 isCurrentItem 自动跟随变化,粒子系统和动画的启停全部自动化------开发者在每个页面中只需声明 running: root.isCurrentItem 即可。
4.9 通用组件复用
项目定义了几个通用组件,保证页面风格一致:
| 组件 | 文件 | 职责 |
|---|---|---|
| BaseRect | common/BaseRect.qml | 页面基础容器,管理 isCurrentItem |
| PageTitle | common/PageTitle.qml | 页面标题,18px 白色加粗居中 |
| BottomNote | common/BottomNote.qml | 底部说明文字,深灰背景 |
| SubTitleRow | common/SubTitleRow.qml | 小节副标题 |
| ColorButton | common/ColorButton.qml | 彩色按钮组件 |
这些组件将布局和样式从业务逻辑中抽离,让每个示例页面只需关注粒子系统本身的代码。
五、运行效果
运行项目后,主界面如下:
- 左侧:180px 宽的导航栏,按分类分组(基本概念、发射区域、扩散方向、粒子随机性、粒子影响器、尾迹发射器、粒子系统示例)------规划分类,目前还在做
- 右侧:StackLayout 内容区,显示当前选中的粒子示例
- 粒子行为:切换页面时,当前页的粒子系统自动运行,离开时自动停止
点击「系统概述」进入第一个示例页面,可以看到四层架构的概览信息和背景粒子效果:

运行截图说明:页面展示 ParticleSystem / Emitter / ParticlePainter / Affector 四层组件的职责说明,背景有 Wander 随机漂移的星形粒子。
项目规划有 7 个分类、30+ 个示例页面,覆盖了 Qt Quick 粒子系统的完整功能。后续文章将逐个模块深入讲解。
六、常见问题与边界条件
Q:Qt 5 项目能否使用粒子系统?
可以。Qt 5 的 QtQuick.Particles 模块功能基本一致,但 import 语句需要指定版本号:
qml
// Qt 5 写法
import QtQuick.Particles 2.15
// Qt 6 写法(推荐)
import QtQuick.Particles
Q:ParticleSystem 和 QML 的 Behavior/NumberAnimation 动画有什么区别?
Behavior 和 NumberAnimation 适合单个元素的属性动画(如按钮缩放、颜色渐变)。ParticleSystem 适合大量独立运动对象的场景------它在引擎层面批量处理粒子数据,避免了为每个粒子创建独立动画对象的开销。
简单判断标准:如果动画对象超过 20 个,考虑 ParticleSystem。
Q:粒子系统的性能上限是多少?
取决于设备 GPU 能力、粒子大小和渲染方式。使用 ImageParticle(GPU 批量渲染)时,中端设备通常可支持数千个活跃粒子。使用 ItemParticle(QML 组件渲染)时,由于每个粒子都是独立的 QML Item,建议控制在 100 个以内。
性能优化的关键手段:
- 不可见时停止粒子系统(
running: isCurrentItem) - 使用
maximumEmitted限制最大活跃粒子数 - 优先使用 ImageParticle 而非 ItemParticle
Q:粒子系统的 running 属性默认是什么?
默认为 true。如果在页面切换场景中使用,务必绑定为 false 或绑定到页面可见性属性,避免不可见时仍消耗 GPU 资源。
七、总结与下篇预告
本文建立了 Qt Quick 粒子系统的四层架构认知:
- ParticleSystem 是容器和调度器,管理全局生命周期
- Emitter 是发射器,决定粒子从哪来、怎么来
- ParticlePainter 是渲染器,决定粒子长什么样(ImageParticle 批量渲染 / ItemParticle 组件渲染)
- Affector 是影响器,决定粒子发射后受什么力
记住这个四层模型:容器、发、画、改------容器管理一切,发射器创造粒子,渲染器绘制粒子,影响器改造粒子。
下一篇将深入 ParticleSystem 的状态控制,详细讲解 start() / stop() / pause() / resume() / reset() / restart() 六种方法的行为差异,以及 running / paused / empty 三态模型的工作机制。
资源下载:qml_particlesystem ------ 包含完整的、可运行的代码
系列目录
- 本文:Qt Quick 粒子系统(一):架构总览与四层模型
- 下一篇:Qt Quick 粒子系统(二):系统控制与生命周期管理