Article directory
Effect demo
QML syntaxClickableImageV2.qml
import QtQuick 2.0
// 用于创建一个可点击的项目(Item)
Item {
id:root
// 设置项目的宽度为column子元素的宽度。
width: column.childrenRect.width
height: column.childrenRect.height
// 定义一个名为text的属性,该属性与label的text属性关联,允许在外部访问和修改该属性。
property alias text: label.text
property alias source: image.source
// signal clicked - 声明一个clicked信号,表示项目被点击的事件。
signal clicked
Column {
id:column
spacing: 10
Image {
id: image
sourceSize: Qt.size(90,90)
}
Text {
id: label
width: image.width
// 设置文本的水平对齐方式为居中对齐。
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
color: "#000000"
}
}
MouseArea {
// 设置鼠标区域的大小与父元素(即Item)相同。
anchors.fill: parent
// 当鼠标区域被点击时,触发项目的clicked信号
onClicked: root.clicked()
}
}
QML syntax EasingCurves.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.3
Rectangle {
id: root
property int duration: 3000
property Item ufo: ufo
property Item ufo1: ufo1
width: 600
height: 400
Image {
anchors.fill: parent
source: "pic//2.jpg"
}
ClickableImageV2 {
id: ufo
x: 20; y: root.height-height
text: qsTr('ufo')
clip: false
visible: true
source: "pic//3.jpg"
onClicked: anim.restart()
}
ClickableImageV2 {
id: ufo1
x:120; y: root.height-height
text: qsTr('ufo1')
clip: false
visible: true
source: "pic//4.jpg"
onClicked: anim1.restart()
}
// 并行动画
ParallelAnimation {
id: anim
NumberAnimation {
target: ufo
properties: "y"
to: 20
duration: root.duration
}
NumberAnimation {
target: ufo
properties: "x"
to: 160
duration: root.duration
}
}
// 连续动画 顺序动画按照声明的顺序运行每个子动画:从上到下。
SequentialAnimation {
id: anim1
NumberAnimation {
target: ufo1
properties: "y"
to: 20
// 60% of time to travel up
duration: root.duration * 0.6
}
NumberAnimation {
target: ufo1
properties: "x"
to: 400
// 40% of time to travel sideways
duration: root.duration * 0.4
}
}
}
Clock ball rolling QML source code
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.3
import Qt.labs.calendar 1.0
Rectangle {
id: root
property int duration: 3000
width: 600
height: 400
// 上面的蓝色矩形高度为200像素,下面的矩形固定在天空的底部和根元素的底部。
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: "pic//clock.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
}
}
}
## Clock ball rolling QML explanation
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.3
import Qt.labs.calendar 1.0
Rectangle { id: root property int duration: 3000
宽度: 600
高度: 400
// 上面的蓝色矩形高度为200像素,下面的矩形固定在天空的底部和根元素的底部。
矩形 {
id: 天空
宽度: 父宽度
高度: 200
渐变: 渐变 {
渐变停止 { 位置: 0.0; 颜色: "#0080FF" }
渐变停止 { 位置: 1.0; 颜色: "#66CCFF" }
}
}
矩形 {
id: 地面
锚点.顶: 天空.底部
锚点.底: root.底部
宽度: 父宽度
渐变: 渐变 {
渐变停止 { 位置: 0.0; 颜色: "#00FF00" }
渐变停止 { 位置: 1.0; 颜色: "#00803F" }
}
}
图像 {
id: 球
x: 0; y: root.高度-高度
源: "图片//clock.png"
// 该图像附有一个鼠标区域。如果球被点击,球的位置将重置,动画将重新开始。
鼠标区域 {
锚点.填充: 父
当被点击时: {
球.x = 0
球.y = root.高度-球.高度
球.旋转 = 0
动画.restart()
}
}
}
并行动画 {
id: 动画
顺序动画 {
数字动画 {
目标: 球
属性: "y"
到: 20
持续时间: root.duration * 0.4
缓动类型: Easing.OutCirc
}
数字动画 {
目标: 球
属性: "y"
到: root.高度-球.高度
持续时间: root.duration * 0.6
缓动类型: Easing.OutBounce
}
}
数字动画 {
目标: 球
属性: "x"
到: root.宽度-球.宽度
持续时间: root.duration
}
旋转动画 {
目标: 球
属性: "旋转"
到: 720
持续时间: root.duration
}
}
}
grammar explanation
This code uses the QtQuick library to create a user interface. Among them, Rectangle is the root element of the interface.
-
id: root
: Specifies the unique identifier root for the Rectangle element. -
property int duration: 3000
: Defines an integer attribute named duration with an initial value of 3000. -
property Item ufo: ufo
: Defines an Item type attribute named ufo, and the corresponding object is ufo. -
property Item ufo1: ufo1
: Defines an Item type attribute named ufo1, and the corresponding object is ufo1. -
width: 600
: Set the width of the Rectangle to 600. -
height: 400
: Set the height of the Rectangle to 400. -
The Image element is nested inside the Rectangle and is used to display images. anchors.fill: parent indicates that the size of the image is the same as that of the parent element Rectangle, and source specifies that the source of the image is "pic//2.jpg".
-
The ClickableImageV2 element represents a clickable image object. The id attribute specifies a unique identifier for the ufo, and the other attributes set its position, text, visibility, and image source. The onClicked event specifies that when the image object is clicked, the restart() method of the anim animation object is executed.
-
Similarly, another ClickableImageV2 element is defined, which represents another clickable image object with a unique id, position, text, visibility, and image source. When the image object is clicked, the restart() method of the anim1 animation object is executed.
-
The ParallelAnimation element represents a parallel animation, which contains two NumberAnimation elements. The first NumberAnimation object sets the target as the ufo object, and moves it to the position of y=20 by changing the y property, and the duration is the time specified by root.duration. The second NumberAnimation object sets the target as the ufo object, and moves it to the position of x=160 by changing the x property, and the duration is also root.duration.
-
The SequentialAnimation element represents a continuous animation, which contains two NumberAnimation elements. These animations will be executed sequentially and sequentially. The first animation sets the target as the ufo1 object, and moves it to the position of y=20 by changing the y property, and the duration is root.duration * 0.6, which is 60% of the movement time. The second animation sets the target as the ufo1 object, and moves it to the position of x=400 by changing the x property, and the duration is root.duration * 0.4, which is 40% of the movement time.
The entire code block implements a user interface that includes images, clickable image objects, and parallel and sequential animation effects.
reference
cartoon
Animations are applied to property changes. Animations define an interpolation curve from one value to another as the property value changes. These animation curves create smooth transitions from one value to another.
An animation is defined by a sequence of target properties to animate, an easing curve for the interpolation curve, and a duration. All animations in Qt Quick are controlled by the same timer and are therefore synchronous. This improves the performance and visual quality of animations.
Animations use value interpolation to control how properties change
This is a basic concept. QML is based on elements, attributes and scripts. Each element provides dozens of attributes, and each attribute is waiting for you to activate. In the books, you'll see it's a spectacular arena.
You'll find yourself looking at some animations and admiring their beauty, but also your creative genius. Remember: animations control property changes, and every element has dozens of properties at your disposal.
// AnimationExample.qml
import QtQuick
Image {
id: root
source: "assets/background.png"
property int padding: 40
property int duration: 4000
property bool running: false
Image {
id: box
x: root.padding;
y: (root.height-height)/2
source: "assets/box_green.png"
NumberAnimation on x {
to: root.width - box.width - root.padding
duration: root.duration
running: root.running
}
RotationAnimation on rotation {
to: 360
duration: root.duration
running: root.running
}
}
MouseArea {
anchors.fill: parent
onClicked: root.running = true
}
}
The example above shows an attribute applied to the x and rotation properties. Each animation has a duration of 4000 milliseconds. Animation start x gradually moves the object's x coordinate to 240 pixels. The rotation animation runs 360 degrees from the current angle. These two animations run in parallel and are clicked on the MouseArea.
You can do this by changing the to and duration properties, or you can add another animation (for example, on opacity or even scale). Combine these and it looks like the object is disappearing into deep space. Try it!
animated elements
There are several types of animated elements, each optimized for a specific use case. Here is a list of the most famous animations:
-
PropertyAnimation - Shows changes in property values
-
NumberAnimation - Shows changes in real type values
-
ColorAnimation - animates changes in color values
-
RotationAnimation - Animates changes in rotation values
Besides these basic and widely used animation elements, Qt Quick also provides more specialized animations for specific use cases:
-
PauseAnimation - Provides a pause for animations
-
SequentialAnimation - allows animations to run sequentially
-
ParallelAnimation - Allows animations to run in parallel
-
AnchorAnimation - Animate changes in anchor values
-
ParentAnimation - fires changes in parent values
-
SmoothedAnimation - allows properties to track values smoothly
-
SpringAnimation - Allows properties to track values in a spring-like motion
-
PathAnimation - animates items along a path
-
Vector3dAnimation - animation showing changes in QVector3d values
Later we will learn how to create an animation sequence. When working with more complex animations, it is sometimes necessary to change properties or run scripts during an ongoing animation. For this purpose, Qt Quick provides action elements, which can be used anywhere other animated elements can be used:
-
PropertyAction - Specifies an immediate property change during an animation
-
ScriptAction - Defines a script to run during the animation
The main animation types are discussed in this chapter using small, focused examples.
apply animation
Animations can be applied in various ways:
Property animation - runs automatically after the element is fully loaded
Property Behavior - run automatically when property value changes
Independent animation - run when using an explicit start animation with start() or running is set to true (e.g. via property binding)
Later we will also see how to use animations in state transitions.
Clickable Image V2
To demonstrate the use of animations, we reused the ClickableImage component from the previous chapter and extended it with a text element.
// ClickableImageV2.qml
// Simple image which can be clicked
import QtQuick
Item {
id: root
width: container.childrenRect.width
height: container.childrenRect.height
property alias text: label.text
property alias source: image.source
signal clicked
Column {
id: container
Image {
id: image
}
Text {
id: label
width: image.width
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
color: "#ececec"
}
}
MouseArea {
anchors.fill: parent
onClicked: root.clicked()
}
}
To organize the elements below the image, we used a column locator and calculated the width and height from the childrenRect property of the column. We expose text and image source properties, and a click signal. We also want the text to be as wide as the image and to wrap. We do this by using the wrapMode property of the text element.
Parent/Child Geometry Dependencies
Due to the inversion of geometry dependencies (parent geometry depends on child geometry), we cannot set width/height on ClickableImageV2 as this would break our width/height binding.
If the item is more like a container for other items and should adapt to the parent's geometry, you should prefer the child's geometry to be dependent on the parent's geometry.
rising object
All three objects are at the same y position (y=200). They all need to go to y=40 and each uses a different method, with different side effects and characteristics.
first object
The first object uses the Animation on strategy. Animation will start immediately.
ClickableImageV2 {
id: greenBox
x: 40; y: root.height-height
source: "assets/box_green.png"
text: qsTr("animation on property")
NumberAnimation on y {
to: 40; duration: 4000
}
}
When an object is clicked, its y position is reset to the starting position, and this applies to all objects. On the first object, the reset has no effect as long as the animation is running.
This can be visually disruptive because the y-position is set to a new value for a fraction of a second before the animation starts. Such competing property changes should be avoided.
second object
The second object is animated using Behavior on. This behavior tells the property that it should animate every change in value. This behavior can be disabled by setting enabled: false on the Behavior element.
ClickableImageV2 {
id: blueBox
x: (root.width-width)/2; y: root.height-height
source: "assets/box_blue.png"
text: qsTr("behavior on property")
Behavior on y {
NumberAnimation {
duration: 4000 }
}
onClicked: y = 40
// random y on each click
// onClicked: y = 40 + Math.random() * (205-40)
}
When you click on the object it will start moving (its y position will be set to 40). Another click has no effect because the position is already set.
You can try using a random value like 40 + (Math.random() * (205-40)) for the y position. You will see that the object will always animate to the new position and adjust its speed to match the animation duration defined by 4 seconds to the destination.
third object
The third object uses independent animation. Animation is defined as its own element and can appear almost anywhere in the document.
ClickableImageV2 {
id: redBox
x: root.width-width-40; y: root.height-height
source: "assets/box_red.png"
onClicked: anim.start()
// onClicked: anim.restart()
text: qsTr("standalone animation")
NumberAnimation {
id: anim
target: redBox
properties: "y"
to: 40
duration: 4000
}
}
Clicking will use the animation's start animation start() function. Every animation has start(), stop(), resume() and restart() functions. Animation itself contains much more information than other earlier animation types.
We need to define target, which is the element to animate, and the name of the property we want to animate. We also need to define a to value, in this case a from value, which allows the animation to start over.
Clicking on the background will reset all objects to their initial positions. The first object cannot be restarted unless a reload of the element is triggered by restarting the program.
Other ways to control animation
Another way to start/stop the animation is to bind a property to the running animation's property. This is especially useful when user input controls properties:
NumberAnimation {
// [...]
// animation runs when mouse is pressed
running: area.pressed
}
MouseArea {
id: area
}
Spiral curve
A property's value change can be controlled by animation. Easing properties allow interpolation curves that affect property changes.
All the animations we define now use linear interpolation because the animation's initial easing type is Easing.Linear. This is best visualized with a small graph, where the y-axis is the property being animated and the x-axis is time (duration). Linear interpolation adds the value from when the from animation begins to the value at the end of the to animation. So the easing type defines the transition curve.
The type of easing should be chosen carefully to support the natural fit of moving objects. For example, when a page slides out, the page should initially slide out slowly, then gain momentum to eventually slide out at high speed, similar to turning a page.
Animations should not be overused.
Like other aspects of UI design, animation should be carefully designed to support the UI flow, not dominate it. Eyes are very sensitive to moving objects, and animations can easily distract users.
In the next example we'll try out some spirals. Each easing curve is represented by a clickable image that, when clicked, animates in the square, then triggers a restart() to run the animation with the new curve.
The code for this example gets a little more complicated. We first create a Mesh with EasingTypes and a Box which is controlled by the easing type. The easing type only shows the curves that the box uses for animation. When the user clicks on the spiral, the box moves in one direction according to the spiral. The animation itself is a standalone animation with the target set to a box and configured as an x-property animation with a duration of 2 seconds.
The interior of EasingType presents curves in real time, and interested readers can take an example in EasingCurves.
// EasingCurves.qml
import QtQuick
import QtQuick.Layouts
Rectangle {
id: root
width: childrenRect.width
height: childrenRect.height
color: '#4a4a4a'
gradient: Gradient {
GradientStop {
position: 0.0; color: root.color }
GradientStop {
position: 1.0; color: Qt.lighter(root.color, 1.2) }
}
ColumnLayout {
Grid {
spacing: 8
columns: 5
EasingType {
easingType: Easing.Linear
title: 'Linear'
onClicked: {
animation.easing.type = easingType
box.toggle = !box.toggle
}
}
EasingType {
easingType: Easing.InExpo
title: "InExpo"
onClicked: {
animation.easing.type = easingType
box.toggle = !box.toggle
}
}
EasingType {
easingType: Easing.OutExpo
title: "OutExpo"
onClicked: {
animation.easing.type = easingType
box.toggle = !box.toggle
}
}
EasingType {
easingType: Easing.InOutExpo
title: "InOutExpo"
onClicked: {
animation.easing.type = easingType
box.toggle = !box.toggle
}
}
EasingType {
easingType: Easing.InOutCubic
title: "InOutCubic"
onClicked: {
animation.easing.type = easingType
box.toggle = !box.toggle
}
}
EasingType {
easingType: Easing.SineCurve
title: "SineCurve"
onClicked: {
animation.easing.type = easingType
box.toggle = !box.toggle
}
}
EasingType {
easingType: Easing.InOutCirc
title: "InOutCirc"
onClicked: {
animation.easing.type = easingType
box.toggle = !box.toggle
}
}
EasingType {
easingType: Easing.InOutElastic
title: "InOutElastic"
onClicked: {
animation.easing.type = easingType
box.toggle = !box.toggle
}
}
EasingType {
easingType: Easing.InOutBack
title: "InOutBack"
onClicked: {
animation.easing.type = easingType
box.toggle = !box.toggle
}
}
EasingType {
easingType: Easing.InOutBounce
title: "InOutBounce"
onClicked: {
animation.easing.type = easingType
box.toggle = !box.toggle
}
}
}
Item {
height: 80
Layout.fillWidth: true
Box {
id: box
property bool toggle
x: toggle ? 20 : root.width - width - 20
anchors.verticalCenter: parent.verticalCenter
gradient: Gradient {
GradientStop {
position: 0.0; color: "#2ed5fa" }
GradientStop {
position: 1.0; color: "#2467ec" }
}
Behavior on x {
NumberAnimation {
id: animation
duration: 500
}
}
}
}
}
}
Play the example to see how the speed changes in the animation. Some animations feel more natural to objects, while others are off-putting.
In addition to duration and easing.type, you can fine-tune the animation. For example, the General PropertyAnimation type (from which most animations inherit) also supports easing.amplitude , easing.overshoot , and easing.period properties, which allow you to fine-tune the behavior of specific easing curves.
Not all easing curves support these parameters. Please refer to the relax table (opens new window) documentation from PropertyAnimation to check if the easing parameters have an effect on the easing curve.
Choose the right animation
Choosing the right animation for elements in the context of the user interface is critical to the result. Remember that animations should support the UI flow; not irritate the user.
group animation
Often the animation will be more complex than just animating a single property. You may wish to run several animations simultaneously or one after the other, or even execute a script between two animations.
For this, grouped animations can be used. As the name suggests, animations can be grouped. Grouping can be done in two ways: parallel or sequential. You can use SequentialAnimation or ParallelAnimation elements, which act as animation containers for other animated elements. These grouped animations are themselves animations and can be used exactly as-is.
parallel animation
All immediate child animations of a parallel animation run in parallel on startup. This allows you to animate different properties simultaneously.
// ParallelAnimationExample.qml
import QtQuick
BrightSquare {
id: root
property int duration: 3000
property Item ufo: ufo
width: 600
height: 400
Image {
anchors.fill: parent
source: "assets/ufo_background.png"
}
ClickableImageV3 {
id: ufo
x: 20; y: root.height-height
text: qsTr('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
}
}
}
continuous animation
Sequential animations run each sub-animation in the order declared: top to bottom.
// SequentialAnimationExample.qml
import QtQuick
BrightSquare {
id: root
property int duration: 3000
property Item ufo: ufo
width: 600
height: 400
Image {
anchors.fill: parent
source: "assets/ufo_background.png"
}
ClickableImageV3 {
id: ufo
x: 20; y: root.height-height
text: qsTr('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
}
}
}
nested animation
Grouped animations can also be nested. For example, a continuous animation can have two parallel animations as sub-animations, and so on. We can visualize this with a football example. The idea is to throw a ball from left to right and animate its behavior.
To understand animation, we need to break it down into the overall transformation of an object. We need to remember that an animation is an animation of a property change. The following are the different conversions:
x translation from left to right (X1)
y translation from bottom to top (Y1) followed by translation from top to bottom (Y2) with some bouncing
Rotate 360 degrees (ROT1) for the entire duration of the animation
The entire duration of the animation should be three seconds.
We start with an empty item as the root element with a width of 480 and a height of 300.
import QtQuick
Item {
id: root
property int duration: 3000
width: 480
height: 300
// [...]
}
We have defined our total animation duration as a reference to better synchronize animation parts.
The next step is to add a background, in our case two rectangles with a green and blue gradient.
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" }
}
}
The upper blue rectangle has a height of 200px, and the lower rectangle is anchored to the bottom of the sky and the bottom of the root element.
Let's bring football to grass. The ball is an image, stored under "assets/soccer_ball.png". First, we want to place it in the bottom left corner, near the edge.
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()
}
}
}
The image has a mouse area attached to it. If the ball is clicked, the position of the ball will reset and the animation will start over.
Let's start with a continuous animation of two y-translations.
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
}
}
This specifies that 40% of the total animation duration should be animated up and 60% should be animated down, with each animation running in sequence. The transform animates on a linear path, but currently has no curves. Curves will be added later using easing curves, and at this point we're focusing on animating the transform.
Next, we need to add the x translation. The x translation should run in parallel with the y translation, so we need to wrap the y translation sequence together with the x translation into a parallel animation.
ParallelAnimation {
id: anim
SequentialAnimation {
// ... our Y1, Y2 animation
}
NumberAnimation {
// X1 animation
target: ball
properties: "x"
to: 400
duration: root.duration
}
}
Finally, we want the ball to spin. To do this, we need to add another animation inside the parallel animation. We choose RotationAnimation because it is dedicated to rotation.
ParallelAnimation {
id: anim
SequentialAnimation {
// ... our Y1, Y2 animation
}
NumberAnimation {
// X1 animation
// X1 animation
}
RotationAnimation {
target: ball
properties: "rotation"
to: 720
duration: root.duration
}
}
That's the whole animation sequence. The only thing left is to provide the correct easing curve for the ball's movement. For the Y1 animation we use an Easing.OutCirc curve since this should look more like a circular motion. Y2 is using Easing.OutBounce to make the ball bounce, the bounce should happen at the end (try it with Easing.InBounce and you will see the bounce start immediately).
The X1 and ROT1 animations remain as they are, with a linear curve.
Here is the final animation code for your reference:
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
}
}