Animations Walt Disney Animation can explain whatever the mind of man can conceive.
Introduction Clutter actors have a variety of properties (position, size, rotation in 3D space, scale, opacity) which govern their visual appearance in the UI. They may also have constraints on how they are aligned and/or positioned relative to each other. The Clutter animation API provides a means of changing properties and constraints as a function of time: moving, scaling, rotating, changing opacity and colour, modifying postional constraints, etc. Clutter also makes it possible to animate non-visual properties if desired.
High level overview Here are the main concepts behind animation in Clutter: An animation changes one or more properties of one or more actors over time: their rotation in a particular dimension (x, y, z), scale, size, opacity etc. An animation has an associated timeline. Think of this as analogous to the "thing" you're controlling when you watch a video on the internet: it's what you control with the play/pause button and what is measured by the bar showing how far through the video you are. As with the controls on a video player, you can play/pause/skip a Clutter timeline; you can also rewind it, loop it, and play it backwards. If a timeline is reversed, the progress along the timeline is still measured the same way as it is in the forward direction: so if you start from the end of the timeline and run it backwards for 75% of its length, the progress is reported as 0.25 (i.e. 25% of the way from the start of the timeline). The duration of a timeline (e.g. 500 milliseconds, 1 second, 10 seconds) specifies how long its animation will last. The timeline can be inspected to find out how much of it has elapsed, either as a value in milliseconds or as a fraction (between 0 and 1) of the total length of the timeline. An animation is divided into frames. The number of frames which make up the animation isn't constant: it depends on various factors, like how powerful your machine is, the state of the drivers for your hardware, and the load on he system. So you won't always get the same number of frames in an animation of a particular duration. The change to a property in an animation occurs over the course of the timeline: the start value of the property heads toward some target value. When it reaches the end of the timeline, the property should have reached the target value. Exactly how the property changes over the course of the timeline is governed by an alpha. This is the trickiest idea to explain, so it has its own section below.
Alphas An alpha is generated for each frame of the animation. The alpha varies between -1.0 and 2.0, and changes during the course of the animation's timeline; ideally, the value should start at 0.0 and reach 1.0 by the end of the timeline. The alpha for any given frame of the animation is determined by an alpha function. Usually, the alpha function will return a value based on progress along the timeline. However, the alpha function doesn't have to respect or pay attention to the timeline: it can be entirely random if desired. To work out the value of a property at a given frame somewhere along the timeline for a given alpha: Determine the difference between the start value and the target end value for the property. Multiply the difference by the alpha for the current frame. Add the result to the start value. The shape of the plot of the alpha function over time is called its easing mode. Clutter provides various modes ranging from CLUTTER_LINEAR (the alpha value is equal to progress along the timeline), to modes based on various polynomial and exponential functions, to modes providing elastic and bounce shapes. See the ClutterAlpha documentation for examples of the shapes produced by these functions. There is also a good interactive demo of the modes on Robert Penner's site. Most of the time, you can use the built-in Clutter easing modes to get the kind of animation effect you want. However, in some cases you may want to provide your own alpha function. Here's an example (based on the quintic ease in mode from clutter-alpha.c): An alpha function just has to have a specified method signature and return a gdouble value when called. As stated above, you'd typically base the return value on the timeline progress; the function above shows how you get the timeline associated with the alpha, so you can apply the alpha function to it.
Clutter's animation API All of the animation approaches in Clutter use the same basic underpinnings (as explained above), but the API provides varying levels of abstraction and/or ease of use on top of those underpinnings. Implicit animations (created using clutter_actor_animate() and related functions) are useful where you want to apply a simple or one-off animation to an actor. They enable you to animate one or more properties using a single easing mode; however, you only specify the target values for the properties you're animating, not the start values. ClutterAnimator provides support for declarative animations (defined using ClutterScript). You can animate multiple actors with this approach, and have more control over the easing modes used during an animation: while implicit animations only allow a single easing mode for all properties, ClutterAnimator supports multiple easing modes for each property; key frames are used to indicate where in the animation each easing mode should be applied. ClutterState enables you to describe states: property values across one or more actors, plus the easing modes used to transition to those values. It can also be combined with ClutterAnimator for finer grained definition of transitions if desired. States are particularly useful if you need actors to animate between a known set of positions/sizes/opacities etc. during their lifecycles (e.g. animating a list of items in a menu, or for animations in a picture viewer where you click on thumbnails to display a full view of a photograph). The recipes in this section show when and where it is appropriate to use each of these approaches.
Inverting Animations
Problem You want to have an animation exactly mirroring another one that you just played.
Solution Reverse the direction of the ClutterTimeline associated with the animation. For example, here's how to invert an implicit animation which moves an actor along the x axis. The direction of the animation is inverted when the movement along the x axis is completed; it is also inverted if the mouse button is pressed on the actor. First, set up the animation: Next, add a function for inverting the timeline: Then add a function which calls _invert_timeline when the animation completes. More importantly, the callback should stop emission of the "completed" signal by the animation. This prevents the ClutterAnimation underlying the implicit animation from being unreferenced; which in turn allows it to be inverted: Finally, the click callback function uses the same _invert_timeline function if the animation is playing; but if the animation is stopped, it will start it instead:
Discussion If you are using ClutterAnimator rather than implicit animations, clutter_animator_get_timeline() enables you to get the underlying timeline; you could then use the techniques shown above to invert it. ClutterState enables a different approach to "inverting" an animation: rather than having a single animation which you invert, you would define two or more keys for an actor (or set of actors) and transition between them. For the example above, you would define two keys: one for the actor's initial position; and a second for the actor at x = 300.0. You would also define the transition between them: 2000 milliseconds with a CLUTTER_EASE_IN_OUT_CUBIC easing mode. With the states defined, you would then use clutter_state_set_state() inside callbacks to animate the actor between the two x positions. Behind the scenes, ClutterState would handle the animations and timelines for you.
Fading an actor out of or into view
Problem You want to animate an actor so that it fades out of or into view.
Solution Animate the actor's opacity property. You can do this using any of the approaches provided by the animation API. Here's how to fade out an actor (until it's completely transparent) using implicit animations: Here's an example of a rectangle fading out using this animation: Video showing an actor fading out using implicit animations CLUTTER_EASE_OUT_CUBIC is one of the Clutter easing modes; see the introduction for more details about what these are and how to choose one. Here's an example of the transitions you could use to fade an actor in and out using ClutterState: You would then trigger an animated state change as events occur in the application (e.g. mouse button clicks): Here's an example of this animation fading in then out again: Video showing an actor fading in then out using ClutterState ClutterState is most useful where you need to animate an actor backwards and forwards between multiple states (e.g. fade an actor in and out of view). Where you just want to fade an actor in or out once, clutter_actor_animate() is adequate.
Discussion Reducing an actor's transparency to zero does not make it inactive: the actor will still be reactive even if it's not visible (responding to key events, mouse clicks etc.). To make it really "disappear", you could use clutter_actor_hide() once you'd made the actor fully transparent.
Rotating an actor
Problem You want to animate rotation of an actor. Some example cases where you might want to do this: To rotate an image so it's the right way up for viewing. To make actors more or less prominent, rotating them towards or away from the view point. To turn an actor "around" and display different UI elements "behind" it.
Solution Animate one of the rotation-angle-(x|y|z) properties of the actor. The most "obvious" (and probably most commonly used) rotation is in the z axis (parallel to the 2D surface of the UI). The other rotation axes (x and y) are less obvious, as they rotate the actor in the depth dimension, "away from" or "towards" the view point. Examples of each type of rotation are given below. While the examples use implicit animations, it is also possible to use ClutterAnimator and ClutterState to animate rotations: see the full example at the end of this recipe for some ClutterState code. I've added an inaccurate (but hopefully useful) metaphor to each rotation axis ("wheel", "letter box", "door"), to make it easier to remember the effect you get from animating in that axis (and when the rotation center is inside the actor). Rotating on the z axis ("wheel") The above code animating a texture: Video showing an actor rotating to 90 degrees on the z axis By default, the center of the rotation is derived from the anchor point of the actor; unless you've changed the anchor point, the default is the top-left corner of the actor. See the Discussion section below for more about setting the rotation center. An animated rotation moves an actor to the specified rotation angle; it does not increment or decrement the actor's current rotation angle by the amount specified. Rotating on the x axis ("letter box") The above code animating a texture: Video showing an actor rotating to -45 degrees on the x axis Notice how the texture rotates away from the view point, and also how perspective effects are applied (as the actor is rotating "into" the depth dimension). Rotating on the y axis ("door") The above code animating a texture: Video showing an actor rotating to 45 degrees on the y axis Again, the rotation is into the depth dimension, so you get perspective effects.
Discussion It can sometimes be difficult to predict exactly how a particular rotation animation will appear when applied. Often the only way to find out is to experiment. However, the sections below outline some of the most common factors which affect animated rotations, with the aim of minimising the experimentation you need to do.
Setting the rotation center for an animation The examples in the previous section used the default center of rotation for each axis. However, it is possible to change the rotation center for an axis, in turn changing the appearance of the animation. Rotation center coordinates are relative to the actor's coordinates, not to the coordinates of the actor's container or the stage.
Setting a rotation center inside an actor You can set the center for rotation on the x or y axes like this: Because z axis rotations are more common, Clutter provides some convenience functions to set the rotation center for this axis: CLUTTER_GRAVITY_CENTER makes the center of the actor the rotation center for the z axis. See the ClutterGravity enumeration for acceptable values for this parameter. Setting the rotation center for the z axis using gravity is recommended, as Clutter will automatically recompute the rotation center if the actor's size changes. For the x and y axes, you have to do this computation yourself if you want an actor's center of rotation to stay in the same place if it is resized. Rotation on the x axis around an actor's center: Video showing an actor rotating around its center on the x axis Rotation on the y axis around an actor's center: Video showing an actor rotating around its center on the y axis Rotation on the z axis around an actor's center: Video showing an actor rotating around its center on the z axis
Setting the rotation center outside an actor Rather than rotating the actor around a point inside itself, the rotation center can be moved to a position outside the actor. (In the case of the z axis, any rotation center setting is outside the actor as its depth is 0.) When animated, the actor will describe an arc around the rotation center, as if it's swinging from an invisible thread. The same code as shown above can be used to set the rotation center: just set the rotation center coordinates to negative numbers (outside the actor). However, you can't use the gravity functions if the rotation center falls outside an actor. For example, here's a rotation to -180 degrees in the x axis, with the y rotation center set to -96 (the same as the height of the actor): Video showing an actor rotating to -180 degrees on the x axis with y rotation center set to -96 Similarly, moving the z rotation center (for a rotation in the x or y axis) will cause the actor to swing "into" or "out of" the UI. Its final apparent size may be different, as it could reach a different depth in the UI by the end of the animation. For example, here's a rotation to -180 in the x axis, with the z rotation center set to -96 (the same as the height of the actor): Video showing an actor rotating to -180 degrees on the x axis with z rotation center set to -96 The apparent final size of the actor is reduced, as it has rotated away from the view point.
Direction of rotation The apparent direction of an animated rotation depends on two things: Whether the angle of rotation is positive or negative. The rotation of the container(s) the actor is inside. In the case of the sign of the rotation, here's what happens for each axis and rotation angle sign (positive or negative). Axis Sign of rotation angle Effect on actor z + Clockwise spin about the x,y center of rotation. z - Anti-clockwise spin about the x,y center of rotation. x + The top swings away from the view point and the bottom swings towards it. If y rotation center == 0, the top is fixed; if y rotation center == the actor's height, the bottom is fixed. x - The bottom swings away from the view point and the top swings towards it. If y rotation center == 0, the top is fixed; if y rotation center == the actor's height, the bottom is fixed. y + The right-hand side swings away from the view point and the left-hand side swings towards it. When x rotation center == 0, the left-hand side if fixed; when x rotation center == the actor's width, the right-hand side is fixed. y - The right-hand side swings towards the view point and the left-hand side swings away from it. When x rotation center == 0, the left-hand side if fixed; when x rotation center == the actor's width, the right-hand side is fixed. If an actor's container is rotated, this may affect the appearance of rotation animations applied to the actor. In particular, if an actor's container has been rotated by 180 degrees in one axis, the direction of that actor's rotation may appear reversed. For example, the video below shows an actor being animated to 90 degrees on the z axis, then back to 0 degrees; the actor's container is then rotated by 180 degrees in the y axis; then the same rotation 90 degree rotation is applied to the actor again. Note that the first time the animation is applied, the rotation is clockwise; but the second time (as the actor is effectively "reversed"), it is anti-clockwise. Video showing how an actor's apparent rotation is affected by the rotation of its parent
Apparent vs. actual rotation There is a difference between an actor's apparent rotation (how much an actor appears to be rotating, from the perspective of someone looking at the UI) and its actual rotation (how much that actor is really rotating). For example, if you rotate an actor and its container simultaneously, each by 90 degrees in the same direction, the actor will appear to have rotated by 180 degrees by the end of the animation. However, calling the clutter_actor_get_rotation() function for that axis on the actor still returns a rotation of 90 degrees.
Orientation of rotation axes The rotation axes remain fixed in the same place on the actor regardless of its rotation, even though from the viewer's perspective they may appear to move. For example, when rotation in the z axis is 0 degrees, the actor's x axis is horizontal (across the UI) from both the actor's and the viewer's perspective. However, if you rotate the actor by 90 degrees in the z axis, the x axis is now vertical from the viewer's perspective, but still horizontal across the actor from the actor's perspective.
Full example Rotating an actor around x, y, and z axes using <type>ClutterState</type> a code sample should be here... but isn't
Creating complex animations with <type>ClutterAnimator</type>
Problem You want to create a complex animation involving one or more actors. The animation will consist of a sequence of transitions over multiple properties on each actor. An example might be moving several actors between points, with different types of movement for each part of the path, while transforming each actor (e.g. scaling or rotating it).
Solution Use a ClutterAnimator to define the animation. Because there are many complex animations you could implement, the example below does this: Video showing a complex animation of an actor using ClutterAnimator Although this uses a single actor, the animation is complex enough to make it difficult to implement with implicit animations or ClutterState (see the Discussion section for reasons why). Here is a JSON definition of the stage, actors, and the ClutterAnimator for this animation: JSON definition of a complex animation using <type>ClutterAnimator</type> a code sample should be here... but isn't The core to understanding this example is understanding how to define keys for a ClutterAnimator. As this is an involved topic, further explanation is given in the Discussion section. The program for loading this JSON definition from a file is as follows: Simple program for loading a JSON script; any key press starts the animation a code sample should be here... but isn't It is also possible to use the ClutterAnimator C API to define keys for an animation, but this will typically be much more verbose than the JSON equivalent. One other advantage of JSON is that it is much simpler to tweak and test an animation, as you don't have to recompile the application each time you edit it (you just load the new JSON file).
Discussion You can think of ClutterAnimator as a way to give directions to actors. For example, you could give a real (human) actor a direction like "move downstage; when you get there, stop and rotate 90 degrees to your right". In code, this might equate to a transition in the x and y properties of the actor, followed by a rotation in one axis. ClutterAnimator can give "directions" to any type of GObject, but we concentrate on animating ClutterActors in this section. Each direction like this has an implicit timeline, spanning the length of time the direction should take to fulfil (you set the length of the timeline through the duration property of the ClutterAnimator). But within that timeline, you may change the proportion of time spent on each action: "move downstage quickly, then slowly rotate 90 degrees to your right". The direction is the same, but we've specified how much of the timeline should be devoted to each action. In ClutterAnimator, this concept is captured by key frames. A key frame represents a point somewhere along the timeline, with one or more target property values for one or more actors. A ClutterAnimator manages the transitions between property values for each object, ensuring that the target values are reached when the associated key frame is reached. To change the amount of time a transition should take, you change the percentage of the timeline between key frames. Using our real stage directions as an example, you might define the key frames like this: 0.2 (after 20% of the timeline): arrive downstage 1.0 (by the end of the timeline): achieve a 90 degree rotation to the right See this section for more details about keys and key frames. Finally, a direction might be further refined with a description of the kind of movement to use: rather than saying "move downstage quickly, then slowly rotate 90 degrees to your right" a director could say: "start off slowly, but build up to a run; run downstage quickly; then stop and start rotating slowly to your right, gradually speeding up, turn a little more, then slow down gradually; you should end up rotated 90 degrees to your right" (this granularity of description is closer to what you might see in dance notation like Laban; though of course you can't animate human opacity, scale, dimensions etc...). ClutterAnimator gives you this level of granularity. Each transition to a property value between key frames can have a separate easing mode: for example, starting off slowly and building to a constant speed equates to an "ease in" mode; starting slowly, speeding up, maintaining a constant speed, then gradually slowing down equates to "ease in and ease out". To summarise: creating a complex animation means deciding: Which properties need to change on which actors? What target value should each property transition to? How quickly (by which key frame) should the property reach the target value? What "shape" (easing mode) should the change to the target value follow?
Understanding keys and key frames A ClutterAnimator maintains a list of properties objects, each being a unique pair of object (an object to be animated) + name (name of the property to be animated on that object). Each properties object in turn has a list of keys, with each key having three elements: The key frame, expressed as a fraction (between 0.0 and 1.0) of the duration of the animation. At this point, the named property should reach a target value. The easing mode to use to transition the property to that value. The target value the property should transition to. For example: { "object" : "rectangle", "name" : "x", "ease-in" : true, "keys" : [ [ 0.0, "linear", 0.0 ], [ 0.1, "easeInCubic", 150.0 ], [ 0.8, "linear", 150.0 ], [ 1.0, "easeInCubic", 0.0 ] ] } defines a sequence of transitions for the x property (position on the x axis) of the rectangle object, as follows: [ 0.0, "linear", 0.0 ]: At the start of the animation, x should be 0.0; linear is used as the easing mode, as there is no transition here. [ 0.1, "easeInCubic", 150.0 ]: By 10% of the way through the animation, x should reach a value of 150.0. This moves the rectangle horizontally across the stage. The easeInCubic easing mode means that the transition to the new value starts slow and speeds up. This makes the movement look more "natural". [ 0.8, "linear", 150.0 ]: From 10% of the way through the animation to 80% of the way through, the x value remains at 150.0. This makes the rectangle stay still on the x axis throughout this period. It's important to specify interim key frames if in a later key frame you intend to change the value again (as is done for the x value here). Otherwise you can get premature transitions to a value over longer periods than you intended. By specifying the interim key frames where the value remains constant, you ensure that it doesn't change before you want it to. [ 1.0, "easeInCubic", 0.0 ]: From 80% of the way through the animation to the end, the x value should transition back to 0.0. This moves the actor back to its starting position on the x axis. Again, an easeInCubic easing mode is used to make the transition appear more natural. There are two more properties you can set for each object/property pair: Set ease-in to true to animate to the target value at the first key frame. If ease-in is false, the animation will "jump" to the target value instead (if the target value is different from the current value). Set interpolation to either "linear" (the default) or "cubic". This sets how ClutterAnimator transitions between key frames; in effect, it further modulates any easing modes set on individual keys: if set to "cubic", you get a slightly more natural and gentle transition between key frames than you do if set to "linear".
Why <type>ClutterAnimator</type>? Why use ClutterAnimator and not the other Clutter animation approaches for complex animations? Implicit animations can animate properties on a single actor; however, you can only specify a single transition for each property. Also, it's not possible to describe complex movement along a path in a single implicit animation: you would have to chain several animations together to do that. To animate multiple actors, you'd also need multiple implicit animations, one for each actor. These animations would also need to be synchronized (for example, by sharing a single timeline). So it would be possible, but more difficult than an implementation using ClutterAnimator. ClutterState can be used for complex animations: each state can describe transitions for multiple actors and multiple properties. However, to make continuous movement (as in the example), you would need to write a state for each movement between a pair of points; then add a callback so that when each state is reached, the animation moves onto the next state. This adds some code (a handler for the completed signal emitted by the ClutterState to set the next state). This could work OK for a few states, but doesn't scale as well as ClutterAnimator if you have many transitions. ClutterState and ClutterAnimator are not mutually exclusive. If you generally need to transition between several known states (e.g. hiding/revealing menus which stay in the same place, moving between two UI layouts), but want to create a complex animation between states, you can use ClutterAnimators to define the transitions: see the documentation for clutter_state_set_animator() for details. ClutterAnimator is a good fit for complex animations, and probably the best fit for the most complex: it is the simplest way to encode a sequence of transitions for a list of object/property pairs which can be treated as a single animation. This is largely because ClutterAnimator is effectively managing the chaining together of the individual transitions into a whole. One other feature of ClutterAnimator which isn't demonstrated here is how it enables transitions to overlap. For example, let's say you wanted an actor to move along a complex path (e.g. described by five pairs of x,y coordinates); but during that movement, you wanted the actor to continuously transition to a scale of 4.0 on both the x and y axes. To achieve this with ClutterState, you would need to set up five transitions (one to move to each pair of x,y coordinates); plus a callback to chain the state transitions together; and within each transition, you'd have to figure out a percentage of the scaling to apply, so that the actor was at a scale of 4.0 on reaching the final state. With ClutterAnimator, you can treat the movement between the coordinates and the scaling separately within the same animation, but overlap their key frames. This makes coding overlapping animations of different properties much more straightforward. See this JSON definition for an example of how to do this.
Full example Running multiple transition sequences with different key frames in parallel using <type>ClutterAnimator</type> This JSON file can be loaded with the same code as used for this example, by passing the JSON file name on the command line: $ ./animations-complex animations-complex-overlapping.json a code sample should be here... but isn't
Reusing a complex animation on different actors
Problem You want to apply the same complex animation to several different actors.
Solution Instead of animating each actor separately, create a rig: an empty container with an associated animation, which will be animated in lieu of animating the actor directly. Do this as follows: Initialise the stage and actors, including those to be animated. Define a ClutterContainer and a ClutterAnimator animation to animate it. When you need to animate an actor: Create an instance of the rig and its animator. Reparent the actor to the rig. Run the rig's animation. For this solution, we're using JSON to define the animation and the user interface elements. For more details about this approach, see the chapter on ClutterScript. Here's an extract of the JSON definition for the stage and one of five rectangles placed at its left edge (the full definition is in the appendix): The key point to note is how a signal handler is defined for the button-press-event, so that the foo_button_pressed_cb() function will trigger the animation when a (mouse) button is pressed on each rectangle. The second JSON definition includes the rig (an empty ClutterGroup) and a ClutterAnimator to animate it. The animation moves the container across the stage and scales it to twice its original size. (This is the same code as in the appendix): a code sample should be here... but isn't The remaining parts of the application code load the user interface definition, setting up the stage and rectangles; and define the callback. The full code is in the appendix, but below is the most important part, the callback function: The code creates a new rig and associated animation at the point when the rectangle is clicked. It then positions the rig at the same coordinates as the rectangle, reparents the rectangle to the rig, and starts the rig's animation. The signal handler has to be declared non-static and you must use -export-dynamic as an option to the compiler, otherwise the function isn't visible to ClutterScript (as outlined in this recipe). This is what the animation looks like: Video of a simple reusable animation
Discussion The above solution reparents an actor to be animated into a rig (an empty placeholder). The rig is a container which acts as a temporary parent for the actor we really want to animate. By animating the rig, it appears as though the actor inside it is being animated (but see these caveats). This means the same animation can be easily applied to different actors: create an instance of the rig, reparent an actor to it, then run the rig's animation. This is simpler than creating a separate animation for each actor individually, or reusing a single ClutterAnimator on different actors (see this section). Using JSON enhances the animation's reusability (it's even potentially reusable in another application), makes the code simpler (an animation can be loaded directly from the script), and makes refactoring easier (the animation can be modified without recompiling the application code). However, it also puts some minor limitations on the animation's reusability; namely, you can only set absolute property values in a JSON animation definition. This makes JSON less useful in cases where you need to animate properties relative to their starting values: for example, "move 50 pixels along the x axis" or "rotate by 10 degrees more on the z axis". (This type of animation is probably less portable anyway.) In such cases, the programmable API may be a better option: see the ClutterAnimator documentation for examples.
One animation vs. many In the sample code, a new instance of the rig and its animation are created for each actor. One side effect of this is that all of the actors can animate simultaneously with the "same" animation. If you don't want this behaviour, but still want to use a rig approach, you could create a single instance of the rig and its animation. Then, you could reparent each actor to it in turn. To ensure that the rig only animates one actor (or group of actors) at a time, you could track whether the rig is currently animating (e.g. by examining the animation's timeline with clutter_animator_get_timeline()). Then, if the animation is running, prevent any other actor from being reparented to the rig. Note that you would also need to "reset" the rig each time the animation completed (move it back to the right start values for its properties), ready to animate the next actor.
Caveats about animating a rig instead of an actor There are a few issues to be aware of in cases where you animate a rig with contained actors, rather than animating the actor directly: Animating a rig doesn't always produce the same visual effect as animating an actor directly. For example, compare the following cases: You rotate an actor by 180 degrees in the y axis, then by 90 degrees in the z axis. The actor appears to rotate in a clockwise direction. You rotate the parent container of an actor by 180 degrees in the y axis; then rotate the actor by 90 degrees in the z axis. The actor appears to rotate in an anti-clockwise direction. By rotating the container, the "back" of the actor faces the view point, so the actor's movement appears reversed. See this recipe for more details. There may be other situations where you get similar discrepancies. Animating a rig doesn't change an actor's properties, but animating the actor does. When you animate a container rather than the actor directly, the reported properties of the actor may not reflect its visual appearance. For example, if you apply a scale animation to a container, the final scale of actors inside it (as returned by clutter_actor_get_scale()) will not reflect the scaling applied to their container; whereas directly animating the actors would cause their scale properties to change. Reparenting an actor to a rig can cause the actor to "jump" to the rig's position, unless you align the actor to the rig first. Note that in the sample code, the position of the actor (x, y coordinates) is copied to the rig before the reparenting happens. The actor is then reparented to the rig, and positioned in the rig's top-left corner. So the actor appears to be in the same position, but is now actually inside a rig at the actor's old position. Why bother to do this? Because the rig has a default position of 0,0 (top-left of its container, the stage). If you reparent the actor to the rig, without first copying the actor's position to the rig, the actor appears to "jump" to the rig's position.
Full example The three separate code examples in this section constitute a single application which implements the above solution. <type>ClutterScript</type> JSON defining several rectangles with signal handlers a code sample should be here... but isn't <type>ClutterScript</type> JSON describing a "rig" and a <type>ClutterAnimator</type> animation a code sample should be here... but isn't Loading <type>ClutterScript</type> from JSON files in response to events in a user interface a code sample should be here... but isn't
Moving actors
Problem You want to animate the movement of one or more actors. For example: To move user interface elements in response to user input (e.g. keyboard control of a character in a game). To move a group of actors "off stage" to make way for another group of actors (e.g. paging through thumbnails in a photo viewer). To move an actor to a different position in the interface (e.g. moving an icon for a trashed file into a wastebin).
Solutions Animate the actors movement on one or more axes (x, y, z/depth) using one or more of the approaches available in the Clutter API (implicit animations, ClutterState, ClutterAnimator).
Solution 1: Implicit animations This works well for simple movement of a single actor to a single set of coordinates. Here is an example of how to animate movement of a ClutterActor actor to position 100.0 on x axis: clutter_actor_animate (actor, CLUTTER_LINEAR, 500, "x", 100.0, NULL); See this example which demonstrates movement in each axis, in response to (mouse) button presses.
Solution 2: <type>ClutterState</type> This suits simple, repeated movement of one or more actors between sets of coordinates. Here is an example of how to create two states for a ClutterState instance to move two actors, actor1 and actor2: ClutterState *transitions = clutter_state_new (); /* all state transitions take 250ms */ clutter_state_set_duration (transitions, NULL, NULL, 250); /* create a state called move-down which moves both actors to y = 200.0 */ clutter_state_set (transitions, NULL, "move-down", actor1, "y", CLUTTER_EASE_OUT_CUBIC, 200.0, actor2, "y", CLUTTER_EASE_OUT_CUBIC, 200.0, NULL); /* create a state called move-up which moves both actors to y = 0.0 */ clutter_state_set (transitions, NULL, "move-up", actor1, "y", CLUTTER_EASE_OUT_CUBIC, 0.0, actor2, "y", CLUTTER_EASE_OUT_CUBIC, 0.0, NULL); /* move the actors by setting the state */ clutter_state_set (transitions, "move-down"); This full example shows how to move and simultaneously scale two actors. When a button is pressed on one actor, it is moved and scaled to occupy the right-hand side of the stage; the other actor is simultaneously moved back to the left-hand side of the stage and scaled down.
Solution 3: <type>ClutterAnimator</type> This is a good way to implement complex movement of one or more actors between sets of coordinates. ClutterAnimator *animator = clutter_animator_new (); /* the animation takes 500ms */ clutter_animator_set_duration (animator, 500); /* at the start of the animation, actor should be at 0.0,0.0; * half-way through, at 100.0,100.0; * by the end, actor should be at 150.0,200.0; * note that you can set different easing modes for each * part of the animation and for each property at each key */ clutter_animator_set (animator, /* keys for the start of the animation */ actor, "x", CLUTTER_LINEAR, 0.0, 0.0, actor, "y", CLUTTER_LINEAR, 0.0, 0.0, /* keys for half-way through the animation */ actor, "x", CLUTTER_EASE_OUT_CUBIC, 0.5, 100.0, actor, "y", CLUTTER_EASE_IN_CUBIC, 0.5, 100.0, /* keys for the end of the animation */ actor, "x", CLUTTER_EASE_OUT_EXPO, 1.0, 150.0, actor, "y", CLUTTER_EASE_OUT_CUBIC, 1.0, 200.0, NULL); /* run the animation */ clutter_animator_start (animator); The full example demonstrates how ClutterAnimator can be used to programmatically animate multiple actors: in this case, to simultaneously move three actors to random positions along the x axis. Synchronising the movement of three actors simultaneously using implicit animations would be possible but awkward; ClutterState might be another option, but it wasn't really designed for this case: there are no persistent states to transition between, as the actor positions are generated on each key press. If you want to apply the same movement to a group of actors, rather than different movements for each actor, it's often better to put the actors into a container of some kind and move that instead of moving the actors individually.
Discussion
Movement can take an actor "outside" its container Actor movement in the x and y axes is relative to the actor's parent container. There is nothing to stop you animating an actor until it falls outside the bounds of its container. This could result in the actor moving "off" the interface; though it's worth remembering that the actor is not unparented or destroyed if this happens. To ensure that an actor remains visible, its position should remain within the visible area of the container. In practice, this means either anywhere in the container, if no clip area has been set; or within the container's clip area, if set.
Anchor points can affect movement An actor's anchor point is defined as an x,y coordinate relative to the top-left of the actor. The default anchor point for an actor is in its top-left corner. However, it is possible to set this to some other coordinate, relative to the actor's top-left corner, using the clutter_anchor_set_anchor_point() function. For example: /* set the actor's size to 100px x 100px */ clutter_actor_set_size (actor, 100, 100); /* set an anchor point half-way along the top of the actor */ clutter_actor_set_anchor_point (actor, 50.0, 0.0); In GL terms, the anchor point of an actor is the equivalent of applying an additional transformation of -x, -y to the actor's modelview. If the anchor point is 0, 0, i.e. the top-left corner, then the transformation will leave the actor in the same place. It is important to note that the anchor point will affect the position in which an actor is painted, but will not change the position or size that its parent allocated for it. Finally, the anchor point will affect the other transformations that can be applied to an actor: scaling and rotating. A positive anchor point within the width/height bounds of the actor is inside the actor. An anchor point outside these bounds is outside the actor. You can also set a negative x or y value for the anchor point, which will again place the point outside the actor's bounds. This is important with respect to moving an actor, because you are actually moving the anchor point and "dragging" the actor along with it. For example: you have an actor with width 50px, and you set its anchor-x property to 25.0. If you move that actor on the x axis, you are effectively moving a point half-way across the top of the actor along the x axis (which in turn moves the actor). Similarly, you could set the same actor's anchor-x to -25.0. If you then moved the actor along the x axis, you would effectively be moving the point 25px left of the top of the actor along that axis. The video below demonstrates the effect on movement of shifting the anchor point on the x axis. The red rectangle has anchor-x set to 25.0; the green rectangle has anchor-x set to 0.0 (the default); the blue rectangle has anchor-x set to -25.0. Video showing the effect of anchor point on movement A ClutterAnimator is used to move each of the rectangles to x = 225.0. Although the three rectangles move to the same position on the x axis, it's actually the anchor points which are at the same position. These all align on the x axis with the left-hand edge of the green rectangle.
Actors can move in the <varname>z</varname> axis The examples so far have shown how to move actors in the x and y axes; but it is also possible to move actors in the z axis (i.e. move them closer or further away from the view point). This lets you move actors under/over each other. To move an actor in the z axis, animate its depth property. Animating to a negative depth moves the actor away from the view point; animating to a positive depth moves the actor towards the view point. Changing the depth of an actor also causes perspective effects: the actor gets smaller and converges on the center of the stage as it gets further from the view point, and gets larger and diverges from the center of the stage as it gets closer. This results in an apparent (but not actual) change in the x,y position and scale of the actor. Animating the depth of an actor is slightly different from animating its x and y coordinates, as depth is relative to the whole stage, not just the parent container of the actor. This means that perspective effects are with respect to the whole stage: so as an actor's depth moves below 0.0, it converges on the center of the stage, and may even apparently move outside its container (if the container stays at the same depth). The video below demonstrates the effect of animating the depth of four actors to a value of -15000.0. Note how the actors converge on the center of the stage, as well as appearing to change position and scale; also note that they appear to move outside the bounds of their parent containers (the four yellow ClutterBoxes). Video showing perspective effects when animating actor depth
Movement is affected by constraints An actor can have its x,y position constrained by the position of other actors through ClutterBindConstraints. This can affect movement in two ways: If an actor has its x and/or y properties bound or aligned to another actor's, you can't animate those properties. In effect this means that the bound actor can't be moved on a bound axis directly, but can only be moved by animating the constraint's properties. If you move an actor which has other actors bound to it, the bound actors will also move. For example, if the actor has several other actors whose x properties are bound to its x property, moving the actor on the x axis will also move the bound actors on that axis. Similarly, if some actor is the source for alignment constraints on other actors, moving the source will cause those other actors to move, so that they remain in alignment with it. For example, consider two actors bound by constraints as follows: /* the source actor for the constraint */ ClutterActor *source; /* the actor bound by the constraint */ ClutterActor *target; /* a constraint to be added to target */ ClutterConstraint *constraint; /* ...initialize actors etc... */ /* create a constraint for binding the x position of some actor to the * x position of source */ constraint = clutter_bind_constraint_new (source, CLUTTER_BIND_X, 0.0); /* add the constraint to target with a name */ clutter_actor_add_constraint_with_name (target, "bind-x", constraint); Animating source on the x axis also animates target on the same axis: clutter_actor_animate (source, CLUTTER_LINEAR, 500, "x", 250.0, NULL); ...while this has no effect, as it would violate constraint (it's best not to animate target's x property directly): clutter_actor_animate (target, CLUTTER_LINEAR, 500, "x", 250.0, NULL); But the constraint's properties can be animated, to change how source and target are bound; which in turn moves target: clutter_actor_animate (target, CLUTTER_LINEAR, 500, "@constraints.bind-x.offset", 250.0, NULL); Note the @constraints.<constraint name>.<constraint property> syntax (which is why we needed to use clutter_actor_add_constraint_with_name(), so that the constraint can be accessed through the actor). We are still animating target, but really we're indirectly animating a property of one of its constraints. Another alternative would be to directly animate the constraint's properties through ClutterState or ClutterAnimator, rather than using pseudo-properties on the actor animation: ClutterAnimator *animator = clutter_animator_new (); clutter_animator_set_duration (animator, 500); clutter_animator_set (animator, constraint, "offset", CLUTTER_LINEAR, 0.0, 0.0, constraint, "offset", CLUTTER_LINEAR, 1.0, 250.0, NULL); clutter_animator_start (animator); This could be useful if you need to animate multiple constraints between multiple values simultaneously.
Full examples Simple movement using implicit animations a code sample should be here... but isn't Using <type>ClutterState</type> to repeatedly move (and scale) two actors a code sample should be here... but isn't Using <type>ClutterAnimator</type> to randomly move three actors along the <varname>x</varname> axis a code sample should be here... but isn't
Looping an animation
Problem You want to loop an animation so it plays multiple times.
Solutions Each animation approach can be used to create a looping animation, as described in the following sections. The animation implemented in each case is a simple repeated movement of a rectangle from the right (x = 150.0) to the left (x = 50.0) of the stage, and back again, looped; like this (just a few iterations): Video showing simple looped movement of an actor
Solution 1: looping an implicit animation Implicit animations, started using clutter_actor_animate(), can be looped via their associated ClutterTimeline. Create a ClutterTimeline which is set to loop: ClutterTimeline *timeline = clutter_timeline_new (1000); clutter_timeline_set_loop (timeline, TRUE); Use this timeline when starting an implicit animation on an actor; in this case, to animate the actor's x coordinate from its initial value to 50.0: /* assume actor is a ClutterActor instance */ /* actor's initial x value is 150.0 */ clutter_actor_set_x (actor, 150.0); /* animate the actor (starting the timeline is implicit) */ clutter_actor_animate_with_timeline (actor, CLUTTER_LINEAR, timeline, "x", 50.0, NULL); One further technique is to repeatedly reverse the timeline's direction to create a "closed loop" animation (one which returns to its origin at the end of each iteration). See this section for details. The full code example shows how to run an implicit animation on a loop.
Solution 2: looping with <type>ClutterAnimator</type> A ClutterAnimator animation can also be looped via its ClutterTimeline. However, as ClutterAnimator enables more complex animations, you don't have to manually invert the timeline at the end of each iteration. Instead, you can animate an actor's properties back to their initial values at the end of each iteration of the loop. Creating the timeline and setting it to loop is the same as for implicit animations: ClutterTimeline *timeline = clutter_timeline_new (2000); clutter_timeline_set_loop (timeline, TRUE); Note that the timeline is twice the length of the one for the implicit animation: this is because, unlike the implicit animation, the movement from right to left and back again is a single animation. By contrast, in the implicit animation, the timeline runs forward, for the right to left movement; and then backwards, for the left to right movement. So rather than a 1000ms timeline running twice (once forward, once backward for the implicit animation), we have a 2000ms timeline running once (for ClutterAnimator). Next, create a ClutterAnimator which animates the actor from right to left, then left to right: /* assume actor is a ClutterActor instance */ ClutterAnimator *animator = clutter_animator_new (); /* use the looping timeline as the timeline for the animator */ clutter_animator_set_timeline (animator, timeline); /* set positions for the actor at various points through the animation: * at progress 0.0, x = 150.0 (right of the stage) * at progress 0.5, x = 50.0 (left of the stage) * at progress 1.0, x = 150.0 again (back to the right) */ clutter_animator_set (animator, actor, "x", CLUTTER_LINEAR, 0.0, 150.0, actor, "x", CLUTTER_LINEAR, 0.5, 50.0, actor, "x", CLUTTER_LINEAR, 1.0, 150.0, NULL); Finally, start the animation: clutter_animator_start (animator); See the full example for more details.
Solution 3: looping with <type>ClutterState</type> You can loop ClutterState animations by creating a cycle of states which "swallows its own tail": i.e. goes from a start state, through intermediate state(s), back to the start state, then again through the intermediate states(s), back to the start state, etc., ad infinitum. For the animation we're implementing, there are two states the actor transitions between: The actor's x value is 150.0 (the start/end state, on the right of the stage). The actor's x value is 50.0 (the intermediate state, on the left of the stage). Here is how to add those states to a ClutterState instance: ClutterState *transitions = clutter_state_new (); /* the duration for a transition from any state to any other is 1 second */ clutter_state_set_duration (transitions, NULL, NULL, 1000); clutter_state_set (transitions, NULL, "right", actor, "x", CLUTTER_LINEAR, 150.0, NULL); clutter_state_set (transitions, NULL, "left", actor, "x", CLUTTER_LINEAR, 50.0, NULL); You also need a handler to move the ClutterState to its next state, called each time a state transition is completed: /* handler to move the ClutterState to its next state */ static void next_state (ClutterState *transitions, gpointer user_data) { const gchar *state = clutter_state_get_state (transitions); if (g_strcmp0 (state, "right") == 0) clutter_state_set_state (transitions, "left"); else clutter_state_set_state (transitions, "right"); } Then connect the ClutterState's completed signal to the handler, so that each time a state is reached, the transition to the next state begins: /* connect the ClutterState completed signal to the handler */ g_signal_connect (transitions, "completed", G_CALLBACK (next_state), NULL); Finally, put the ClutterState into the start state to begin the animation: clutter_state_warp_to_state (transitions, "right"); See the full example for more details.
Discussion We use two different approaches to looping in the solutions: Setting the ClutterTimeline to loop (via clutter_timeline_set_loop()). This is the best approach where the timeline is explicit (for ClutterAnimator and implicit animations). Cycling through states in a ClutterState. In this case, the timeline is implicit and we don't need to manually control it: the loop is a consequence of cycling repeatedly through a series of states. The following sections cover some other aspects of looping animations.
Looping a fixed number of times ClutterTimeline doesn't have any built-in functionality to support looping a certain number of times. But it is reasonably easy to count the number of iterations completed and stop the animation when some limit is reached. For example, you could use a static counter to keep track of the iteration count: static guint counter = 0; Implement the looping behaviour as in the above solutions, but use a callback function to set/reset the counter each time the timeline completes. For example, for the ClutterAnimator solution, you would connect the completed signal of the timeline to a callback function: g_signal_connect (timeline, "completed", G_CALLBACK (timeline_completed_cb), NULL); And implement a callback function which resets the counter and stops the timeline if more than two iterations have been counted: static void timeline_completed_cb (ClutterTimeline *timeline, gpointer user_data) { counter++; if (counter > 2) { counter = 0; clutter_timeline_stop (timeline); } } Note that it's simple to count iterations and control the timeline using ClutterAnimator or ClutterState, as the whole animation (right to left and back) is a discrete unit. Doing the same with implicit animations is possible (one forward + one backward run along the timeline is one iteration). But you will be really stretching the implicit animation API beyond its intended use cases.
Creating a "closed loop" with an implicit animation When using implicit animations, at the end of the timeline (before the next iteration of the loop), an actor's properties "jump" back to their initial values (as they were when the timeline started). For example, in the earlier solution, the actor's initial x value was 150.0; so the default behaviour on each iteration of the loop would be to animate the actor to x = 50.0 then jump it immediately back to x = 150.0, before continuing the loop. To prevent this happening, you can create a "closed" loop: animate the actor's properties away from their initial values, then back again. This could be done manually, by creating two separate animations, one the inverse of the other, and chaining them together. However, a simpler solution is to run forward through the timeline once, and have the timeline invert itself when its end is reached. The animation then continues, but in reverse. Once the backward iteration completes, the timeline sets itself to run forward again, etc. To make a timeline reverse its direction each time it completes, use the clutter_timeline_set_auto_reverse() function: clutter_timeline_set_auto_reverse (timeline, TRUE); This is the approach used in the example, which results in a smooth, repeated right to left, left to right motion. See this recipe for more details about inverting a timeline.
Full examples Looping an implicit animation a code sample should be here... but isn't Looping with <type>ClutterAnimator</type> a code sample should be here... but isn't Looping with <type>ClutterState</type> a code sample should be here... but isn't
Animated scaling
Problem You want to animate scaling of an actor. Example use cases: To animate zooming in/out of a texture in an image viewer application. To add an animated "bounce" effect (quick scale up followed by scale down) to a UI element to indicate it has received focus.
Solution Animate the actor's scale-x and scale-y properties to change the scaling on the x and y axes respectively. For example, to animate an actor to twice its current scale with implicit animations: Alternatively, ClutterAnimator or ClutterState can be used to animate an actor's scale properties. See this example which uses ClutterState to animate scaling.
Discussion Scaling an actor is done through its scale-x and scale-y properties, each of which takes a double value. A value of less than 1.0 for an axis scales an actor down on that axis, reducing its apparent size; values greater than 1.0 scale an actor up, increasing its apparent size. Why "apparent" size? Because scaling applies a transform to an actor which changes how it appears on the stage, without changing its "real" size. Similarly, scaling an actor may transform its position: it could appear to move to a different position within its container, although it is "really" at its original position. Run the example to see how size and position are transformed by scaling. It can be useful to know an actor's transformed position and size after scaling: for example, if you were implementing a reflowing layout manager which used scaling as part of its allocation algorithm. Here's an example of how to get these properties for an actor: Note that you can scale an actor on both axes by the same amount (uniform scaling), or by a different amount on each axis (differential scaling). Use clutter_actor_is_scaled() to determine whether scaling has been applied to an actor: this function returns FALSE if both scale-x and scale-y are 1.0; otherwise, it returns TRUE.
Scaling vs. resizing Scaling changes the apparent size of an actor, while leaving its real size unchanged. By contrast, resizing changes the real size of the actor, by modifying its width and height properties. Resizing and scaling produce the same visual effect, as both make an actor appear to be larger or smaller. Therefore, for most purposes, they are interchangeable if you just want to change an actor's apparent size. So why would you scale an actor rather than resize it? If you've scaled an actor, you can easily reset it to its original size, by setting its scale back to 1.0 on both axes. By contrast, to reset a resized actor to its original size, you would have to track the original size manually: the actor doesn't make its original size accessible. Scaling can easily change the apparent size of multiple actors inside a container. For example, say you wanted to shrink multiple actors inside a container to half their original size. There are two ways you could do this: The hard way would be to resize each actor individually. You couldn't just resize the container, as resizing a container doesn't resize its children: usually they will be clipped so that they are either partially or wholly hidden. The easy way would be to set the container's scale to half its initial value: the actors in the container would retain their original sizes, but would appear at half size.
Scaling, layouts and containers It is possible to scale actors inside containers. For example, if you were using a ClutterBox which has a ClutterBoxLayout layout manager, you could scale the children of that layout. However, you should remain aware that layout managers don't take account of the scale of their children, only their size. So if you scale up an actor inside a layout manager, it may overlap other actors in the layout: the size allocated by the layout manager doesn't increase as an actor's scale increases. Similarly, scaling an actor down doesn't reduce the space it will be allocated by a layout.
Setting the scale center An actor's scale center is the point around which scaling occurs: when you scale the actor, it will "shrink" into (if scale < 1.0) or "expand" out of (if scale > 1.0) its scale center. You can change an actor's scale center using either gravity (a named position on the actor; for example, the middle of the top edge of the actor is CLUTTER_GRAVITY_NORTH); or x,y coordinates relative to the actor's anchor point (by default, the anchor point for an actor is at 0,0). Setting scale gravity has the same consequences as setting both the scale-center-x and scale-center-y properties for an actor. For example, CLUTTER_GRAVITY_NORTH_EAST sets the scale center to <width of the actor>, 0, relative to the actor's anchor point (defaults to the top-right corner of the actor). However, the advantage of scale gravities is that they change with the actor: so if the actor is resized, you don't have to manually reset the scale center. This means that CLUTTER_GRAVITY_NORTH_EAST will always represent the top-right corner of the actor, regardless of how it is scaled or resized. The same is true of each of the other scale gravities. If you're animating an actor's scale but want a different scale center, set it before the animation begins. One way to do this is to leave the actor's scale unchanged, but with a different scale center: Another approach is to set scale center properties via GObject, which doesn't require you to figure out the actor's scale first: Once the scale center is set, you can animate the scaling as per usual. It is even possible to animate the scale-center-* properties, which can produce interesting, though slightly unpredictable, effects. It's usually better to change the scale center before the animation starts. The example cycles through the available scale gravities, showing the effect on the animation of each of the scale centers. The second example shows how to combine scaling in and out on a texture, in response to mouse button presses. In this case, the scale gravity remains at CLUTTER_GRAVITY_NORTH_WEST (i.e. at the anchor point of the actor). However, the anchor point is moved to the coordinates of each double click on button 1 (usually the left mouse button) or button 3 (usually the right mouse button); which in turn automatically moves the scale center before the texture is scaled. As a result, the texture "expands" or "contracts" around the clicked point, while the point remains still. One final caveat about scale centers: if an actor is already scaled, the scale center coordinates are relative to the real size of the actor, rather than its transformed size. This can result in a "jumping" effect if you change the scale center on a scaled actor. For example, you might set the scale gravity of an actor to CLUTTER_GRAVITY_WEST, then scale the actor to 0.5 on both axes. Later, you change the actor's scale gravity to CLUTTER_GRAVITY_EAST. The effect of this is to "jump" the actor to the right, so its right-hand edge is aligned with where it was at scale 1.0. If this isn't desirable, you can just retain the scale center on a scaled actor, and only change it when the actor is unscaled.
Full examples Animated scaling of an actor using each of the scale gravities. Press any key to start the animation. a code sample should be here... but isn't Animated scaling (up and down) of a texture in response to button presses. Call with the path to an image as the first argument. a code sample should be here... but isn't