QML Book 第五章 动态元素 2

5.1.4 分组动画

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

QML Book 第五章 动态元素 2_第1张图片
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
        }
    }
}
QML Book 第五章 动态元素 2_第2张图片
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
        }
    }
}
QML Book 第五章 动态元素 2_第3张图片
sequentialanimation_sequence

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

QML Book 第五章 动态元素 2_第4张图片
soccer_init

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

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

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

QML Book 第五章 动态元素 2_第5张图片
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" }
        }
    }
QML Book 第五章 动态元素 2_第6张图片
soccer_stage1

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

QML Book 第五章 动态元素 2_第7张图片
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()
            }
        }
    }
QML Book 第五章 动态元素 2_第8张图片
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
    }
}
QML Book 第五章 动态元素 2_第9张图片
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
    }
}
QML Book 第五章 动态元素 2_第10张图片
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
        }
    }

}

你可能感兴趣的:(QML Book 第五章 动态元素 2)