QT QML 文字飘散效果

QML 文字飘散效果

1,目标及展示

首先希望实现文字、图片、控件等在触发后,呈现飘散并消失的效果。在QT例程《Qt Quick Particles Examples》是一个海星点击鼠标后呈现打散的效果,这个效果和最终需要的略有不同,所以我们在它的基础上再加上我需要的一些元素,最终实现如下效果。


图1
图6

2,设计分析

这个项目我们分析可以拆分成两个部分,第一是背景飘散效果,第二是背景逐渐消失效果,然后将这两种效果进行组合,就实现最终需要的效果。
首先先实现飘散效果,QT具有非常强大的粒子化效果,并且只需很少的几行代码就能实现效果。不过对于初次使用qml语言进行开发的设计人员,需要先了解关键字的含义和功能,当然我们可以在例程中微调每个关键字参数,根据效果变化进行快速了解作用,也可以直接通过强大的F1帮助键对关键字说明文档进行全面的了解。
在我们这些关键字有一定认识后,就可以快速进行粒子化设计了。
第二就是逐步消失的效果,这里的方法就有很多了:方法一可以使用PS技术,找个单侧是过渡效果的蒙版图片,利用动画效果逐步遮住显示内容;
方法二可以直接使用qml 中的 LinearGradient:线性过渡的方法来实现,从单侧开始让逐步隐藏,使10%的区域从透明度为0(全透明)过渡到1(不透明)状态,然后让这10%的区域逐渐扩大范围,最终实现背景消失。如果希望过渡区间更加均匀可以把10%改成20%或更大。
还有很多方法可以实现这个效果,从适应性考虑采用的是后者。

3,设计内容

先说下我的开发环境:
qt5.4.2集成的Qt creator 3.4.1版本
首先创建Quick项目,如果自动创建了MainForm.ui.qml,直接删除就可以了,我们直接在main.qml中开发就可以了。
我们在建粒子系统之前,先建立一个画布,画布大小填充父画布。然后在这个画布上添加粒子系统的基本元素:ParticleSystem、Emitter、ImageParticle和应用文件import QtQuick.Particles 2.0代码如下:


import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Particles 2.0

Window {
    width: 800
    height: 320
    visible: true

    Rectangle {
        id: root
        anchors.fill: parent
        color: "#1f1f1f"

        ParticleSystem {
            id: myParticleSystem
        }

        Emitter {
            id: myEmitter
            system: myParticleSystem

            //发射器区域宽和高
            width: 240
            height: parent.height

            //发射频率每秒500个元素
            emitRate: 500
            //每个元素的生命周期是1000毫秒
            lifeSpan: 1000
            //每个元素的大小是16*16像素
            size: 16
        }

        ImageParticle {
            system: myParticleSystem
            //Qt自带粒子图,可以换成自定义图片
            source: "qrc:///particleresources/fuzzydot.png"
            //粒子图使用白色
            color: "white"
        }

    }
}
图2

这个图看来只有粒子,我们还需要动态飘动的效果,下面在粒子发射器中增加如下代码。

velocity: PointDirection {
    x: 150
}
acceleration: PointDirection {
    x: 12
}

水平向右设置150像素/秒的速度,水平向右添加一个12像素/秒^2的加速。如果我们需要让他向左发射时,可以让x:-150,加速设置也是同理,添加一个符号就表示向左加速。如果我们需要向下发射可以y:150,同理向上发射时是y:-150。
这时我们就有了所有粒子向右飘动的界面,但是飘动状态还比较单调。我们可以给他增加一些差异性的元素,让元素更丰富。
让元素在±8*8像素范围内随机变化

sizeVariation: 8

在velocity中增加x/y速度的随机变化

xVariation: 60
yVariation: 20

在acceleration中增加x/y加速度的随机变化

xVariation: 6
yVariation: 5

到此元素飘动就有了雪花飞舞的感觉了,飘动的层次均匀并且舒服。

现在感觉颜色稍微有点单调,我们可以在ImageParticle中对颜色增加随机系数

colorVariation: 0.1

颜色也有一定的波动了,如下图显示


图3

现在我们的任务就是让发射器一边发射一边向右移动,直到移动到界面外,实现飘散的元素飞舞过程。这里我们可以使用动画元素,让发射器在1秒内从窗体的最左边向右侧窗口外移动,实现的代码如下

//数值动画
NumberAnimation {
    id: myAnimation
    //设定动画的目标
    target: myEmitter
    //设定改变的属性是x坐标
    properties: "x"
    //x移动到父窗口的边沿
    to: root.width
    //在1秒内完成移动
    duration: 1000
}

有了动画代码,我们还需要一个触发这个动画的事件,另外粒子系统创建后先不要发送,我们演示时通过鼠标来激活动画和粒子系统。
在粒子系统ParticleSystem添加如下代码,让粒子系统在创建时停止运行。

running: false

然后添加数据触发代码

MouseArea {
    anchors.fill: parent

    //鼠标点击测试
    onClicked: {
        //让myEmitter窗口复位,这样鼠标可重复点击
        if (myEmitter.x > 0)
        {
            myAnimation.stop()
            myEmitter.x = 0;
        }
            
        //激活粒子系统
        myParticleSystem.restart()
        //激活动画
        myAnimation.restart()
    }
}

由于每次动画执行结束后,myEmitter窗口已经在显示窗体以外了,那么在点击时先让myEmitter窗口回到初始位置,我们就实现了点击后重复演示功能。
效果如下


图4

到这里我们第一项飘散的效果就完成了,下一步是实现背景元素逐渐消失的特效。
我们后续会用到LinearGradient元素,需要添加引用import QtGraphicalEffects 1.0。我们首先测试文字,添加如下内容。注意添加的位置,qml添加图层的规则是先建立的(代码行号偏小的)图层在底层,后建立的(代码行号偏大的)图层在顶层,我们文字需要显示在飘飞效果的下面显示,所以代码尽量靠前放置。

Text {
    id:myText
    anchors.centerIn: parent
    text: "Hello world!"
    font.bold: true
    font.pixelSize: 120
    font.family: "微软雅黑"
    visible: false
}

LinearGradient {
    source: myText
    anchors.fill: myText
    start: Qt.point(0, 0)
    end: Qt.point(myText.width, 0)
    gradient: Gradient {
        GradientStop{id: myGradientStart; position: 0.0; color: "#FFFFFFFF"}
        GradientStop{id: myGradientEnd; position: 0.1; color: "#FFFFFFFF"}
    }
}

代码中Text项目被设置为隐藏。是因为我们下面的渐变效果已经基于Text进行渐变覆盖,我们需要显示的是渐变的内容,Text的内容就不再被需要了,且当渐变被设置为透明时,会显示背景的内容,这时Text如果显示出了就不是我们所希望的效果了。
现在文本内容有了,下一步如何让其逐步消失,我们可以用动画控制渐变的定位。
图5
如上图所示,首先让定位点myGradientStart在200毫秒内变成无色,然后让定位点myGradientEnd移动到终点,这个过程同时让myGradientStart跟随移动到终点,但是他们之间需要有个时间差,消失边界才会有个过渡区间。这里我们可以使用顺序动画、并行动画来组合完成这个功能。

最终代码如下

import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Particles 2.0
import QtGraphicalEffects 1.0


Window {
    width: 800
    height: 320
    visible: true

    Rectangle {
        id: root
        anchors.fill: parent
        color: "#1f1f1f"

        Text {
            id:myText
            anchors.centerIn: parent
            text: "Hello world!"
            font.bold: true
            font.pixelSize: 120
            font.family: "微软雅黑"
            visible: false
        }

        LinearGradient {
            source: myText
            anchors.fill: myText
            start: Qt.point(0, 0)
            end: Qt.point(myText.width, 0)
            gradient: Gradient {
                GradientStop{id: myGradientStart; position: 0.0; color: "#FFFFFFFF"}
                GradientStop{id: myGradientEnd; position: 0.1; color: "#FFFFFFFF"}
            }
        }

        ParticleSystem {
            id: myParticleSystem
            running: false
        }

        Emitter {
            id: myEmitter
            system: myParticleSystem
            anchors.verticalCenter: parent.verticalCenter

            //发射器区域宽和高
            width: 240
            height: 180

            //发射频率每秒500个元素
            emitRate: 500
            //每个元素的生命周期是1000毫秒
            lifeSpan: 1000
            //每个元素的大小是16*16像素
            size: 16
            //元素可以在±8*8像素范围内随机变化
            sizeVariation: 8
            //元素发射速度设置,使用点方向模式
            velocity: PointDirection {
                //水平方式速度 150像素/秒
                x: 150
                //随着变量调整
                xVariation: 60
                yVariation: 20
            }
            //元素行进加速度设置,使用点方式模式
            acceleration: PointDirection {
                x: 12
                //随着变量调整
                xVariation: 6
                yVariation: 5
            }
        }

        ImageParticle {
            system: myParticleSystem
            //Qt自带粒子图,可以换成自定义图片
            source: "qrc:///particleresources/fuzzydot.png"
            //粒子图使用白色
            color: "white"
            //颜色随机系数
            colorVariation: 0.1
        }

        ParallelAnimation {
            id: myAnimation

            //数值动画
            NumberAnimation {
                //设定动画的目标
                target: myEmitter
                //设定改变的属性是x坐标
                properties: "x"
                //x移动到父窗口的边沿
                to: root.width
                //在1秒内完成移动
                duration: 1000
            }

            SequentialAnimation {
                PropertyAnimation {
                    target: myGradientStart
                    properties: "color"
                    to: "#00FFFFFF"
                    duration: 200
                }

                ParallelAnimation {
                    PropertyAnimation {
                        target: myGradientEnd
                        properties: "position"
                        to: "1.0"
                        duration: 1000
                    }

                    PropertyAnimation {
                        target: myGradientStart
                        properties: "position"
                        to: "0.9"
                        duration: 1000
                    }
                }

                PropertyAnimation {
                    target: myGradientEnd
                    properties: "color"
                    to: "#00FFFFFF"
                    duration: 200
                }
            }
        }

        MouseArea {
            anchors.fill: parent

            //鼠标点击测试
            onClicked: {
                //让myEmitter窗口复位,这样鼠标可重复点击
                if (myEmitter.x > 0)
                {
                    myAnimation.stop()
                    myEmitter.x = 0;

                    myGradientStart.position = 0.0
                    myGradientStart.color = "#FFFFFFFF"

                    myGradientEnd.position = 0.1
                    myGradientEnd.color = "#FFFFFFFF"
                }

                //激活粒子系统
                myParticleSystem.restart()
                //激活动画
                myAnimation.restart()
            }
        }
    }
}

在以上代码中简单修改,就可以实现图6的效果了。

4,总结

此例程主要使用的功能块粒子系统,给粒子增加一些随机设置参数,可以让粒子系统显示更自然。另外在粒子系统中还有很有趣的功能如精灵显示、粒子影响器等等。另外一个主要的功能就是渐变功能,可以让颜色A过渡到颜色B,及本例程中使用的从无色A过渡到有色B。

大家如果发现文章中存在问题或者需要讨论,可以在简书、博客园、或邮箱将问题进行留言,我会及时修正和更新。
邮箱:whqcxz@163.com
博客园:https://www.cnblogs.com/hiker-blogs/p/12860533.html
简书://www.greatytc.com/p/89a6fe5c4d21
原创:https://www.simbahiker.com/news/0220200428001.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354