diff --git a/doc/reference/clutter/clutter-animation-tutorial.xml b/doc/reference/clutter/clutter-animation-tutorial.xml index b76ecbbc9..a0bb5144d 100644 --- a/doc/reference/clutter/clutter-animation-tutorial.xml +++ b/doc/reference/clutter/clutter-animation-tutorial.xml @@ -9,51 +9,53 @@ + + + Emmanuele + Bassi + +
+ ebassi@linux.intel.com +
+
+
Creating Animations with Clutter - - - With Clutter using hardware accelration for graphics rendering, + With Clutter using hardware accelration for graphics rendering, complex and fast animations are possible. This chapter describes basic techniques and the utilities Clutter provides in aiding animation - creation. - - + creation.
- Basic Animations + Basic Animations - + The most basic way to create animations with Clutter is via the use + of g_timeout_add(). This enables a callback function to be called at a + defined interval. The callback function can then modify actors visual + properties as to produce an animation. - The most basic way to create animations with Clutter is via the use of - g_timeout_add(). This enables a callback function to be called at a - defined interval. The callback function can then modify actors visual - properties as to produce an animation. - - - - - - Simple Rotation... - - + + Simple timeout example + Implement a rotating actor using 360 "frames" + struct RotationClosure { ClutterActor *actor; - ClutterFixed final_angle; - ClutterFixed current_angle; + + gdouble final_angle; + gdouble current_angle; }; static gboolean rotate_actor (gpointer data) { - RotationClosure *clos = data; + struct RotationClosure *clos = data; - clutter_actor_set_rotationx (clos->actor, clos->current_angle, 0, 0, 0); + clutter_actor_set_rotation (clos->actor, clos->current_angle, 0, 0, 0); /* add one degree */ - clos->current_angle += COGL_FIXED_ONE; + clos->current_angle += 1.0 if (clos->current_angle == clos->final_angle) return FALSE; @@ -64,329 +66,229 @@ rotate_actor (gpointer data) static void rotate_actor_cleanup (gpointer data) { - RotationClosure *clos = data; + struct RotationClosure *clos = data; g_object_unref (clos->actor); g_free (clos); } ... - RotationClosure *clos = NULL; + struct RotationClosure *clos = NULL; - clos = g_new (RotationClosure, 1); + clos = g_new (struct RotationClosure, 1); clos->actor = g_object_ref (an_actor); - clos->final_angle = CLUTTER_FLOAT_TO_FIXED (360.0); + clos->final_angle = 360.0; clos->current_angle = 0; - g_timeout_add_full (1000 / 360, /* fps to interval in milliseconds */ + g_timeout_add_full (1000 / 360, /* 360 updates in one second */ rotate_actor, clos, rotate_actor_cleanup); - - - - Priorities - - - %G_PRIORITY_DEFAULT should always be used as the timeouts priority - (in case of g_timeout_add_full()) as not to intefere with Clutter's - scheduling of repaints and input event handling. - - - + + + + Priorities + %G_PRIORITY_DEFAULT should always be used as the timeouts priority + (in case of g_timeout_add_full()) as not to intefere with Clutter's + scheduling of repaints and input event handling. +
+
- Timelines - - #ClutterTimelines abstract a set period of time with a set frame - rate at which to call a provided callback. - - - - #ClutterTimelines also extend the timeout sources functionality - further by: - - - - Having a set duration (in milliseconds) and a set - 'frame rate' - that is, the rate at which the callback is - called - Passing current progress information to the - callback - Handling 'dropped frames' and guarenteeing the set - duration by skipping over frames if the callback cannot keep up with - the set frame rate - Querying the number of milliseconds elapsed between - the current and previous callback. - Allowing the timeline to be modified on the fly as - well as being stopped, started, looped, rewound and - reversed - Using a #ClutterTimeoutPool to more efficiently - schedule multiple timeout sources without incurring in potential - starvation of the main loop slices - + Timelines - - A Timeline is created with; - + Using g_timeout_add() to control an animation is complicated + and does not work in concert with the rest of the operations Clutter + must perform for each redraw cycle. - -clutter_timeline_new (n_frames, frames_per_seconds); - + For this reason, Clutter provides #ClutterTimeline, a class that + allows scheduling animations with a definite duration. Timelines are + advanced during the redraw cycle so that animations are ready to be + performed at the right time. This also means that animations will not + affect the event processing; it also means that if the animation is too + complex it will be called with a longer delay, thus not blocking the + whole UI. - - Taking a number of frames and a frames per second, or by; - + A Timeline is created with: - -clutter_timeline_new_for_duration (msecs); - + +clutter_timeline_new (duration_in_milliseconds); + - - Which takes the duration of the timeline in milliseconds with a - default frame rate (See clutter_get_default_frame_rate()). - + The duration of the timeline then be modifed via the + #ClutterTimeline:duration property or by using + clutter_timeline_set_duration(). - - - The speed, duration and number of frames of the timeline then be - modifed via the objects properties and API calls. The timeline can - be made to loop by setting its "loop" property to %TRUE. + A timeline is started via clutter_timeline_start() and its + playback further manipulated by the clutter_timeline_pause(), + clutter_timeline_stop(), clutter_timeline_rewind() and + clutter_timeline_skip() functions. - - + By attaching a handler to the timeline's + #ClutterTimeline::new-frame signal a timeline can then be used to + drive an animation by altering an actor's visual properties. The + callback looks like: - The timelines is started via clutter_timeline_start() and its - playback further manipulated by the clutter_timeline_pause(), - clutter_timeline_stop(), clutter_timeline_rewind() and - clutter_timeline_skip() calls. - - - - - By attaching a handler to the timeline's #ClutterTimeline::new-frame - signal a timeline can then be used to drive an animation by altering - an actor's visual properties in this callback. The callback looks like: - - - + void on_new_frame (ClutterTimeline *timeline, - gint frame_num, + gint elapsed_msecs, gpointer user_data) { } - - + - The frame_num parameter is set to the timeline's - current frame number (which is between 1 and the "num-frames" property). - This value can be used to compute the state of a particular animation - that is dependant on the frame numer. The clutter_timeline_get_progress() - function can also be used to get a normalised value of the timeline's - current position between 0 and 1. + The elapsed_msecs parameter is set to the amount + of time elapsed since the beginning of the timeline, and its value is + always between 0 and the #ClutterTimeline:duration value. - - + The function clutter_timeline_get_progress() can also be used to + get a normalised value of the timeline's current position between 0 and + 1. - Timelines can also be played in reverse by setting the direction using - clutter_timeline_set_direction(), and can also have a one-time delay set - before they begin playing by using clutter_timeline_set_delay(). + Timelines can also be played in reverse by setting the direction + using clutter_timeline_set_direction(), and can also have a one-time + delay set before they begin playing by using the function + clutter_timeline_set_delay(). - - + Timelines can also control a pyshical simulation; the + clutter_timeline_get_delta() function allows retrieving the number of + milliseconds elapsed since the previous callback to ensure the physics + engine to be able to take the actual time elapsed between iterations + into account. - Timelines can also control a pyshical simulation; the - clutter_timeline_get_delta() function allows retrieving the number of - frames and milliseconds elapsed since the previous callback to ensure - the physics engine to be able to take the actual time elapsed between - iterations into account. - - - - - - The following example demonstrates rotating an actor with a timeline. - - + + Using a Timeline to drive an animation + Rewrite the example above with a #ClutterTimeline instead of + using g_timeout_add() + #include <clutter/clutter.h> -void -on_new_frame (ClutterTimeline *timeline, - gint frame_num, - gpointer data) +static void +on_new_frame (ClutterTimeline *timeline, + gint elapsed_msecs, + ClutterActor *actor) { - ClutterActor *actor = CLUTTER_ACTOR(data); + gdouble angle = 360 * clutter_timeline_get_progress (timeline); clutter_actor_set_rotation (actor, CLUTTER_Z_AXIS, - (gdouble) frame_num, + angle, clutter_actor_get_width (actor) / 2, clutter_actor_get_height (actor) / 2, 0); } -int -main (int argc, char *argv[]) -{ + ... + ClutterTimeline *timeline; - ClutterActor *stage, *actor; - GdkPixbuf *pixbuf; - - clutter_init (&argc, &argv); - - stage = clutter_stage_get_default (); - - pixbuf = gdk_pixbuf_new_from_file ("an-image.png", NULL); - - actor = clutter_texture_new_from_pixbuf (pixbuf); - - clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor); - - clutter_actor_set_position (actor, 100, 100); - - timeline = clutter_timeline_new_for (360, 60); /* one degree per frame */ + timeline = clutter_timeline_new (1000); /* one second */ clutter_timeline_set_loop (timeline, TRUE); - g_signal_connect (timeline, "new-frame", G_CALLBACK (on_new_frame), actor); - - clutter_actor_show_all (stage); - clutter_timeline_start (timeline); + + - clutter_main(); - - return 0; -} - - - - - Multiple timelines can be sequenced in order by means of the - #ClutterScore. See the #ClutterScore documentation for more details on - using this. - - + Multiple timelines can be sequenced in order by using a + #ClutterScore. See the #ClutterScore documentation for more details on + using this.
+
- Behaviours - + Behaviours + With a large application containing many animations, the use of + just timelines can become unwieldy and difficult to manage, with much + code duplication in the #ClutterTimeline::new-frame handlers that can + require over-complex code changes for minor animation modifications. To + ease these problems the #ClutterAlpha and #ClutterBehaviour classes were + created. - With a large application containing many animations, the use of just - timelines can become unwieldy and difficult to manage with much code - duplication in the new-frame handlers that can require over complex - code changes for minor animation modifications. To ease these - problems the #ClutterAlpha and #ClutterBehaviour classes were created. + #ClutterAlpha and #ClutterBehaviour attempt to generalise the + #ClutterTimeline::new-frame function by defining common actions (or + behaviours) that can be quickly modified, applied to multiple actors or + mixed on a single actor. - - + A #ClutterAlpha is a 'function of time' (and does not refer to the + alpha channel of a RGBA color). It is created by referencing a source + timeline and an "easing mode" whichproduces a value between -1.0 and 2.0 + depending on the progress of the timeline. Clutter provides various + easing modes, as described by the #ClutterAnimationMode enumeration. It is + also possible to register new animation modes using the function + clutter_alpha_register_func() or to provide a custom #ClutterAlphaFunc for + a specific #ClutterAlpha instance. - #ClutterAlpha and #ClutterBehaviour attempt to generalise the - new-frame function by defining common actions or behaviours that can - be quickly modified, applied to multiple actors or mixed on a single - actor. + A #ClutterBehaviour is created with a #ClutterAlpha and a set of + parameters for whatever the behaviour modifies in an actor. The value of + a #ClutterAlpha during the animation is then mapped to a value for the + behaviour parameters and then applied on the actors referenced by the + #ClutterBehaviour. With the #ClutterAlpha's underlying timeline playing + the produced value will change and the behaviour will animate an + actor. - - + A #ClutterBehaviour is effectively 'driven' by a supplied + #ClutterAlpha and when then applied to an actor it will modify a visual + property or feature of the actor dependant on the Alpha's value. For + example, a path-based behaviour applied to an actor will alter its + position along a #ClutterPath, depending on the current alpha value over + time. The actual progress of the motion will depend on the chosen "easing + mode". - A ClutterAlpha is simply a 'function of time' (not a pixel alpha channel!). - It is created by referencing a source timeline and an "easing mode" which - produces a value between -1 and 2 depending on the progress of the - timeline. Clutter provides various easing modes, as described by - the #ClutterAnimationMode enumeration. It is also possible to register - a new animation mode using clutter_alpha_register_func() or to provide - a custom #ClutterAlphaFunc for a specific #ClutterAlpha instance. + Multiple behaviours can of course be applied to an actor as well + as a single behaviour being applied to multiple actors. The separation + of timelines, alphas and behaviours allows for a single timeline to drive + many behaviours each potentially using different alpha functions. + Behaviour parameters can also be changed on the fly. - - + +
+ Effects of alpha functions on a path + +
The actors position between the path's end points + directly correlates to the #ClutterAlpha's current alpha value + driving the behaviour. With the #ClutterAlpha's animation mode + set to %CLUTTER_LINEAR the actor will follow the path at a constant + velocity, but when changing to %CLUTTER_EASE_SINE_IN_OUT the actor + initially accelerates before quickly decelerating.
+
+
- A Behaviour is created with a #ClutterAlpha and a set of limits for - whatever the behaviour modifies in an actor. The current #ClutterAlpha - value is then mapped to a value between these limits and this value - set on any applied actors. With the #ClutterAlpha's underlying - timeline playing the produced value will change and the behaviour - will animate the actor. + The behaviours included in Clutter are: -
- + + + + #ClutterBehaviourDepth + Changes the depth of actors + + + #ClutterBehaviourEllipse + Moves actors along an elliptical path + + + #ClutterBehaviourOpacity + Changes the opacity of actors + + + #ClutterBehaviourPath + Moves actors along a path + + + #ClutterBehaviourRotate + Rotates actors along an axis + + + #ClutterBehaviourScale + Changes the scaling factors of actors + + + - A #ClutterBehaviour is effectively 'driven' by a supplied #ClutterAlpha - and when then applied to an actor it will modify a visual property or - feature of the actor dependant on the Alpha's value. For example a - path based behaviour applied to an actor will alter its position - along the path dependant on the current alpha value over time. The - actual motion will depend on the chosen "easing mode". - - - - - Multiple behaviours can of course be applied to an actor as well as - a single behaviour being applied to multiple actors. The separation - of timelines, alphas and behaviours allows for a single timeline to - drive many behaviours each potentially using different alpha - functions. Behaviour parameters can also be changed on the fly. - - - - - -
- Effects of alpha functions on a path - -
- The actors position between the path's end points directly correlates - to the #ClutterAlpha's current alpha value driving the behaviour. With - the #ClutterAlpha's animation mode set to %CLUTTER_LINEAR the actor - will follow the path at a constant velocity, but when changing to - %CLUTTER_EASE_SINE_IN_OUT the actor initially accelerates before quickly - decelerating. -
-
- -
- - - The behaviours included in Clutter are - - - - - - - #ClutterBehaviourDepth - Changes the depth of actors - - - #ClutterBehaviourEllipse - Moves actors along an ellipsis - - - #ClutterBehaviourOpacity - Changes the opacity of actors - - - #ClutterBehaviourPath - Moves actors along a path - - - #ClutterBehaviourRotate - Rotates actors along an axis - - - #ClutterBehaviourScale - Changes the scaling factors of - actors - - - - - - - - The following example demonstrates an ellipse behaviour in action. - - + + Using a #ClutterBehaviour + The following example demonstrates an ellipse behaviour in + action. + #include <clutter/clutter.h> int @@ -402,18 +304,23 @@ main (int argc, char *argv[]) stage = clutter_stage_get_default (); - pixbuf = gdk_pixbuf_new_from_file ("ohpowers.png", NULL); - - actor = clutter_texture_new_from_pixbuf (pixbuf); - + actor = clutter_texture_new_from_file ("ohpowers.png, NULL); clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor); - timeline = clutter_timeline_new_for_duration (4000); /* milliseconds */ + /* set up the animation to be 4 seconds long */ + timeline = clutter_timeline_new (4000); clutter_timeline_set_loop (timeline, TRUE); - /* Set an alpha func to power the behaviour */ + /* set up a sinusoidal easing mode to power the behaviour; the + * alpha will take a reference on the timeline so we can safely + * release the reference we hold + */ alpha = clutter_alpha_new_full (timeline, CLUTTER_EASE_SINE_IN_OUT); + g_object_unref (timeline); + /* the behaviour will own the alpha by sinking its floating + * reference (if needed) + */ behave = clutter_behaviour_ellipse_new (alpha, 200, /* center x */ 200, /* center y */ @@ -431,128 +338,121 @@ main (int argc, char *argv[]) clutter_main(); - /* clean up */ + /* clean up; behaviours are top-level objects */ g_object_unref (behave); - g_object_unref (timeline); return 0; } + + - - + The parameters of a #ClutterBehaviour can be changed whilst + a animation is running. - Behaviour parameters can be changed whilst a animation is running + There can be many #ClutterAlpha's attached to a single timeline. + There can be many behaviours for a #ClutterAlpha. There can be many + behaviours applied to an actor. A #ClutterScore can be used to chain + many behaviours together. - - There can be many #ClutterAlpha's attached to a single timeline. There - can be many behaviours for a #ClutterAlpha. There can be many behaviours - applied to an actor. A #ClutterScore can be used to chain many behaviour - together. - + Combining behaviours that effect the same actor properties + (i.e two separate paths) will cause unexpected results. The values will + not be merged in any way with only the last applied behaviour taking + precedence. - Combining behaviours that effect the same actor properties - (i.e two separate paths) will cause unexpected results. The values - will not be merged in any way with only the last applied behaviour taking - precedence. - - - Tips for implementing a new behaviour can be found here. - + Tips for implementing a new behaviour can be found here.
+
- Implicit Animations + Implicit Animations - Using behaviours for simple animations of a single actor may - be too complicated, in terms of memory management and bookkeeping - of the object instances. For this reason, Clutter also provides a - simple animation API for implicit animations using properties of - an actor: clutter_actor_animate(). + Using behaviours for simple animations of a single actor may + be too complicated, in terms of memory management and bookkeeping + of the object instances. For this reason, Clutter also provides a + simple animation API for implicit animations using properties of + an actor: clutter_actor_animate(). - The clutter_actor_animate() family of functions will create - and use an implicit #ClutterAnimation instance, which will then - handle the animation of one or more #ClutterActor properties between - a range of values. + The clutter_actor_animate() family of functions will create + and use an implicit #ClutterAnimation instance, which will then + handle the animation of one or more #ClutterActor properties between + a range of values. - - - The following example demonstrates how to use the - clutter_actor_animate() method to tween an actor - between the current position and a new set of coordinates. - The animation takes 200 milliseconds to complete and - uses a linear speed. - - - clutter_actor_animate (actor, CLUTTER_LINEAR, 200 - "x", 200, - "y", 200, + + Using clutter_actor_animate() + The following example demonstrates how to use the + clutter_actor_animate() method to tween an actor between the current + position and a new set of coordinates. The animation takes 200 + milliseconds to complete and uses a linear progression. + + clutter_actor_animate (actor, CLUTTER_LINEAR, 200 + "x", 200, + "y", 200, + NULL); + + + + The clutter_actor_animate() method returns a #ClutterAnimation + instance that can be used to start, stop and modify the animation + while it's running. The #ClutterAnimation::completed signal will + be emitted when the animation has been completed. + + When the animation is complete it will be automatically + unreferenced, and disposed if nothing else is holding a reference + on it. + + + Animating inside an event handler + The following example demonstrates how to animate an actor + inside the signal handler for a button press event. If the user + presses the button on a new position while the animation is running, + the animation will be restarted with the new final values + updated. + + static gboolean + on_button_press (ClutterActor *actor, + ClutterEvent *event, + gpointer user_data) + { + gfloat event_x, event_y; + + clutter_event_get_coords (event, &event_x, &event_y); + clutter_actor_animate (actor, CLUTTER_EASE_SINE_OUT, 500, + "x", event_x, + "y", event_y, NULL); - - + return TRUE; + } + + - The clutter_actor_animate() method returns a #ClutterAnimation - instance that can be used to start, stop and modify the animation - while it's running. The #ClutterAnimation::completed signal will - be emitted when the animation has been completed. + Calling clutter_actor_animate() multiple times on an + actor which is being animated will cause the animation to be updated + with the new values. - When the animation is complete it will be automatically - unreferenced, and disposed if nothing else is holding a reference - on it. - - Calling clutter_actor_animate() multiple times on an - actor which is being animated will cause the animation to be updated - with the new values. - - - - The following example demonstrates how to animate an actor - inside the signal handler for a button press event. If the - user presses the button on a new position while the animation - is running, the animation will be restarted with the new - final values updated. - - - static gboolean - on_button_press (ClutterActor *actor, - ClutterButtonEvent *event, - gpointer user_data) - { - clutter_actor_animate (actor, CLUTTER_EASE_SINE_OUT, 500, - "x", event->x, - "y", event->y, - NULL); - return TRUE; - } - - + If you need to chain up multiple animations created using + clutter_actor_animate() you should connect to the + #ClutterAnimation::completed signal using g_signal_connect_after() + to have the guarantee that the current #ClutterAnimation has been + detached from the actor. The documentation for clutter_actor_animate() + has further examples.
- Conclusion - + Conclusion + Clutter provides a number of utility classes to aid animations + and complex animations can be produced by combining the various features + provided. - Clutter provides a number of utility classes to aid animations and - complex animations can be produced by combining the various features - provided. - - - - - Of course animations can becreated outside of the Clutter animation - framework, as the framework is not expected to cover every kind of - possible animation scenario. - - - - - The animation functionality in Clutter is primarily suited to - building animations with a set or finite running time - i.e transitions - and the like. For animations involving variable input (such as touchscreen - handling) physical simulations may be more suited. - - + Of course animations can becreated outside of the Clutter animation + framework, as the framework is not expected to cover every kind of + possible animation scenario. + The animation functionality in Clutter is primarily suited to + building animations with a set or finite running time - i.e transitions + between states. For animations involving variable input (such as + touchscreen handling) physical simulations may be more suited.
+