首页 > 代码库 > Qt5官方demo解析集10——Qt Quick Particles Examples - Emitters

Qt5官方demo解析集10——Qt Quick Particles Examples - Emitters

本系列所有文章可以在这里查看http://blog.csdn.net/cloud_castle/article/category/2123873


前段时间去听了Qt在北京的开发者大会,感觉QML是大势所趋,所以回来后想好好补补QML方面的东西。无奈无论是书籍还是网络上,这方面的教材都太少了。

霍亚飞的《Qt Creator快速入门》第二版中做了一些介绍,但也只是基本的元素,布局,动画等。QML绚丽的粒子特效,传感器,多媒体模块,WebView,GPS,蓝牙等等...都没有提及。

所以这段时间也在做Qt官网QML Applications这个教材的翻译,大家有兴趣的也可以看看。


好了,废话不多说,我们今天就来看一个 Qt 粒子系列中的第一个例子Emitters:

我们还是先看看介绍怎么说:

This is a collection of small QML examples relating to using Emitters in the particle system. 

Each example is a small QML file emphasizing a particular type or feature.

也就是说,这个例子是由很多使用Emitters的小例子构成的,每个小例子突出了Emitter的一个特性。我们一个个来看~




首先,我们要知道Emitter是QML粒子系统中的“发射器”,可以理解为每个粒子都是通过这个“发射器”“发射”到屏幕上去的。在这个发射器中,我们就可以设置一些粒子显示的基本属性了,例如发射多少个粒子,每个粒子生命周期多长,发射到哪个坐标点上,等等等等。这样,仅仅通过操作发射器,我们就可以实现一个基本的粒子显示效果,这也是这个demo的由来。


首先我们运行该demo,出现的是一个选择页面,每个页面进去都一个Emitter的小例子:


这个页面的代码实现就不提了,我们以它内容中的顺序来:

在这个demo中qml文件以资源文件的形式存放,我们在资源中找到emitters.qrc,里面的velocityfrommotion.qml就是代表了我们第一个内容的qml文件。


(1)Velocity from Motion


从这个标题我们可以知道,它演示了如何使用Emitter来控制粒子的运动速率。

我们先看其运行效果再来看代码:


我们可以看到有一圈粒子以中心点为圆心在做半径可变的圆周运动,另一部分粒子则做螺旋式圆周运动。如果在屏幕上点击鼠标并拖动,粒子还会产生在鼠标所在位置(如果是触屏则是触碰位置),效果十分好看。

velocityfrommotion.qml:

import QtQuick 2.0
import QtQuick.Particles 2.0 

Rectangle {                                           // 矩形根元素

    id: root

    height: 540
    width: 360

    gradient: Gradient {                              // 加入渐变效果
        GradientStop { position: 0; color: "#000020" }
        GradientStop { position: 1; color: "#000000" }
    }

    MouseArea {                                       // 为了实现点击效果加入
        id: mouseArea
        anchors.fill: root
    }

    ParticleSystem { id: sys1 }                         // 要实现粒子特效必须基于这个ParticleSystem
    ImageParticle {                                     // 也是粒子系统的基本元素
        system: sys1                                    // 指明系统
        source: "qrc:///particleresources/glowdot.png"  // 这里选用了“光点”粒子,详细内容见下方第(1)点
        color: "cyan"                                   // 颜色
        alpha: 0                                        // 透明度
        SequentialAnimation on color {                  // 在颜色上加入动画
            loops: Animation.Infinite                   // 无限循环,等同-1,如果是正值则循环具体次数
            ColorAnimation {                            // 颜色动画
                from: "cyan"
                to: "magenta"
                duration: 1000
            }
            ColorAnimation {
                from: "magenta"
                to: "blue"
                duration: 2000
            }
            ColorAnimation {
                from: "blue"
                to: "violet"
                duration: 2000
            }
            ColorAnimation {
                from: "violet"
                to: "cyan"
                duration: 2000
            }
        }
        colorVariation: 0.3                             // 颜色变化率,从0到1,越大粒子群的色彩越丰富
    }
    //! [0]
    Emitter {                                                  // 这就是我们的发射器了
        id: trailsNormal
        system: sys1                                           // 一样要指明具体的粒子系统

        emitRate: 500                                          // 每秒粒子发射数
        lifeSpan: 2000                                         // 粒子生命周期(毫秒)

        y: mouseArea.pressed ? mouseArea.mouseY : circle.cy       // 发射到的坐标值
        x: mouseArea.pressed ? mouseArea.mouseX : circle.cx       // 这里使用mouseArea检测是否按下,有则使用按下的坐标,否则使用下面的计算坐标。这里的粒子之所以能够持续的发射,其缘由正是QML的属性绑定,由于这个坐标值的持续变化,造成了我们所见的粒子动画。


        velocity: PointDirection {xVariation: 4; yVariation: 4;}  // 当粒子产生后,其扩散行为在x,y方向上的速度
        acceleration: PointDirection {xVariation: 10; yVariation: 10;} // 扩散行为的加速度
        velocityFromMovement: 8                                   // 基于当前粒子的运动为其添加一个额外的速度向量

        size: 8                                                   // 粒子大小,单位是像素,默认为16
        sizeVariation: 4                                          // 一个会随机加到size和endSize上的增量
    }
    //! [0]                                                      
    ParticleSystem { id: sys2 }                                   // 第二个粒子系统,与前者大同小异
    ImageParticle {
        color: "cyan"
        system: sys2
        alpha: 0
        SequentialAnimation on color {
            loops: Animation.Infinite
            ColorAnimation {                                     
                from: "magenta"
                to: "cyan"
                duration: 1000
            }
            ColorAnimation {
                from: "cyan"
                to: "magenta"
                duration: 2000
            }
        }
        colorVariation: 0.5
        source: "qrc:///particleresources/star.png"               // 这里选取了一种不同的粒子,star更小更亮且具有两条发亮的对角线
    }
    Emitter {
        id: trailsStars
        system: sys2

        emitRate: 100                                            // 较少的星星掺杂在光点中,达到绚丽的效果
        lifeSpan: 2200


        y: mouseArea.pressed ? mouseArea.mouseY : circle.cy
        x: mouseArea.pressed ? mouseArea.mouseX : circle.cx

        velocity: PointDirection {xVariation: 4; yVariation: 4;}
        acceleration: PointDirection {xVariation: 10; yVariation: 10;}
        velocityFromMovement: 8

        size: 22
        sizeVariation: 4
    }
    ParticleSystem { id: sys3; }                                // 螺旋运动的粒子
    ImageParticle {
        source: "qrc:///particleresources/glowdot.png"
        system: sys3
        color: "orange"                                     
        alpha: 0
        SequentialAnimation on color {                         // 初始值为橙色,但颜色动画仅需在红绿间切换,因为橙色是其过渡色
            loops: Animation.Infinite
            ColorAnimation {
                from: "red"
                to: "green"
                duration: 2000
            }
            ColorAnimation {
                from: "green"
                to: "red"
                duration: 2000
            }
        }

        colorVariation: 0.2                                 // 如果这个值是1,就看不出上述的动画效果了,如果为0,粒子群的颜色显得单调

    }
    Emitter {
        id: trailsNormal2
        system: sys3

        emitRate: 300
        lifeSpan: 2000

        y: mouseArea.pressed ? mouseArea.mouseY : circle2.cy
        x: mouseArea.pressed ? mouseArea.mouseX : circle2.cx

        velocityFromMovement: 16

        velocity: PointDirection {xVariation: 4; yVariation: 4;}
        acceleration: PointDirection {xVariation: 10; yVariation: 10;}

        size: 12
        sizeVariation: 4
    }
    ParticleSystem { id: sys4; }
    ImageParticle {
        system: sys4
        source: "qrc:///particleresources/star.png"
        color: "green"
        alpha: 0
        SequentialAnimation on color {
            loops: Animation.Infinite
            ColorAnimation {
                from: "green"
                to: "red"
                duration: 2000
            }
            ColorAnimation {
                from: "red"
                to: "green"
                duration: 2000
            }
        }

        colorVariation: 0.5
    }
    Emitter {
        id: trailsStars2
        system: sys4

        emitRate: 50
        lifeSpan: 2200


        y: mouseArea.pressed ? mouseArea.mouseY : circle2.cy
        x: mouseArea.pressed ? mouseArea.mouseX : circle2.cx

        velocityFromMovement: 16
        velocity: PointDirection {xVariation: 2; yVariation: 2;}
        acceleration: PointDirection {xVariation: 10; yVariation: 10;}

        size: 22
        sizeVariation: 4
    }



    color: "white"                                                     // 不明白这个color的意义,因为矩形的颜色在渐变那里已经被确定了

    Item {                                                             // 我们可以使用Item来包含一个逻辑对象,并为它命名,定义其属性来进行调用
        id: circle
        //anchors.fill: parent
        property real radius: 0                                        // 定义属性radius
        property real dx: root.width / 2                               // 圆心横坐标dx
        property real dy: root.height / 2                              // 圆心纵坐标dy   
        property real cx: radius * Math.sin(percent*6.283185307179) + dx  // 计算当前横坐标cx
        property real cy: radius * Math.cos(percent*6.283185307179) + dy  // 计算当前纵坐标cy
        property real percent: 0                                       // 百分比percent

        SequentialAnimation on percent {                               // 使用连续动画改变<span style="font-family: Arial, Helvetica, sans-serif;">自定义属性percent</span>

            loops: Animation.Infinite
            running: true
            NumberAnimation {                                          // 数值动画,注意到这里的往复动画会使粒子顺时针、逆时针交替运动,实际效果也是如此
            duration: 1000
            from: 1
            to: 0
            loops: 8
            }
            NumberAnimation {
            duration: 1000
            from: 0
            to: 1
            loops: 8
            }

        }

        SequentialAnimation on radius {                // 半径的动画
            loops: Animation.Infinite
            running: true
            NumberAnimation {
                duration: 4000
                from: 0
                to: 100
            }
            NumberAnimation {
                duration: 4000
                from: 100
                to: 0
            }
        }
    }

    Item {                                 // 外圈螺旋式运动的粒子需要两个逻辑对象控制其公转与自转
        id: circle3
        property real radius: 100
        property real dx: root.width / 2
        property real dy: root.height / 2
        property real cx: radius * Math.sin(percent*6.283185307179) + dx
        property real cy: radius * Math.cos(percent*6.283185307179) + dy
        property real percent: 0

        SequentialAnimation on percent {             // 这里的百分比仅使用一个循环,就不会产生顺逆时针的交替运动
            loops: Animation.Infinite
            running: true
            NumberAnimation { from: 0.0; to: 1 ; duration: 10000;  }
        }
    }

    Item {
        id: circle2
        property real radius: 30
        property real dx: circle3.cx               // 注意这里的圆心就不再是屏幕圆心了
        property real dy: circle3.cy
        property real cx: radius * Math.sin(percent*6.283185307179) + dx
        property real cy: radius * Math.cos(percent*6.283185307179) + dy
        property real percent: 0

        SequentialAnimation on percent {
            loops: Animation.Infinite
            running: true
            NumberAnimation { from: 0.0; to: 1 ; duration: 1000; }
        }
    }

}
第一点:要注意这一句source: "qrc:///particleresources/glowdot.png"并不是从资源文件中取来的glowdot.png这个文件,类似QML中绝对路径的file:///...一样,这是其内置的一个路径。查看ImageParticle的帮助文档我们可以知道,Qt提供了3种默认的粒子图片,且都是白色以及像素级的,这是为了使渲染和透明度达到更好的效果。这三个图片的路径分别是qrc:///particleresources/star.png、qrc:///particleresources/glowdot.png、qrc:///particleresources/fuzzydot.png。实际而言,如果你下载了Qt 源码包,应该是还有一个内置图片的,名为noise.png,路径为qrc:///particleresources/noise.png(这里的源码版本是Qt 5.2.0)。你可以在源码包内搜索particlesources这个文件夹,其内容如下:


(2)Burst and Pulse

第一节的代码挺多的,后面的都稍微要少一些。第二个内容从标题可以得知其使用粒子构建了爆炸和脉冲效果。

先看看显示效果:


这些粒子会以这两个文字为中心交替四散开来。


burstandpulse.qml:

import QtQuick 2.0
import QtQuick.Particles 2.0

Rectangle {                                        // 还是矩形背景,不过定义了一个属性用来进行逻辑判断
    width: 320
    height: 480
    color: "black"
    property bool lastWasPulse: false
    Timer {
        interval: 3500                     // 3.5秒定时器
        triggeredOnStart: true
        running: true
        repeat: true
        onTriggered: {
        //! [0]
            if (lastWasPulse) {             // 如果上次是脉冲
                burstEmitter.burst(500);    // burstEmitter调用burst发射500个粒子(一次)
                lastWasPulse = false;
            } else {
                pulseEmitter.pulse(500);    // 这里的pulse(500)会先检查pulseEmitter是否在运行,如果没有则会将它激活500毫秒,然后停止。所以虽然pulseEmitter和burstEmitter的代码一模一样,但粒子效果不同,由于Emitter每秒发射1000个粒子,0.5秒也是500个,但它是在一个持续时间内发射的,因此粒子带相对burstEmitter更宽。
                lastWasPulse = true;
            }
        //! [0]
        }
    }
    ParticleSystem {                        // 这里将ParticleSystem作为ImageParticle、Emitter的父对象,它们就不再需要指定系统
        id: particles
        anchors.fill: parent
        ImageParticle {                     // 基本图像粒子
            source: "qrc:///particleresources/star.png"
            alpha: 0
            colorVariation: 0.6             // 0.6保证了丰富的色彩
        }

        Emitter {                           // 与上面的例子不同,这个粒子系统包含两个发射器
            id: burstEmitter
            x: parent.width/2               // 最主要的是,这里的Emitter没有了属性绑定,因此需要在定时器中手动调用burst和pulse
            y: parent.height/3
            emitRate: 1000
            lifeSpan: 2000
            enabled: false
            velocity: AngleDirection{magnitude: 64; angleVariation: 360}  // 这里使用了AngleDirection以使用角度定义粒子行为,magnitude指明了在该角度的运动距离,angleVariation使粒子方向随机从0到其大小之间产生。这里也就是一个圆
            size: 24
            sizeVariation: 8
            Text {
                anchors.centerIn: parent    
                color: "white"
                font.pixelSize: 18
                text: "Burst"
            }
        }
        Emitter {
            id: pulseEmitter
            x: parent.width/2
            y: 2*parent.height/3
            emitRate: 1000
            lifeSpan: 2000
            enabled: false
            velocity: AngleDirection{magnitude: 64; angleVariation: 360}
            size: 24
            sizeVariation: 8
            Text {
                anchors.centerIn: parent
                color: "white"
                font.pixelSize: 18
                text: "Pulse"
            }
        }
    }
}


(3)Custom Emitter

在这一节中我们将了解到如何基于Emitter定义更复杂的粒子行为。

运行效果相当华丽:



动态效果更棒,ok,直接上代码:

import QtQuick 2.0
import QtQuick.Particles 2.0

ParticleSystem {                    // 我们可以直接将ParticleSystem作为根项目
    id: sys
    width: 360
    height: 600
    running: true
    Rectangle {                     // 进一步将背景Rectangle作为第一个子项目
        z: -1                       // 这里z: -1不写也行,不过对于背景元素这样写是好习惯
        anchors.fill: parent
        color: "black"
    }

    property real petalLength: 180    // 定义了花瓣长度和花瓣旋转属性
    property real petalRotation: 0
    NumberAnimation on petalRotation {
        from: 0;
        to: 360;
        loops: -1;                   // 等同于loops: Animation.infinite
        running: true
        duration: 24000
    }

    function convert(a) {return a*(Math.PI/180);}  // JavaScript函数,角度转弧度
    Emitter {
        lifeSpan: 4000
        emitRate: 120
        size: 12
        anchors.centerIn: parent
        //! [0]
        onEmitParticles: {                        // 从名字可以看出,这是一个信号处理函数,类似信号槽机制中的槽函数,它将在粒子被发出时触发。我们可以在这个函数中使用一个JavaScript数组存放我们的粒子群,并以此改变这些粒子的属性,完成复杂的显示效果。但是执行JavaScript计算的效率是比较慢的,所以在包含大量粒子的系统中不建议这样使用。
            for (var i=0; i<particles.length; i++) {                                // 第二点  
                var particle = particles[i];
                particle.startSize = Math.max(02,Math.min(492,Math.tan(particle.t/2)*24)); 
                var theta = Math.floor(Math.random() * 6.0);                     
                particle.red = theta == 0 || theta == 1 || theta == 2 ? 0.2 : 1;  
                particle.green = theta == 2 || theta == 3 || theta == 4 ? 0.2 : 1;
                particle.blue = theta == 4 || theta == 5 || theta == 0 ? 0.2 : 1;
                theta /= 6.0;
                theta *= 2.0*Math.PI;
                theta += sys.convert(sys.petalRotation);//Convert from degrees to radians
                particle.initialVX = petalLength * Math.cos(theta);
                particle.initialVY = petalLength * Math.sin(theta);
                particle.initialAX = particle.initialVX * -0.5;
                particle.initialAY = particle.initialVY * -0.5;
            }
        }
        //! [0]
    }

    ImageParticle {
        source: "qrc:///particleresources/fuzzydot.png"
        alpha: 0.0
    }
}
第二点:我们慢慢剖析这段JavaScript代码:首先使用for针对每个粒子进行操作,要注意这里取到的粒子是所有生命周期内的粒子。然后通过 patircle.t 取到粒子从产生到现在的时间,单位秒。注意这个时间不是针对单个粒子,而是整个粒子系统的。02限定了粒子的最小初始尺寸,492限制了其最大的初始尺寸。通过这个算式,我们得到了一个从2到492的循环数,但是由于tan 的存在,这个数值在越大的时候会增加得越快。

接着我们产生了一个0-6的随机数,接下来对通过这个随机数设置粒子的RGB值,当theta == 0时,r=0.2,g=1,b=0.2,实际也就是"#51FF51"。theta == 1时,为“#51FFFF”,其它类似,这样我们使用三个式子得到了6种色彩。

然后theta /= 6.0重新得到了一个0-1的随机数,然后乘以2π得到一个弧度值,并加上粒子系统当前的旋转角度(之前定义的属性)。接着我们由此又赋予了粒子相应的初始速度与初始加速度,可以看到,初始加速度与速度成反比。自此,我们的“粒子花瓣”就构成了。

最后不能忘了我们的基本元素ImageParticle,这里采用了fuzzydot元素,大家可以换成star或是glowdot,看看这几个元素显示效果的区别。


(4)Emit Mask

这个小例子展示了QML将图像粒子化的功能。除了粒子化,其粒子的消逝和产生还带来了一种流动的视觉体验。显示效果如下:



其代码不过短短30行。emitmask.qml:

import QtQuick 2.0
import QtQuick.Particles 2.0

Rectangle {
    color: "goldenrod"                                   // 一种比较浓郁的黄色
    width: 400
    height: 400
    ParticleSystem {
        width: 300                                      // 我们同样可以设置粒子系统的尺寸
        height: 300
        anchors.centerIn: parent

        ImageParticle {
            source: "qrc:///particleresources/glowdot.png"   // 如果使用star,更有一种流水的波光凌凌的感觉
            z: 2                                          // z属性继承自item,同样为了保证粒子不被覆盖。这里可以不写
            anchors.fill: parent
            color: "#336666CC"                           // 定义了一个ARGB的颜色值,33为透明度,00-FF透明度逐渐降低。这样不用再额外设置alpha
            colorVariation: 0.0                          // 保证粒子色彩一致
        }

        Emitter {
            anchors.fill: parent
            emitRate: 6000
            lifeSpan: 720
            size: 10
            //! [0]
            shape: MaskShape {                          // shape属性可以定义为直线,椭圆以及这里的MaskShape,这样Emitter会将粒子随机发射到规定的形状区域内
                source: "../../images/starfish_mask.png" // 这里我们可以使用自己的图片,使用绝对路径或是相对路径
            }
            //! [0]
        }

    }
}



(5)Maximum Emitted
这个例子展示了在Emitter中限制粒子数量的方法。通过点击屏幕产生一组发散的粒子:



maximumEmitted.qml:

import QtQuick 2.0
import QtQuick.Particles 2.0

Rectangle {
    color: "black"
    width: 360
    height: 540
    ParticleSystem {
        id: sys
        anchors.fill: parent
        onEmptyChanged: if (empty) sys.pause();  // empty是ParticleSystem的一个属性,当该系统中没有“活着”的粒子时,这个值为true。显而易见,onEmptyChanged则是该属性所绑定的一个槽函数(QML 中叫Handler,处理者)。当没有粒子显示时,我们将该系统暂停以节省资源。
        ImageParticle {
            system: sys
            id: cp
            source: "qrc:///particleresources/glowdot.png"
            colorVariation: 0.4
            color: "#000000FF"                 // 这里将粒子设置为全透明的蓝色,但由于上的的0.4,因此粒子群并不是全蓝的
        }

        Emitter {
            //burst on click
            id: bursty
            system: sys                 // 由于这里的Emitter被ParticleSystem包含,这句并不是必须的
            enabled: ma.pressed         // 将enabled与pressed信号绑定,当按键或手指抬起Emitter即停止
            x: ma.mouseX
            y: ma.mouseY
            emitRate: 16000
            maximumEmitted: 4000        // 最大的粒子数为4000个,如果屏蔽这句话,按住鼠标不松开,粒子会被持续发射,而不是现在这样一圈接一圈
            acceleration: AngleDirection {angleVariation: 360; magnitude: 360; }  // 360度方向,距离360
            size: 8
            endSize: 16
            sizeVariation: 4
        }

        MouseArea {
            anchors.fill: parent
            onPressed: sys.resume()
            id: ma
        }
    }
}


(6)Shape and Direction

这个例子通过控制粒子的形状和方向创建了一个入口效果。四周的粒子向中心涌入,变小并消逝掉。



shapeanddirection.qml:

import QtQuick 2.0
import QtQuick.Particles 2.0

Rectangle {
    id: root
    width: 360
    height: 540
    color: "black"
    Image {
        anchors.fill: parent
        source: "../../images/portal_bg.png"      // 这是一张星空的背景图
    }

    ParticleSystem {
        id: particles
        anchors.fill: parent

        ImageParticle {
            groups: ["center","edge"]           // 这个属性继承自ImageParticle的父类ParticlePainter,不同的Emitter可以使用不同的组成员
            anchors.fill: parent
            source: "qrc:///particleresources/glowdot.png"
            colorVariation: 0.1
            color: "#009999FF"
        }

        Emitter {
            anchors.fill: parent
            group: "center"          // 选择一个组成员进行发射。默认值为"",这与ImageParticle的groups 的默认值相同。
            emitRate: 400
            lifeSpan: 2000
            size: 20
            sizeVariation: 2
            endSize: 0              // 设置粒子的结束大小为0,这样形成粒子逐渐远离直至消失的效果
            //! [0]
            shape: EllipseShape {fill: false}  // 以椭圆形状覆盖,fill: false 表示仅覆盖边缘
            velocity: TargetDirection {      // 不同于PointDirection,AngleDirection,这里使用了TargetDirection(目标方向),还有一个未登场的是CumulativeDirection(累加方向)。这4个类型一般来说已经足以解决我们的问题
                targetX: root.width/2     // 目标点X坐标
                targetY: root.height/2    // 目标点Y坐标
                proportionalMagnitude: true   // 如果该值为false,magnitude的值为粒子每秒移动的像素值,如果被设置为true,则这样计算:
                magnitude: 0.5              // 比如此处粒子的产生点为(360,270),目标点为(180,270),那么移动速度为180*0.5,即每秒90个像素值,这样刚好在粒子两秒的生命周期内达到中心点。
            }
            //! [0]
        }

        Emitter {                 // 这个Emitter产生周围飘散的粒子
            anchors.fill: parent
            group: "edge"
            startTime: 2000      // 这个属性设置使得程序一开始就能够展示两秒后的效果,这样就略过了粒子生成的过程
            emitRate: 2000
            lifeSpan: 2000
            size: 28
            sizeVariation: 2
            endSize: 16
            shape: EllipseShape {fill: false}
            velocity: TargetDirection {
                targetX: root.width/2
                targetY: root.height/2
                proportionalMagnitude: true
                magnitude: 0.1              // 同上面所说,这里的0.1使得这些粒子只能运动36个像素点便消逝掉
                magnitudeVariation: 0.1     // 设置这个属性使得不必所有粒子运动速度都相同
            }
            acceleration: TargetDirection { // 同样可将TargetDirection应用于加速度
                targetX: root.width/2
                targetY: root.height/2
                targetVariation: 200         // 目标速度
                proportionalMagnitude: true
                magnitude: 0.1
                magnitudeVariation: 0.1
            }
        }
    }
}


(7)TrailEmitter

这个例子展示了如何使用TrailEmitter构建跟随粒子,并创建了一个火焰场景。



trailemitter:

import QtQuick 2.0
import QtQuick.Particles 2.0

Rectangle {
    id: root
    width: 360
    height: 540
    color: "black"

    ParticleSystem {
        id: particles
        anchors.fill: parent

        ImageParticle {                       // 这次我们在系统中创建了2种图像粒子,并进行分组以用在不同的位置上
            id: smoke
            system: particles
            anchors.fill: parent
            groups: ["A", "B"]
            source: "qrc:///particleresources/glowdot.png"
            colorVariation: 0
            color: "#00111111"                // 灰色
        }
        ImageParticle {
            id: flame
            anchors.fill: parent
            system: particles
            groups: ["C", "D"]
            source: "qrc:///particleresources/glowdot.png"
            colorVariation: 0.1
            color: "#00ff400f"                // 橘红
        }

        Emitter {                            // 我们先取C组橘红粒子来创建底部的火焰
            id: fire
            system: particles
            group: "C"

            y: parent.height
            width: parent.width

            emitRate: 350
            lifeSpan: 3500

            acceleration: PointDirection {y: -17; xVariation: 3 }  // 使粒子向上漂移,并能够轻微地左右摆动
            velocity: PointDirection { xVariation: 3}

            size: 24
            sizeVariation: 8
            endSize: 4
        }

        TrailEmitter {                       // TrailEmitter类似Emitter,但是用来创建跟随粒子
            id: fireSmoke
            group: "B"                       // 本身粒子种类
            system: particles
            follow: "C"                      // 跟随粒子种类
            width: root.width
            height: root.height - 68         // 使下方火焰区域内不会出现烟雾

            emitRatePerParticle: 1           // 跟随粒子发射的比率
            lifeSpan: 2000

            velocity: PointDirection {y:-17*6; yVariation: -17; xVariation: 3}
            acceleration: PointDirection {xVariation: 3}

            size: 36
            sizeVariation: 8
            endSize: 16
        }

        TrailEmitter {               // 串起的火苗
            id: fireballFlame
            anchors.fill: parent
            system: particles
            group: "D"
            follow: "E"

            emitRatePerParticle: 120  // 由于这里的跟随粒子没有定义速度与加速度,因此在出现后就被固定了。但我们依然可以靠产生和消逝实现动画
            lifeSpan: 180             // 因此这里的生命周期特别短,如果要实现一长条火焰,可以增大这个数值
            emitWidth: TrailEmitter.ParticleSize
            emitHeight: TrailEmitter.ParticleSize
            emitShape: EllipseShape{}   // 设置跟随区域

            size: 16
            sizeVariation: 4
            endSize: 4
        }

        TrailEmitter {
            id: fireballSmoke
            anchors.fill: parent
            system: particles
            group: "A"
            follow: "E"

            emitRatePerParticle: 128
            lifeSpan: 2400                         // 由于烟雾需要有自己的运动轨迹,因此生命周期较火苗更长
            emitWidth: TrailEmitter.ParticleSize
            emitHeight: TrailEmitter.ParticleSize
            emitShape: EllipseShape{}

            velocity: PointDirection {yVariation: 16; xVariation: 16}  // 刚产生的烟雾向下运行,随之慢慢向上升腾
            acceleration: PointDirection {y: -16}

            size: 24
            sizeVariation: 8
            endSize: 8
        }

        Emitter {                       // 注意这个Emitter所用的例子组"E"是不存在的,所以实际上它只是一个引导A和D的框架。如果想要清楚地看出这段代码的工作状态,大家可以自己创建一个绿色的图像粒子,并命名群组为E。
            id: balls
            system: particles
            group: "E"

            y: parent.height
            width: parent.width

            emitRate: 2
            lifeSpan: 7000

            velocity: PointDirection {y:-17*4*2; xVariation: 6*6}    // 向上的速度以及向下的加速度
            acceleration: PointDirection {y: 17*2; xVariation: 6*6}  // 使火焰得以腾起,然后下落消失

            size: 8
            sizeVariation: 4
        }

        Turbulence {                                // 最后,为烟雾加上一点气流效果,就像它被风吹着一样,这样带来更好的效果
            anchors.fill: parent
            groups: ["A","B"]
            strength: 32
            system: particles
        }
    }
}

本系列下一篇文章:Qt5官方demo解析集11——Qt Quick Particles Examples - Affectors