Animations

Animations are applied to property changes. An animation defines the interpolation curve from one value to another value when a property value changes. These animation curves create smooth transitions from one value to another.

An animation is defined by a series of target properties to be animated, an easing curve for the interpolation curve, and a duration. All animations in Qt Quick are controlled by the same timer and are therefore synchronized. This improves the performance and visual quality of animations.

Animations control how properties change using value interpolation

This is a fundamental concept. QML is based on elements, properties, and scripting. Every element provides dozens of properties, each property is waiting to get animated by you. In the book, you will see this is a spectacular playing field.

You will catch yourself looking at some animations and just admiring their beauty, and your creative genius, too. Please remember then: animations control property changes and every element has dozens of properties at your disposal.

Unlock the power!

Animations - 图1

  1. // animation.qml
  2. import QtQuick
  3. Image {
  4. id: root
  5. source: "assets/background.png"
  6. property int padding: 40
  7. property int duration: 4000
  8. property bool running: false
  9. Image {
  10. id: box
  11. x: root.padding;
  12. y: (root.height-height)/2
  13. source: "assets/box_green.png"
  14. NumberAnimation on x {
  15. to: root.width - box.width - root.padding
  16. duration: root.duration
  17. running: root.running
  18. }
  19. RotationAnimation on rotation {
  20. to: 360
  21. duration: root.duration
  22. running: root.running
  23. }
  24. }
  25. MouseArea {
  26. anchors.fill: parent
  27. onClicked: root.running = true
  28. }
  29. }

The example above shows a simple animation applied on the x and rotation properties. Each animation has a duration of 4000 milliseconds (msec) and loops forever. The animation on x moves the x-coordinate from the object gradually over to 240px. The animation on rotation runs from the current angle to 360 degrees. Both animations run in parallel and are started as soon as the UI is loaded.

You can play around with the animation by changing the to and duration properties, or you could add another animation (for example, on the opacity or even the scale). Combining these it could look like the object is disappearing into deep space. Try it out!

Animation Elements

There are several types of animation elements, each optimized for a specific use case. Here is a list of the most prominent animations:

  • PropertyAnimation - Animates changes in property values

  • NumberAnimation - Animates changes in qreal-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 an animation

  • SequentialAnimation - Allows animations to be run sequentially

  • ParallelAnimation - Allows animations to be run in parallel

  • AnchorAnimation - Animates changes in anchor values

  • ParentAnimation - Animates changes in parent values

  • SmoothedAnimation - Allows a property to smoothly track a value

  • SpringAnimation - Allows a property to track a value in a spring-like motion

  • PathAnimation - Animates an item alongside a path

  • Vector3dAnimation - Animates changes in QVector3d values

Later we will learn how to create a sequence of animations. While working on more complex animations, there is sometimes a need to change a property or to run a script during an ongoing animation. For this Qt Quick offers the action elements, which can be used everywhere where the other animation elements can be used:

  • PropertyAction - Specifies immediate property changes during animation

  • ScriptAction - Defines scripts to be run during an animation

The major animation types will be discussed in this chapter using small, focused examples.

Applying Animations

Animation can be applied in several ways:

  • Animation on property - runs automatically after the element is fully loaded

  • Behavior on property - runs automatically when the property value changes

  • Standalone Animation - runs when the animation is explicitly started using start() or running is set to true (e.g. by a property binding)

Later we will also see how animations can be used inside state transitions.

Clickable Image V2

To demonstrate the usage of animations we reuse our ClickableImage component from an earlier chapter and extended it with a text element.

  1. // ClickableImageV2.qml
  2. // Simple image which can be clicked
  3. import QtQuick
  4. Item {
  5. id: root
  6. width: container.childrenRect.width
  7. height: container.childrenRect.height
  8. property alias text: label.text
  9. property alias source: image.source
  10. signal clicked
  11. Column {
  12. id: container
  13. Image {
  14. id: image
  15. }
  16. Text {
  17. id: label
  18. width: image.width
  19. horizontalAlignment: Text.AlignHCenter
  20. wrapMode: Text.WordWrap
  21. color: "#ececec"
  22. }
  23. }
  24. MouseArea {
  25. anchors.fill: parent
  26. onClicked: root.clicked()
  27. }
  28. }

To organize the element below the image we used a Column positioner and calculated the width and height based on the column’s childrenRect property. We exposed text and image source properties, and a clicked signal. We also wanted the text to be as wide as the image, and for it to wrap. We achieve the latter by using the Text element’s wrapMode property.

Parent/child geometry dependency

Due to the inversion of the geometry-dependency (parent geometry depends on child geometry), we can’t set a width/height on the ClickableImageV2, as this will break our width/height binding.

You should prefer the child’s geometry to depend on the parent’s geometry if the item is more like a container for other items and should adapt to the parent’s geometry.

The objects ascending

Animations - 图2

The three objects are all at the same y-position (y=200). They all need to travel to y=40, each of them using a different method with different side-effects and features.

  1. ClickableImageV2 {
  2. id: greenBox
  3. x: 40; y: root.height-height
  4. source: "assets/box_green.png"
  5. text: "animation on property"
  6. NumberAnimation on y {
  7. to: 40; duration: 4000
  8. }
  9. }

First object

The first object travels using the Animation on <property> strategy. The animation starts immediately.

When an object is clicked, its y-position is reset to the start position, and this applies to all of the objects. On the first object, the reset does not have any effect as long as the animation is running.

This can be visually disturbing, as 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.

  1. ClickableImageV2 {
  2. id: blueBox
  3. x: (root.width-width)/2; y: root.height-height
  4. source: "assets/box_blue.png"
  5. text: "behavior on property"
  6. Behavior on y {
  7. NumberAnimation { duration: 4000 }
  8. }
  9. onClicked: y = 40
  10. // random y on each click
  11. // onClicked: y = 40 + Math.random() * (205-40)
  12. }

Second object

The second object travels using a Behavior on animation. This behavior tells the property it should animate each change in value. The behavior can be disabled by setting enabled: false on the Behavior element.

The object will start traveling when you click it (its y-position is then set to 40). Another click has no influence, as the position is already set.

You could try to use a random value (e.g. 40+(Math.random()\*(205-40)) for the y-position. You will see that the object will always animate to the new position and adapt its speed to match the 4 seconds to the destination defined by the duration of the animation.

  1. ClickableImageV2 {
  2. id: redBox
  3. x: root.width-width-40; y: root.height-height
  4. source: "assets/box_red.png"
  5. onClicked: anim.start()
  6. // onClicked: anim.restart()
  7. text: "standalone animation"
  8. NumberAnimation {
  9. id: anim
  10. target: redBox
  11. properties: "y"
  12. to: 40
  13. duration: 4000
  14. }
  15. }

Third object

The third object uses a standalone animation. The animation is defined as its own element and can be almost anywhere in the document.

The click will start the animation using the animation’s start() function. Each animation has start(), stop(), resume(), and restart() functions. The animation itself contains much more information than the other animation types earlier.

We need to define the target, which is the element to be animated, along with the names of the properties that we want to animate. We also need to define a to value, and, in this case, a from value, which allows a restart of the animation.

Animations - 图3

A click on the background will reset all objects to their initial position. The first object cannot be restarted except by re-starting the program which triggers the re-loading of the element.

Other ways to control Animations

Another way to start/stop an animation is to bind a property to the running property of an animation. This is especially useful when the user-input is in control of properties:

  1. NumberAnimation {
  2. ...
  3. // animation runs when mouse is pressed
  4. running: area.pressed
  5. }
  6. MouseArea {
  7. id: area
  8. }

Easing Curves

The value change of a property can be controlled by an animation. Easing attributes allow influencing the interpolation curve of a property change.

All animations we have defined by now use a linear interpolation because the initial easing type of an animation is Easing.Linear. It’s best visualized with a small plot, where the y-axis is the property to be animated and the x-axis is the time (duration). A linear interpolation would draw a straight line from the from value at the start of the animation to the to value at the end of the animation. So the easing type defines the curve of change.

Easing types should be carefully chosen to support a natural fit for a moving object. For example, when a page slides out, the page should initially slide out slowly and then gain momentum to finally slide out at high speed, similar to turning the page of a book.

Animations should not be overused.

As with other aspects of UI design, animations should be designed carefully to support the UI flow, not dominate it. The eye is very sensitive to moving objects and animations can easily distract the user.

In the next example, we will try some easing curves. Each easing curve is displayed by a clickable image and, when clicked, will set a new easing type on the square animation and then trigger a restart() to run the animation with the new curve.

Animations - 图4

The code for this example was made a little bit more complicated. We first create a grid of EasingTypes and a Box which is controlled by the easing types. An easing type just displays the curve which the box shall use for its animation. When the user clicks on an easing curve the box moves in a direction according to the easing curve. The animation itself is a standalone animation with the target set to the box and configured for x-property animation with a duration of 2 seconds.

TIP

The internals of the EasingType renders the curve in real time, and the interested reader can look it up in the EasingCurves example.

  1. // EasingCurves.qml
  2. import QtQuick
  3. import QtQuick.Layouts
  4. Rectangle {
  5. id: root
  6. width: childrenRect.width
  7. height: childrenRect.height
  8. color: '#4a4a4a'
  9. gradient: Gradient {
  10. GradientStop { position: 0.0; color: root.color }
  11. GradientStop { position: 1.0; color: Qt.lighter(root.color, 1.2) }
  12. }
  13. ColumnLayout {
  14. Grid {
  15. spacing: 8
  16. columns: 5
  17. EasingType {
  18. easingType: Easing.Linear
  19. title: 'Linear'
  20. onClicked: {
  21. animation.easing.type = easingType
  22. box.toggle = !box.toggle
  23. }
  24. }
  25. EasingType {
  26. easingType: Easing.InExpo
  27. title: "InExpo"
  28. onClicked: {
  29. animation.easing.type = easingType
  30. box.toggle = !box.toggle
  31. }
  32. }
  33. EasingType {
  34. easingType: Easing.OutExpo
  35. title: "OutExpo"
  36. onClicked: {
  37. animation.easing.type = easingType
  38. box.toggle = !box.toggle
  39. }
  40. }
  41. EasingType {
  42. easingType: Easing.InOutExpo
  43. title: "InOutExpo"
  44. onClicked: {
  45. animation.easing.type = easingType
  46. box.toggle = !box.toggle
  47. }
  48. }
  49. EasingType {
  50. easingType: Easing.InOutCubic
  51. title: "InOutCubic"
  52. onClicked: {
  53. animation.easing.type = easingType
  54. box.toggle = !box.toggle
  55. }
  56. }
  57. EasingType {
  58. easingType: Easing.SineCurve
  59. title: "SineCurve"
  60. onClicked: {
  61. animation.easing.type = easingType
  62. box.toggle = !box.toggle
  63. }
  64. }
  65. EasingType {
  66. easingType: Easing.InOutCirc
  67. title: "InOutCirc"
  68. onClicked: {
  69. animation.easing.type = easingType
  70. box.toggle = !box.toggle
  71. }
  72. }
  73. EasingType {
  74. easingType: Easing.InOutElastic
  75. title: "InOutElastic"
  76. onClicked: {
  77. animation.easing.type = easingType
  78. box.toggle = !box.toggle
  79. }
  80. }
  81. EasingType {
  82. easingType: Easing.InOutBack
  83. title: "InOutBack"
  84. onClicked: {
  85. animation.easing.type = easingType
  86. box.toggle = !box.toggle
  87. }
  88. }
  89. EasingType {
  90. easingType: Easing.InOutBounce
  91. title: "InOutBounce"
  92. onClicked: {
  93. animation.easing.type = easingType
  94. box.toggle = !box.toggle
  95. }
  96. }
  97. }
  98. Item {
  99. height: 80
  100. Layout.fillWidth: true
  101. Box {
  102. id: box
  103. property bool toggle
  104. x: toggle?20:root.width-width-20
  105. anchors.verticalCenter: parent.verticalCenter
  106. gradient: Gradient {
  107. GradientStop { position: 0.0; color: "#2ed5fa" }
  108. GradientStop { position: 1.0; color: "#2467ec" }
  109. }
  110. Behavior on x {
  111. NumberAnimation {
  112. id: animation
  113. duration: 500
  114. }
  115. }
  116. }
  117. }
  118. }
  119. }

Please play with the example and observe the change of speed during an animation. Some animations feel more natural for the object and some feel irritating.

Besides the duration and easing.type, you are able to fine-tune animations. For example, the general PropertyAnimation type (from which most animations inherit) additionally supports easing.amplitude, easing.overshoot, and easing.period properties, which allow you to fine-tune the behavior of particular easing curves.

Not all easing curves support these parameters. Please consult the easing tableAnimations - 图5 (opens new window) from the PropertyAnimation documentation to check if an easing parameter has an influence on an easing curve.

Choose the right Animation

Choosing the right animation for the element in the user interface context is crucial for the outcome. Remember the animation shall support the UI flow; not irritate the user.

Grouped Animations

Often animations will be more complex than just animating one property. You might want to run several animations at the same time or one after another or even execute a script between two animations.

For this, grouped animations can be used. As the name suggests, it’s possible to group animations. Grouping can be done in two ways: parallel or sequential. You can use the SequentialAnimation or the ParallelAnimation element, which act as animation containers for other animation elements. These grouped animations are animations themselves and can be used exactly as such.

Animations - 图6

All direct child animations of a parallel animation run in parallel when started. This allows you to animate different properties at the same time.

  1. // parallelanimation.qml
  2. import QtQuick
  3. BrightSquare {
  4. id: root
  5. width: 600
  6. height: 400
  7. property int duration: 3000
  8. property Item ufo: ufo
  9. Image {
  10. anchors.fill: parent
  11. source: "assets/ufo_background.png"
  12. }
  13. ClickableImageV3 {
  14. id: ufo
  15. x: 20; y: root.height-height
  16. text: 'ufo'
  17. source: "assets/ufo.png"
  18. onClicked: anim.restart()
  19. }
  20. ParallelAnimation {
  21. id: anim
  22. NumberAnimation {
  23. target: ufo
  24. properties: "y"
  25. to: 20
  26. duration: root.duration
  27. }
  28. NumberAnimation {
  29. target: ufo
  30. properties: "x"
  31. to: 160
  32. duration: root.duration
  33. }
  34. }
  35. }

Animations - 图7

A sequential animation runs each child animation in the order in which it is declared: top to bottom.

  1. // SequentialAnimationExample.qml
  2. import QtQuick
  3. BrightSquare {
  4. id: root
  5. width: 600
  6. height: 400
  7. property int duration: 3000
  8. property Item ufo: ufo
  9. Image {
  10. anchors.fill: parent
  11. source: "assets/ufo_background.png"
  12. }
  13. ClickableImageV3 {
  14. id: ufo
  15. x: 20; y: root.height-height
  16. text: 'rocket'
  17. source: "assets/ufo.png"
  18. onClicked: anim.restart()
  19. }
  20. SequentialAnimation {
  21. id: anim
  22. NumberAnimation {
  23. target: ufo
  24. properties: "y"
  25. to: 20
  26. // 60% of time to travel up
  27. duration: root.duration*0.6
  28. }
  29. NumberAnimation {
  30. target: ufo
  31. properties: "x"
  32. to: 400
  33. // 40% of time to travel sideways
  34. duration: root.duration*0.4
  35. }
  36. }
  37. }

Animations - 图8

Grouped animations can also be nested. For example, a sequential animation can have two parallel animations as child animations, and so on. We can visualize this with a soccer ball example. The idea is to throw a ball from left to right and animate its behavior.

Animations - 图9

To understand the animation we need to dissect it into the integral transformations of the object. We need to remember that animations animate property changes. Here are the different transformations:

  • An x-translation from left-to-right (X1)

  • A y-translation from bottom to top (Y1) followed by a translation from up to down (Y2) with some bouncing

  • A rotation of 360 degrees over the entire duration of the animation (ROT1)

The whole duration of the animation should take three seconds.

Animations - 图10

We start with an empty item as the root element of the width of 480 and height of 300.

  1. import QtQuick
  2. Item {
  3. id: root
  4. width: 480
  5. height: 300
  6. property int duration: 3000
  7. ...
  8. }

We have defined our total animation duration as a reference to better synchronize the animation parts.

The next step is to add the background, which in our case are 2 rectangles with green and blue gradients.

  1. Rectangle {
  2. id: sky
  3. width: parent.width
  4. height: 200
  5. gradient: Gradient {
  6. GradientStop { position: 0.0; color: "#0080FF" }
  7. GradientStop { position: 1.0; color: "#66CCFF" }
  8. }
  9. }
  10. Rectangle {
  11. id: ground
  12. anchors.top: sky.bottom
  13. anchors.bottom: root.bottom
  14. width: parent.width
  15. gradient: Gradient {
  16. GradientStop { position: 0.0; color: "#00FF00" }
  17. GradientStop { position: 1.0; color: "#00803F" }
  18. }
  19. }

Animations - 图11

The upper blue rectangle takes 200 pixels of the height and the lower one is anchored to the top of the sky and to the bottom of the root element.

Let’s bring the soccer ball onto the green. The ball is an image, stored under “assets/soccer_ball.png”. For the beginning, we would like to position it in the lower left corner, near the edge.

  1. Image {
  2. id: ball
  3. x: 0; y: root.height-height
  4. source: "assets/soccer_ball.png"
  5. MouseArea {
  6. anchors.fill: parent
  7. onClicked: {
  8. ball.x = 0;
  9. ball.y = root.height-ball.height;
  10. ball.rotation = 0;
  11. anim.restart()
  12. }
  13. }
  14. }

Animations - 图12

The image has a mouse area attached to it. If the ball is clicked, the position of the ball will reset and the animation is restarted.

Let’s start with a sequential animation for the two y translations first.

  1. SequentialAnimation {
  2. id: anim
  3. NumberAnimation {
  4. target: ball
  5. properties: "y"
  6. to: 20
  7. duration: root.duration * 0.4
  8. }
  9. NumberAnimation {
  10. target: ball
  11. properties: "y"
  12. to: 240
  13. duration: root.duration * 0.6
  14. }
  15. }

Animations - 图13

This specifies that 40% of the total animation duration is the up animation and 60% the down animation, with each animation running after the other in sequence. The transformations are animated on a linear path but there is no curving currently. Curves will be added later using the easing curves, at the moment we’re concentrating on getting the transformations animated.

Next, we need to add the x-translation. The x-translation shall run in parallel with the y-translation, so we need to encapsulate the sequence of y-translations into a parallel animation together with the x-translation.

  1. ParallelAnimation {
  2. id: anim
  3. SequentialAnimation {
  4. // ... our Y1, Y2 animation
  5. }
  6. NumberAnimation { // X1 animation
  7. target: ball
  8. properties: "x"
  9. to: 400
  10. duration: root.duration
  11. }
  12. }

Animations - 图14

In the end, we would like the ball to be rotating. For this, we need to add another animation to the parallel animation. We choose RotationAnimation, as it’s specialized for rotation.

  1. ParallelAnimation {
  2. id: anim
  3. SequentialAnimation {
  4. // ... our Y1, Y2 animation
  5. }
  6. NumberAnimation { // X1 animation
  7. // X1 animation
  8. }
  9. RotationAnimation {
  10. target: ball
  11. properties: "rotation"
  12. to: 720
  13. duration: root.duration
  14. }
  15. }

That’s the whole animation sequence. The one thing that’s left is to provide the correct easing curves for the movements of the ball. For the Y1 animation, we use a Easing.OutCirc curve, as this should look more like a circular movement. Y2 is enhanced using an Easing.OutBounce to give the ball its bounce, and the bouncing should happen at the end (try with Easing.InBounce and you will see that the bouncing starts right away).

The X1 and ROT1 animation are left as-is, with a linear curve.

Here is the final animation code for your reference:

  1. ParallelAnimation {
  2. id: anim
  3. SequentialAnimation {
  4. NumberAnimation {
  5. target: ball
  6. properties: "y"
  7. to: 20
  8. duration: root.duration * 0.4
  9. easing.type: Easing.OutCirc
  10. }
  11. NumberAnimation {
  12. target: ball
  13. properties: "y"
  14. to: root.height-ball.height
  15. duration: root.duration * 0.6
  16. easing.type: Easing.OutBounce
  17. }
  18. }
  19. NumberAnimation {
  20. target: ball
  21. properties: "x"
  22. to: root.width-ball.width
  23. duration: root.duration
  24. }
  25. RotationAnimation {
  26. target: ball
  27. properties: "rotation"
  28. to: 720
  29. duration: root.duration
  30. }
  31. }