Explicit animations

You can use explicit animations to determine precisely how a UI control is animated. You can specify properties of the animation, such as duration, starting and ending points, and easing curve (how the animated value changes over time).

All explicit animations inherit from the  AbstractAnimation class. This class provides general animation behaviors, such as specifying a target control for the animation, starting and stopping the animation, and specifying the number of times the animation should be repeated.

Individual animations are represented as transitions from one state to another, or from an initial set of property values to a final set of property values. You can use transition animations to fade, scale, translate, or change the opacity of a control. These transitions inherit from the AbstractTransition class.

You can also group animations together and execute them sequentially (one after another) or in parallel (at the same time). These animation groupings inherit from the  GroupAnimation class.

A hierarchy tree for the AbstractAnimation class.

Transition animations

Each implementation of the  AbstractTransition class allows you to animate different visual properties of a control:

Each transition includes from and to properties that specify the initial and final values of the animated property. You should check the API reference for the transition that you're interested in to determine the names of the from and to properties. For example, the RotateTransition animation uses fromAngleZ and toAngleZ as its from and to properties.

If you don't specify values for both the from and to properties, the missing value is derived from the current value of the property. Consider a  Label that has an initial opacity of 1.0. If you specify a from value of 0.7 for a FadeTransition animation and add this animation to the Label, the opacity starts from 0.7 and fades to 1.0 over the course of the animation.

Some transitions allow you to animate more than one property at the same time. The TranslateTransition animation lets you animate both the translationX and translationY properties in the same animation, and the ScaleTransition animation lets you animate both the scaleX and scaleY properties. In these types of animations, each property has its own version offrom and to. For example, TranslateTransition includes the fromXfromYtoX, and toY properties.

Using transition animations in QML

In QML, you add animations to a control's animations list, which represents all of the animations that apply to the control. When you want to start the animation, you simply call its play() function. Here's how to create a button that, when it's clicked, moves horizontally over the course of three seconds:

import bb.cascades 1.0
 
Page {
    content: Container {
        Button {
            text: "Click me"
             
            animations: [
                TranslateTransition {
                    id: translateAnimation
                    toX: 400
                    duration: 3000
                }
            ]
             
            onClicked: {
                translateAnimation.play();
            }
        }
    }
}

You can specify multiple transitions in the animation list and play them at the same time or at different times in your app. Here's how to play a button's TranslateTransition and RotateTransition animations at the same time:

import bb.cascades 1.0
 
Page {
    content: Container {
        Button {
            text: "Click me"
             
            animations: [
                TranslateTransition {
                    id: translateAnimation
                    toX: 400
                    duration: 3000
                },
                RotateTransition {
                    id: rotateAnimation
                    toAngleZ: 180
                    duration: 2000
                }
            ]
             
            onClicked: {
                translateAnimation.play();
                rotateAnimation.play();
            }
        }
    }
}

You can also use group animations to achieve the same result, as you'll see later.

Unlike in implicit animations, the value of a property that you animate doesn't change immediately when the animation begins. Instead, the value of the property is updated when the animation ends. However, like in implicit animations, the property doesn't change for each frame of the animation. For example, if you have a control that uses a RotateTransition from 0 to 45 with a duration of 2000, the rotationZ property isn't updated with all intermediate values between 0 and 45; it is updated only with the value of 45, and only when the animation ends.

If you want to obtain all of the intermediate values of a property during an animation, you can access the value of the property as it's changing by using a signal. These types of signals typically have the suffix Changing. Here's how to monitor the intermediate values of a button's translationX property while it's being animated and display the values in a text area:

import bb.cascades 1.0
 
Page {
    content: Container {
        Button {
            text: "Click me"
             
            animations: [
                TranslateTransition {
                    id: translateAnimation
                    toX: 400
                    duration: 3000
                }
            ]
             
            onTranslationXChanging: {
                displayArea.text = "" + translationX;
            }
             
            onClicked: {
                translateAnimation.play();
            }
        }
         
        TextArea {
            id: displayArea
            text: " "
        }
    }
}

Using transition animations in C++

In C++, you create objects that represent the animations for your UI controls, and then add these animations to the controls. You can add animations to controls by calling the  addAnimation() function, or you can specify the control that the animation applies to when you create the animation. To start the animation, you call  play() as you would in QML. The various animation classes ( TranslateTransition FadeTransition, and so on) use the builder pattern, making it easy for you to create animations with different properties.

Here's how to create two buttons, one of which has animations associated with it. When the first button is clicked, the second button's animations are played. The second button translates and rotates simultaneously. The button's rotation animation is then repeated twice after the translation ends.

// Create the root page and top-level container
Page* root = new Page;
Container* myContainer = new Container;
 
// Create the button that starts the animations. This button's clicked()
// signal is connected to a slot function
Button* startButton = Button::create("Start");

// If any Q_ASSERT statement(s) indicate that the slot failed to connect to 
// the signal, make sure you know exactly why this has happened. This is not
// normal, and will cause your app to stop working
bool res = QObject::connect(startButton, SIGNAL(clicked()), this,
                            SLOT(onButtonClicked()));

// This is only available in Debug builds
Q_ASSERT(res);
 
// Since the variable is not used in the app, this is added to avoid a 
// compiler warning
Q_UNUSED(res);
 
// Add the start button to the top-level container
myContainer->add(startButton);
 
// Create the button that animates, and add the animations to it
Button* animatedButton = Button::create("Animated button");
mTranslateAnimation = TranslateTransition::create()
                       .toX(400)
                       .duration(3000);
animatedButton->addAnimation(mTranslateAnimation);
mRotateAnimation = RotateTransition::create(animatedButton)
                    .toAngleZ(360)
                    .duration(3000)
                    .repeatCount(3);
myContainer->add(animatedButton);
 
// Set the content of the page and display it
root->setContent(myContainer);
app->setScene(root);
// A slot that handles the button click and plays the animations
 
void App::onButtonClicked()
{
    mTranslateAnimation->play();
    mRotateAnimation->play();
}

In your header file, you declare the variables that represent the animations, as well as the onButtonClicked() slot:

// Header file
 
//...
TranslateTransition* mTranslateAnimation;
RotateTransition* mRotateAnimation;
 
public slots:
    void onButtonClicked();
 
//...

Easing curves

Easing curves specify how an animated value changes over the duration of an animation. The value might change quickly at first, and then change less quickly as the animation proceeds. Or, the value might change slowly at the beginning and the end of the animation, and change quickly in the middle. Easing curves provide you with a way to fine-tune your animations so that they model the visual behavior that you want in your apps.

You can use the easing curves that are defined in the  StockCurve class to customize your animations. Each easing curve in this class is defined by two attributes:

  • Interpolation function: This attribute determines the overall shape of the animation and is often based on an equation that describes the animation. For example, the  QuadraticIn easing curve is based on a quadratic function (t^2), where the rate of animation increases as the animation proceeds. The  ElasticIn easing curve is based on an exponentially decaying sine function that simulates a gentle elastic motion.
  • Velocity: This attribute determines the speed of the animation at the beginning and the end of the animation's duration. An ease-in velocity (for example, the  BounceIn or  CubicIn easing curves) indicates that the animation occurs slowly at the beginning and more quickly at the end of the animation. An ease-out velocity (for example, the  BounceOut or  CubicOut easing curves) indicates that the animation occurs quickly at the beginning and more slowly at the end of the animation. An ease-in, ease-out velocity (for example, the  BounceInOut or CubicInOut easing curves) indicates that the animation occurs slowly both at the beginning and end of the animation.

In QML, you can set an easing curve for an animation by using the easingCurve property. In C++, you can call  setEasingCurve() or use easingCurve() in the builder pattern when you create an animation. Here's how to create a button, in both QML and C++, that includes a translation animation and a rotation animation. Each animation uses a different easing curve.

Code sample: Using easing curves in QML

import bb.cascades 1.0
 
Page {
    content: Container {
        Button {
            text: "Click me"
             
            animations: [
                TranslateTransition {
                    id: translateAnimation
                    toX: 400
                    duration: 2000
                    easingCurve: StockCurve.QuadraticInOut
                },
                RotateTransition {
                    id: rotateAnimation
                    toAngleZ: 150
                    duration: 2000
                    easingCurve: StockCurve.ElasticOut
                }
            ]
             
            onClicked: {
                translateAnimation.play();
                rotateAnimation.play();
            }
        }
    }
}

Code sample: Using easing curves in C++

// The variables mTranslateAnimation and mRotateAnimation are 
// declared in a header file. 
 
// Create the root page and top-level container
Page* root = new Page;
Container* myContainer = new Container;
 
// Create the button and connect its clicked() signal to a slot function. Make
// sure to test the return value to detect any errors.
Button* myButton = Button::create("Click me");

// If any Q_ASSERT statement(s) indicate that the slot failed to connect to 
// the signal, make sure you know exactly why this has happened. This is not
// normal, and will cause your app to stop working!!
bool res = QObject::connect(myButton, SIGNAL(clicked()), this,
                            SLOT(onButtonClicked()));
Q_ASSERT(res);
 
// Since the variable is not used in the app, this is added to avoid a 
// compiler warning.
Q_UNUSED(res);
                  
// Create the animations and add them to the button
mTranslateAnimation = TranslateTransition::create()
                      .toX(400)
                      .duration(2000)
                      .easingCurve(StockCurve::QuadraticInOut);
mRotateAnimation = RotateTransition::create()
                   .toAngleZ(150)
                   .duration(2000)
                   .easingCurve(StockCurve::ElasticOut);
myButton->addAnimation(mTranslateAnimation);
myButton->addAnimation(mRotateAnimation);
 
// Add the button to the top-level container
myContainer->add(myButton);
 
// Set the content of the page and display it
root->setContent(myContainer);
app->setScene(root);
// A slot that handles the button click and plays the animations, and
// is also declared in a header file.
 
void App::onButtonClicked()
{
    mTranslateAnimation->play();
    mRotateAnimation->play();
}

Group animations

In Cascades, there are several ways to play animations at the same time or one after another. One method, described above, is to simply call each animation's  play() function whenever you want the animations to play. Because play() doesn't block (it returns immediately), you can call play() for several different animations in one place in your app, and all of the animations will start at the same time.

If you have several animations that you always want to play concurrently or sequentially, you can create a group animation to contain them. Group animations allow you to control a set of animations as a unit. You can start or stop the entire set of animations using a single call to play() or  stop(), instead of calling play() or stop() for each individual animation in the group.

The  GroupAnimation class is the base class for a group of animations, and has the following subclasses:

Here's how to create two buttons, in both QML and C++, and each of them has a group of animations associated with it. One button has animations that play concurrently, and the other button has animations that play sequentially.

Code sample: Grouping animations in QML

import bb.cascades 1.0
 
Page {
    content: Container {
        Button {
            text: "Button one"
             
            animations: [
                ParallelAnimation {
                    id: buttonOneAnimation
                     
                    animations: [
                        TranslateTransition {
                            toX: 400
                            duration: 2000
                        },
                        FadeTransition {
                            toOpacity: 0
                            duration: 2000
                        }
                    ]
                }
            ]
             
            onClicked: {
                buttonOneAnimation.play();
            }
        }
         
        Button {
            text: "Button two"
             
            animations: [
                SequentialAnimation {
                    id: buttonTwoAnimation
                     
                    animations: [
                        TranslateTransition {
                            toX: 400
                            duration: 2000
                        },
                        TranslateTransition {
                            toX: 0
                            duration: 2000
                        },
                        RotateTransition {
                            toAngleZ: 180
                            duration: 1500
                        }
                    ]
                }
            ]
             
            onClicked: {
                buttonTwoAnimation.play();
            }
        }
    }
}

Code sample: Grouping animations in C++

// To run this sample, you need to add the signals and slots,
// as well as functions that start the animations.
 
Button* buttonOne = Button::create("Button one");
ParallelAnimation* buttonOneAnimation =
        ParallelAnimation::create(buttonOne)
        .add(TranslateTransition::create().toX(400).duration(2000))
        .add(FadeTransition::create().to(0).duration(2000));
 
Button* buttonTwo = Button::create("Button two");
SequentialAnimation* buttonTwoAnimation =
        SequentialAnimation::create(buttonTwo)
        .add(TranslateTransition::create().toX(400).duration(2000))
        .add(TranslateTransition::create().toX(0).duration(2000))
        .add(RotateTransition::create().toAngleZ(180).duration(1500));

In QML, when you use either SequentialAnimation or ParallelAnimation to group animations, the animations list is a default property and doesn't need to be specified explicitly. You can simply list the individual animations that belong to the group animation directly:

SequentialAnimation {
    TranslateTransition {
        toX: 300
        duration: 500
    }
    RotateTransition {
        toAngleZ: 60
        duration: 700
    }
}

It's important to note that when you group animations, you can start and stop the parent ParallelAnimation or SequentialAnimation, but you can't start and stop the individual animations within the group animation. Similarly, you can query the status of a parent ParallelAnimation or SequentialAnimation only (for example, by calling functions such as isPlaying() and isStopped()), and animation signals such as  started() and stopped() are emitted for the parent group animations only.

Reusable animations

In QML, you can use the animations list to create specific animations for each control in your app. However, lists of animations that are declared in QML can be very long, especially if you want to use the same animations in multiple places.

You can declare animations in separate QML files and reuse them in your app, similar to how you can define custom controls in separate QML files. You can also define a generic animation in its own QML file, and then customize the animation by using input parameters.

Here's how to create three controls that use the same animation, which is defined in a file called CustomAnimation.qml. The first two controls use a custom input parameter called rotation to specify the amount of rotation for the two controls.

// main.qml
import bb.cascades 1.0
 
Page {
    content: Container {
        Container {
            preferredWidth: 100
            preferredHeight: 100
            background: Color.Red
             
            animations: CustomAnimation {
                id: firstAnimation
                rotation: 90
            }
        }
         
        Label {
            text: "Some text"
             
            animations: CustomAnimation {
                id: secondAnimation
                rotation: 90
            }
        }
         
        Button {
            text: "A button"
             
            animations: CustomAnimation {
                id: thirdAnimation
            }
        }
         
        onCreationCompleted: {
            firstAnimation.play();
            secondAnimation.play();
            thirdAnimation.play();
        }
    }
}

The corresponding QML file, CustomAnimation.qml, defines the custom animation. It includes a property called rotation that determines how much the animation should rotate. This property has a default value that's used if no input parameter is specified in CustomAnimation.

// CustomAnimation.qml
import bb.cascades 1.0
 
ParallelAnimation {
    property int rotation: 360
    property int length: rotation * 2
    SequentialAnimation {
        RotateTransition {
            toAngleZ: rotation
            duration: 1000
        }
        RotateTransition {
            toAngleZ: 47
            duration: 1000
        }
    }
    SequentialAnimation {
        TranslateTransition {
            toX: length
            duration: 1000
        }
        TranslateTransition {
            toX: 0
            duration: 1000
        }
    }
}

Last modified: 2013-12-21



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus