QML Book 第五章 动态元素 2

5.1.4 分组动画

通常的动画会比一个属性的动画更加复杂。我们可能希望同时运行多个动画,或者一个接一个地运行动画,有时又希望在两个动画之间执行一个脚本什么的。此时,分组动画为我们的上述需求提供了一种可能。正如这个名字所表达的那样,我们可以对动画进行分组。分组可以通过两种方式进行:并行或顺序。我们可以使用 ParallelAnimation(并行动画) 或 SequentialAnimation(顺序动画) 元素,它们被用来充当其他动画元素的动画容器。这些组合的动画本身就是动画,并且也可以被独立地使用。

groupedanimation

当启动时,并行动画的所有直接子动画将并行运行。这允许我们同时对不同的属性应用动画。

// parallelanimation.qml
import QtQuick 2.5

BrightSquare {
    id: root
    width: 600
    height: 400
    property int duration: 3000
    property Item ufo: ufo

    Image {
        anchors.fill: parent
        source: "assets/ufo_background.png"
    }


    ClickableImageV3 {
        id: ufo
        x: 20; y: root.height-height
        text: 'ufo'
        source: "assets/ufo.png"
        onClicked: anim.restart()
    }

    ParallelAnimation {
        id: anim
        NumberAnimation {
            target: ufo
            properties: "y"
            to: 20
            duration: root.duration
        }
        NumberAnimation {
            target: ufo
            properties: "x"
            to: 160
            duration: root.duration
        }
    }
}
parallelanimation_sequence

顺序动画将首先运行第一个子动画,然后继续执行接下来的每个子动画。

// sequentialanimation.qml
import QtQuick 2.5

BrightSquare {
    id: root
    width: 600
    height: 400
    property int duration: 3000

    property Item ufo: ufo

    Image {
        anchors.fill: parent
        source: "assets/ufo_background.png"
    }

    ClickableImageV3 {
        id: ufo
        x: 20; y: root.height-height
        text: 'rocket'
        source: "assets/ufo.png"
        onClicked: anim.restart()
    }

    SequentialAnimation {
        id: anim
        NumberAnimation {
            target: ufo
            properties: "y"
            to: 20
            // 60% of time to travel up
            duration: root.duration*0.6
        }
        NumberAnimation {
            target: ufo
            properties: "x"
            to: 400
            // 40% of time to travel sideways
            duration: root.duration*0.4
        }
    }
}
sequentialanimation_sequence

分组动画也可以是嵌套的,例如,一个顺序的分组动画可以包含两个并行的分组动画作为子动画,等等。我们可以用一个足球的例子来把它形象化。它的思路是把球从左向右扔,并让它动起来的行为。

soccer_init

为了理解动画,我们需要将其分解为对象的整体转换。我们需要记住动画要做的是动态的属性改变。以下是不同的转换:

  • 一个从左到右的 x 值的转换(到达 X1)
  • 一个 y 值从下到上(到达 Y1)的转换,紧接着一个从上到下(到达 Y2)的跳跃转换
  • 在整个动画持续时间内旋转 360 度(旋转到 ROT1)

动画的整个持续时间应该是 3 秒。

soccer_plan

我们从一个空的 Item 元素开始,它是宽为 480 和高为 300 的根元素。

import QtQuick 2.5

Item {
    id: root
    width: 480
    height: 300
    property int duration: 3000

    ...
}

我们已经定义了整个动画的持续时间作为参考,以更好地同步动画部分。

下一步是添加背景,在我们的例子中是两个矩形,绿色和蓝色的渐变。

    Rectangle {
        id: sky
        width: parent.width
        height: 200
        gradient: Gradient {
            GradientStop { position: 0.0; color: "#0080FF" }
            GradientStop { position: 1.0; color: "#66CCFF" }
        }
    }
    Rectangle {
        id: ground
        anchors.top: sky.bottom
        anchors.bottom: root.bottom
        width: parent.width
        gradient: Gradient {
            GradientStop { position: 0.0; color: "#00FF00" }
            GradientStop { position: 1.0; color: "#00803F" }
        }
    }
soccer_stage1

上面的蓝色矩形的高度为 200 像素,而下面的部分则是顶部被锚定在天空的底部并且底部被锚定在根元素上的。

soccer_ball

让我们把球放到绿色的部分上。这个球是一个图像,存储在 “assets/soccer_ball.png” 中,大家也可以将上面的图片另存为我们自己的图像资源。开始的时候,我们把它放在左下角,靠近边缘的位置。

    Image {
        id: ball
        x: 0; y: root.height-height
        source: "assets/soccer_ball.png"

        MouseArea {
            anchors.fill: parent
            onClicked: {
                ball.x = 0;
                ball.y = root.height-ball.height;
                ball.rotation = 0;
                anim.restart()
            }
        }
    }
soccer_stage2

图像上有一个鼠标点击区域。如果球被点击,球的位置将被重置,动画重新启动。

让我们先从两个 y 值转换的顺序分组动画开始。

SequentialAnimation {
    id: anim
    NumberAnimation {
        target: ball
        properties: "y"
        to: 20
        duration: root.duration * 0.4
    }
    NumberAnimation {
        target: ball
        properties: "y"
        to: 240
        duration: root.duration * 0.6
    }
}
soccer_stage3

上面的代码指定了前一个动画 40% 的动画持续时间和后一个动画 60% 的持续时间。一个接一个的动画执行作为一个顺序分组动画。转换是在线性的路径上动态执行的,但是目前还没有使用曲线效果。曲线效果将在稍后使用缓冲曲线属性添加,此时我们只专注于使转换动画。

接下来,我们需要添加 x 值的转换。 x 值的转换应该与 y 值的转换同时运行,所以我们需要将执行 y 值的转换的顺序分组动画与 x 值的转换动画一起封装到一个平行分组动画中去。

ParallelAnimation {
    id: anim
    SequentialAnimation {
        // ... our Y1, Y2 animation
    }
    NumberAnimation { // X1 animation
        target: ball
        properties: "x"
        to: 400
        duration: root.duration
    }
}
soccer_stage4

最后,我们希望球旋转起来。为此,我们需要向并行动画添加另一个动画。我们选择旋转动画(RotationAnimation),因为它是专门用于处理元素旋转的。

ParallelAnimation {
    id: anim
    SequentialAnimation {
        // ... our Y1, Y2 animation
    }
    NumberAnimation { // X1 animation
        // X1 animation
    }
    RotationAnimation {
        target: ball
        properties: "rotation"
        to: 720
        duration: root.duration
    }
}

这就是整个动画序列。剩下的一件事就是为球的运动提供正确的缓冲曲线。对于 Y1 动画我们使用 Easing.OutCirc 曲线,这看起来更像一个圆形运动。Y2 的动画是一个增强效果,我们使用 Easing.OutBounce 曲线,使球可以在结束时发生反弹(使用 Easing.InBounce 曲线的话你会看到反弹效果在开始时就会发生)。剩下的 X1 和 ROT1 动画继续使用线性曲线即可。

    ParallelAnimation {
        id: anim
        SequentialAnimation {
            NumberAnimation {
                target: ball
                properties: "y"
                to: 20
                duration: root.duration * 0.4
                easing.type: Easing.OutCirc
            }
            NumberAnimation {
                target: ball
                properties: "y"
                to: root.height-ball.height
                duration: root.duration * 0.6
                easing.type: Easing.OutBounce
            }
        }
        NumberAnimation {
            target: ball
            properties: "x"
            to: root.width-ball.width
            duration: root.duration
        }
        RotationAnimation {
            target: ball
            properties: "rotation"
            to: 720
            duration: root.duration
        }
    }

整个示例的完整代码如下所示:

import QtQuick 2.5

Item {
    id: root
    width: 640
    height: 380
    property int duration: 3000

    Rectangle {
        id: sky
        width: parent.width
        height: 200
        gradient: Gradient {
            GradientStop { position: 0.0; color: "#0080FF" }
            GradientStop { position: 1.0; color: "#66CCFF" }
        }
    }
    Rectangle {
        id: ground
        anchors.top: sky.bottom
        anchors.bottom: root.bottom
        width: parent.width
        gradient: Gradient {
            GradientStop { position: 0.0; color: "#00FF00" }
            GradientStop { position: 1.0; color: "#00803F" }
        }
    }

    Image {
        id: ball
        x: 0; y: root.height-height
        source: "assets/soccer_ball.png"

        MouseArea {
            anchors.fill: parent
            onClicked: {
                ball.x = 0;
                ball.y = root.height-ball.height;
                ball.rotation = 0;
                anim.restart()
            }
        }
    }

    ParallelAnimation {
        id: anim
        SequentialAnimation {
            NumberAnimation {
                target: ball
                properties: "y"
                to: 20
                duration: root.duration * 0.4
                easing.type: Easing.OutCirc
            }
            NumberAnimation {
                target: ball
                properties: "y"
                to: root.height-ball.height
                duration: root.duration * 0.6
                easing.type: Easing.OutBounce
            }
        }
        NumberAnimation {
            target: ball
            properties: "x"
            to: root.width-ball.width
            duration: root.duration
        }
        RotationAnimation {
            target: ball
            properties: "rotation"
            to: 720
            duration: root.duration
        }
    }

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

推荐阅读更多精彩内容

  • 注意:最新的构建时间:2016/03/21这章的源代码能够在 Assets[http://qmlbook.gith...
    赵者也阅读 1,278评论 0 1
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,092评论 4 62
  • 我听过最智慧的一句话,“一切都会过去的!” 并坚信这个教导来面对这个世界。 你也有自己听过特别喜欢,特别智慧的一句...
    尚灵心阅读 497评论 0 1
  • 天地有真眸,风云清淡染。 回首一笑间,万物尽骚然。
    平天下之文世界阅读 264评论 3 11
  • 不是所有人单身都代表是可撩状态 有些人对恋爱已没有指望 只想安安静静读几年书 然后发财
    魔力小鸡仔阅读 100评论 0 0