[docs] Update the animations tutorial

Bring the Animation framework introduction/tutorial up to the 1.0
API for timelines and animations.
This commit is contained in:
Emmanuele Bassi 2009-07-21 11:29:52 +01:00
parent fdfd208c04
commit 78773ab6fe

View File

@ -9,51 +9,53 @@
</address> </address>
</affiliation> </affiliation>
</author> </author>
<author>
<firstname>Emmanuele</firstname>
<surname>Bassi</firstname>
<affiliation>
<address>
<email>ebassi@linux.intel.com</email>
</address>
</affiliation>
</author>
</chapterinfo> </chapterinfo>
<title>Creating Animations with Clutter</title> <title>Creating Animations with Clutter</title>
<para> <para>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 complex and fast animations are possible. This chapter describes basic
techniques and the utilities Clutter provides in aiding animation techniques and the utilities Clutter provides in aiding animation
creation. creation.</para>
</para>
<section id="clutter-animation-basic"> <section id="clutter-animation-basic">
<title>Basic Animations</title> <title>Basic Animations</title>
<para> <para>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
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 defined interval. The callback function can then modify actors visual
properties as to produce an animation. properties as to produce an animation.</para>
</para>
<example id="clutter-timeout-example"> <example id="clutter-timeout-example">
<para> <title>Simple timeout example</title>
Simple Rotation... <para>Implement a rotating actor using 360 "frames"</para>
</para>
<programlisting> <programlisting>
struct RotationClosure { struct RotationClosure {
ClutterActor *actor; ClutterActor *actor;
ClutterFixed final_angle;
ClutterFixed current_angle; gdouble final_angle;
gdouble current_angle;
}; };
static gboolean static gboolean
rotate_actor (gpointer data) rotate_actor (gpointer data)
{ {
RotationClosure *clos = data; struct RotationClosure *clos = data;
clutter_actor_set_rotationx (clos-&gt;actor, clos-&gt;current_angle, 0, 0, 0); clutter_actor_set_rotation (clos-&gt;actor, clos-&gt;current_angle, 0, 0, 0);
/* add one degree */ /* add one degree */
clos-&gt;current_angle += COGL_FIXED_ONE; clos-&gt;current_angle += 1.0
if (clos-&gt;current_angle == clos-&gt;final_angle) if (clos-&gt;current_angle == clos-&gt;final_angle)
return FALSE; return FALSE;
@ -64,294 +66,196 @@ rotate_actor (gpointer data)
static void static void
rotate_actor_cleanup (gpointer data) rotate_actor_cleanup (gpointer data)
{ {
RotationClosure *clos = data; struct RotationClosure *clos = data;
g_object_unref (clos-&gt;actor); g_object_unref (clos-&gt;actor);
g_free (clos); g_free (clos);
} }
... ...
RotationClosure *clos = NULL; struct RotationClosure *clos = NULL;
clos = g_new (RotationClosure, 1); clos = g_new (struct RotationClosure, 1);
clos-&gt;actor = g_object_ref (an_actor); clos-&gt;actor = g_object_ref (an_actor);
clos-&gt;final_angle = CLUTTER_FLOAT_TO_FIXED (360.0); clos-&gt;final_angle = 360.0;
clos-&gt;current_angle = 0; clos-&gt;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, rotate_actor,
clos, clos,
rotate_actor_cleanup); rotate_actor_cleanup);
</programlisting> </programlisting>
</example> </example>
<note><title>Priorities</title> <note>
<para> <title>Priorities</title>
<para>%G_PRIORITY_DEFAULT should always be used as the timeouts priority
%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 (in case of g_timeout_add_full()) as not to intefere with Clutter's
scheduling of repaints and input event handling. scheduling of repaints and input event handling.</para>
</para>
</note> </note>
</section> </section>
<section id="clutter-animation-timelines"> <section id="clutter-animation-timelines">
<title>Timelines</title> <title>Timelines</title>
<para>
#ClutterTimeline<!-- -->s abstract a set period of time with a set frame
rate at which to call a provided callback.
</para>
<para> <para>Using g_timeout_add() to control an animation is complicated
#ClutterTimeline<!-- -->s also extend the timeout sources functionality and does not work in concert with the rest of the operations Clutter
further by: must perform for each redraw cycle.</para>
</para>
<orderedlist> <para>For this reason, Clutter provides #ClutterTimeline, a class that
<listitem><para>Having a set duration (in milliseconds) and a set allows scheduling animations with a definite duration. Timelines are
'frame rate' - that is, the rate at which the callback is advanced during the redraw cycle so that animations are ready to be
called</para></listitem> performed at the right time. This also means that animations will not
<listitem><para>Passing current progress information to the affect the event processing; it also means that if the animation is too
callback</para></listitem> complex it will be called with a longer delay, thus not blocking the
<listitem><para>Handling 'dropped frames' and guarenteeing the set whole UI.</para>
duration by skipping over frames if the callback cannot keep up with
the set frame rate</para></listitem>
<listitem><para>Querying the number of milliseconds elapsed between
the current and previous callback.</para></listitem>
<listitem><para>Allowing the timeline to be modified on the fly as
well as being stopped, started, looped, rewound and
reversed</para></listitem>
<listitem><para>Using a #ClutterTimeoutPool to more efficiently
schedule multiple timeout sources without incurring in potential
starvation of the main loop slices</para></listitem>
</orderedlist>
<para> <para>A Timeline is created with:</para>
A Timeline is created with;
</para>
<programlisting> <programlisting>
clutter_timeline_new (n_frames, frames_per_seconds); clutter_timeline_new (duration_in_milliseconds);
</programlisting> </programlisting>
<para> <para>The duration of the timeline then be modifed via the
Taking a number of frames and a frames per second, or by; #ClutterTimeline:duration property or by using
</para> clutter_timeline_set_duration().</para>
<programlisting> <para>A timeline is started via clutter_timeline_start() and its
clutter_timeline_new_for_duration (msecs);
</programlisting>
<para>
Which takes the duration of the timeline in milliseconds with a
default frame rate (See clutter_get_default_frame_rate()).
</para>
<para>
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.
</para>
<para>
The timelines is started via clutter_timeline_start() and its
playback further manipulated by the clutter_timeline_pause(), playback further manipulated by the clutter_timeline_pause(),
clutter_timeline_stop(), clutter_timeline_rewind() and clutter_timeline_stop(), clutter_timeline_rewind() and
clutter_timeline_skip() calls. clutter_timeline_skip() functions.</para>
</para> <para>By attaching a handler to the timeline's
<para> #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:</para>
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:
</para>
<programlisting> <programlisting>
void void
on_new_frame (ClutterTimeline *timeline, on_new_frame (ClutterTimeline *timeline,
gint frame_num, gint elapsed_msecs,
gpointer user_data) gpointer user_data)
{ {
} }
</programlisting> </programlisting>
<para>
The <literal>frame_num</literal> parameter is set to the timeline's <para>The <literal>elapsed_msecs</literal> parameter is set to the amount
current frame number (which is between 1 and the "num-frames" property). of time elapsed since the beginning of the timeline, and its value is
This value can be used to compute the state of a particular animation always between 0 and the #ClutterTimeline:duration value.</para>
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.
</para> <para>The function clutter_timeline_get_progress() can also be used to
<para> get a normalised value of the timeline's current position between 0 and
1.</para>
Timelines can also be played in reverse by setting the direction using <para>Timelines can also be played in reverse by setting the direction
clutter_timeline_set_direction(), and can also have a one-time delay set using clutter_timeline_set_direction(), and can also have a one-time
before they begin playing by using clutter_timeline_set_delay(). delay set before they begin playing by using the function
clutter_timeline_set_delay().</para>
</para> <para>Timelines can also control a pyshical simulation; the
<para>
Timelines can also control a pyshical simulation; the
clutter_timeline_get_delta() function allows retrieving the number of clutter_timeline_get_delta() function allows retrieving the number of
frames and milliseconds elapsed since the previous callback to ensure milliseconds elapsed since the previous callback to ensure the physics
the physics engine to be able to take the actual time elapsed between engine to be able to take the actual time elapsed between iterations
iterations into account. into account.</para>
</para>
<example id="clutter-timeline-example"> <example id="clutter-timeline-example">
<para> <title>Using a Timeline to drive an animation</title>
The following example demonstrates rotating an actor with a timeline. <para>Rewrite the example above with a #ClutterTimeline instead of
</para> using g_timeout_add()</para>
<programlisting> <programlisting>
#include &lt;clutter/clutter.h&gt; #include &lt;clutter/clutter.h&gt;
void static void
on_new_frame (ClutterTimeline *timeline, on_new_frame (ClutterTimeline *timeline,
gint frame_num, gint elapsed_msecs,
gpointer data) ClutterActor *actor)
{ {
ClutterActor *actor = CLUTTER_ACTOR(data); gdouble angle = 360 * clutter_timeline_get_progress (timeline);
clutter_actor_set_rotation (actor, CLUTTER_Z_AXIS, clutter_actor_set_rotation (actor, CLUTTER_Z_AXIS,
(gdouble) frame_num, angle,
clutter_actor_get_width (actor) / 2, clutter_actor_get_width (actor) / 2,
clutter_actor_get_height (actor) / 2, clutter_actor_get_height (actor) / 2,
0); 0);
} }
int ...
main (int argc, char *argv[])
{
ClutterTimeline *timeline; ClutterTimeline *timeline;
ClutterActor *stage, *actor; timeline = clutter_timeline_new (1000); /* one second */
GdkPixbuf *pixbuf;
clutter_init (&amp;argc, &amp;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 */
clutter_timeline_set_loop (timeline, TRUE); clutter_timeline_set_loop (timeline, TRUE);
g_signal_connect (timeline, "new-frame", G_CALLBACK (on_new_frame), actor); g_signal_connect (timeline, "new-frame", G_CALLBACK (on_new_frame), actor);
clutter_actor_show_all (stage);
clutter_timeline_start (timeline); clutter_timeline_start (timeline);
clutter_main();
return 0;
}
</programlisting> </programlisting>
</example> </example>
<note><para> <note><para>Multiple timelines can be sequenced in order by using a
Multiple timelines can be sequenced in order by means of the
#ClutterScore. See the #ClutterScore documentation for more details on #ClutterScore. See the #ClutterScore documentation for more details on
using this. using this.</para></note>
</para></note>
</section> </section>
<section id="clutter-animation-behaviours"> <section id="clutter-animation-behaviours">
<title>Behaviours</title> <title>Behaviours</title>
<para> <para>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.</para>
With a large application containing many animations, the use of just <para>#ClutterAlpha and #ClutterBehaviour attempt to generalise the
timelines can become unwieldy and difficult to manage with much code #ClutterTimeline::new-frame function by defining common actions (or
duplication in the new-frame handlers that can require over complex behaviours) that can be quickly modified, applied to multiple actors or
code changes for minor animation modifications. To ease these mixed on a single actor.</para>
problems the #ClutterAlpha and #ClutterBehaviour classes were created.
</para> <para>A #ClutterAlpha is a 'function of time' (and does not refer to the
<para> 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.</para>
#ClutterAlpha and #ClutterBehaviour attempt to generalise the <para>A #ClutterBehaviour is created with a #ClutterAlpha and a set of
new-frame function by defining common actions or behaviours that can parameters for whatever the behaviour modifies in an actor. The value of
be quickly modified, applied to multiple actors or mixed on a single a #ClutterAlpha during the animation is then mapped to a value for the
actor. 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.</para>
</para> <para>A #ClutterBehaviour is effectively 'driven' by a supplied
<para> #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".</para>
A ClutterAlpha is simply a 'function of time' (not a pixel alpha channel!). <para> Multiple behaviours can of course be applied to an actor as well
It is created by referencing a source timeline and an "easing mode" which as a single behaviour being applied to multiple actors. The separation
produces a value between -1 and 2 depending on the progress of the of timelines, alphas and behaviours allows for a single timeline to drive
timeline. Clutter provides various easing modes, as described by many behaviours each potentially using different alpha functions.
the #ClutterAnimationMode enumeration. It is also possible to register Behaviour parameters can also be changed on the fly.</para>
a new animation mode using clutter_alpha_register_func() or to provide
a custom #ClutterAlphaFunc for a specific #ClutterAlpha instance.
</para>
<para>
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.
</para>
<para>
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".
</para>
<para>
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.
</para>
<para> <para>
<figure id="behaviour-path-alpha"> <figure id="behaviour-path-alpha">
<title>Effects of alpha functions on a path</title> <title>Effects of alpha functions on a path</title>
<graphic fileref="path-alpha-func.png" format="PNG"/> <graphic fileref="path-alpha-func.png" format="PNG"/>
<blockquote> <blockquote>The actors position between the path's end points
The actors position between the path's end points directly correlates directly correlates to the #ClutterAlpha's current alpha value
to the #ClutterAlpha's current alpha value driving the behaviour. With driving the behaviour. With the #ClutterAlpha's animation mode
the #ClutterAlpha's animation mode set to %CLUTTER_LINEAR the actor set to %CLUTTER_LINEAR the actor will follow the path at a constant
will follow the path at a constant velocity, but when changing to velocity, but when changing to %CLUTTER_EASE_SINE_IN_OUT the actor
%CLUTTER_EASE_SINE_IN_OUT the actor initially accelerates before quickly initially accelerates before quickly decelerating.</blockquote>
decelerating.
</blockquote>
</figure> </figure>
</para> </para>
<para>The behaviours included in Clutter are:</para>
<para> <para>
The behaviours included in Clutter are
</para>
<para>
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term>#ClutterBehaviourDepth</term> <term>#ClutterBehaviourDepth</term>
@ -359,7 +263,7 @@ main (int argc, char *argv[])
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term>#ClutterBehaviourEllipse</term> <term>#ClutterBehaviourEllipse</term>
<listitem><simpara>Moves actors along an ellipsis</simpara></listitem> <listitem><simpara>Moves actors along an elliptical path</simpara></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term>#ClutterBehaviourOpacity</term> <term>#ClutterBehaviourOpacity</term>
@ -375,17 +279,15 @@ main (int argc, char *argv[])
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term>#ClutterBehaviourScale</term> <term>#ClutterBehaviourScale</term>
<listitem><simpara>Changes the scaling factors of <listitem><simpara>Changes the scaling factors of actors</simpara></listitem>
actors</simpara></listitem>
</varlistentry> </varlistentry>
</variablelist> </variablelist>
</para> </para>
<example id="clutter-behaviour-example"> <example id="clutter-behaviour-example">
<para> <title>Using a #ClutterBehaviour</title>
The following example demonstrates an ellipse behaviour in action. <para>The following example demonstrates an ellipse behaviour in
</para> action.</para>
<programlisting> <programlisting>
#include &lt;clutter/clutter.h&gt; #include &lt;clutter/clutter.h&gt;
@ -402,18 +304,23 @@ main (int argc, char *argv[])
stage = clutter_stage_get_default (); stage = clutter_stage_get_default ();
pixbuf = gdk_pixbuf_new_from_file ("ohpowers.png", NULL); actor = clutter_texture_new_from_file ("ohpowers.png, NULL);
actor = clutter_texture_new_from_pixbuf (pixbuf);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor); 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); 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); 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, behave = clutter_behaviour_ellipse_new (alpha,
200, /* center x */ 200, /* center x */
200, /* center y */ 200, /* center y */
@ -431,36 +338,32 @@ main (int argc, char *argv[])
clutter_main(); clutter_main();
/* clean up */ /* clean up; behaviours are top-level objects */
g_object_unref (behave); g_object_unref (behave);
g_object_unref (timeline);
return 0; return 0;
} }
</programlisting> </programlisting>
</example> </example>
<note>Behaviour parameters can be changed whilst a animation is running</note> <note><para>The parameters of a #ClutterBehaviour can be changed whilst
a animation is running.</para></note>
<para> <para>There can be many #ClutterAlpha's attached to a single timeline.
There can be many #ClutterAlpha's attached to a single timeline. There There can be many behaviours for a #ClutterAlpha. There can be many
can be many behaviours for a #ClutterAlpha. There can be many behaviours behaviours applied to an actor. A #ClutterScore can be used to chain
applied to an actor. A #ClutterScore can be used to chain many behaviour many behaviours together.</para>
together.
</para>
<warning><para>Combining behaviours that effect the same actor properties <warning><para>Combining behaviours that effect the same actor properties
(i.e two separate paths) will cause unexpected results. The values (i.e two separate paths) will cause unexpected results. The values will
will not be merged in any way with only the last applied behaviour taking not be merged in any way with only the last applied behaviour taking
precedence.</para></warning> precedence.</para></warning>
<para> <note><para>Tips for implementing a new behaviour can be found <link
Tips for implementing a new behaviour can be found <link linkend="creating-your-own-behaviours">here</link>.</para></note>
linkend="creating-your-own-behaviours">here</link>.
</para>
</section> </section>
<section id="clutter-animation-implicit"> <section id="clutter-animation-implicit">
<title>Implicit Animations</title> <title>Implicit Animations</title>
@ -476,13 +379,11 @@ main (int argc, char *argv[])
a range of values.</para> a range of values.</para>
<example id="clutter-actor-animate-example"> <example id="clutter-actor-animate-example">
<para> <title>Using clutter_actor_animate()</title>
The following example demonstrates how to use the <para>The following example demonstrates how to use the
clutter_actor_animate() method to tween an actor clutter_actor_animate() method to tween an actor between the current
between the current position and a new set of coordinates. position and a new set of coordinates. The animation takes 200
The animation takes 200 milliseconds to complete and milliseconds to complete and uses a linear progression.</para>
uses a linear speed.
</para>
<programlisting> <programlisting>
clutter_actor_animate (actor, CLUTTER_LINEAR, 200 clutter_actor_animate (actor, CLUTTER_LINEAR, 200
"x", 200, "x", 200,
@ -500,59 +401,58 @@ main (int argc, char *argv[])
unreferenced, and disposed if nothing else is holding a reference unreferenced, and disposed if nothing else is holding a reference
on it.</para></warning> on it.</para></warning>
<para>Calling clutter_actor_animate() multiple times on an
actor which is being animated will cause the animation to be updated
with the new values.</para>
<example id="clutter-actor-animate-multi-example"> <example id="clutter-actor-animate-multi-example">
<para> <title>Animating inside an event handler</title>
The following example demonstrates how to animate an actor <para>The following example demonstrates how to animate an actor
inside the signal handler for a button press event. If the inside the signal handler for a button press event. If the user
user presses the button on a new position while the animation presses the button on a new position while the animation is running,
is running, the animation will be restarted with the new the animation will be restarted with the new final values
final values updated. updated.</para>
</para>
<programlisting> <programlisting>
static gboolean static gboolean
on_button_press (ClutterActor *actor, on_button_press (ClutterActor *actor,
ClutterButtonEvent *event, ClutterEvent *event,
gpointer user_data) gpointer user_data)
{ {
gfloat event_x, event_y;
clutter_event_get_coords (event, &amp;event_x, &amp;event_y);
clutter_actor_animate (actor, CLUTTER_EASE_SINE_OUT, 500, clutter_actor_animate (actor, CLUTTER_EASE_SINE_OUT, 500,
"x", event-&gt;x, "x", event_x,
"y", event-&gt;y, "y", event_y,
NULL); NULL);
return TRUE; return TRUE;
} }
</programlisting> </programlisting>
</example> </example>
<para>Calling clutter_actor_animate() multiple times on an
actor which is being animated will cause the animation to be updated
with the new values.</para>
<para>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.</para>
</section> </section>
<section id="clutter-animation-conclusion"> <section id="clutter-animation-conclusion">
<title>Conclusion</title> <title>Conclusion</title>
<para> <para>Clutter provides a number of utility classes to aid animations
and complex animations can be produced by combining the various features
provided.</para>
Clutter provides a number of utility classes to aid animations and <para>Of course animations can becreated outside of the Clutter animation
complex animations can be produced by combining the various features
provided.
</para>
<para>
Of course animations can becreated outside of the Clutter animation
framework, as the framework is not expected to cover every kind of framework, as the framework is not expected to cover every kind of
possible animation scenario. possible animation scenario.</para>
</para> <para>The animation functionality in Clutter is primarily suited to
<para>
The animation functionality in Clutter is primarily suited to
building animations with a set or finite running time - i.e transitions building animations with a set or finite running time - i.e transitions
and the like. For animations involving variable input (such as touchscreen between states. For animations involving variable input (such as
handling) physical simulations may be more suited. touchscreen handling) physical simulations may be more suited.</para>
</para>
</section> </section>
</chapter> </chapter>