mirror of
https://github.com/brl/mutter.git
synced 2024-12-25 04:22:05 +00:00
cookbook: Recipe for "looping animations"
Added a recipe giving examples of how to loop animations for each part of the animation API (implicit, animator, state). The discussion covers looping a fixed number of times and inverting a single implicit animation to create a loop which goes back to its start on each iteration.
This commit is contained in:
parent
e205bd0fdc
commit
8bdfa4ddee
@ -2311,19 +2311,20 @@ clutter_animator_start (animator);
|
|||||||
<section>
|
<section>
|
||||||
<title>Problem</title>
|
<title>Problem</title>
|
||||||
|
|
||||||
<para>You want to loop an animation so it repeats indefinitely.</para>
|
<para>You want to loop an animation so it plays multiple times.</para>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<title>Solutions</title>
|
<title>Solutions</title>
|
||||||
|
|
||||||
<para>Each <link linkend="animations-introduction-api">animation
|
<para>Each <link linkend="animations-introduction-api">animation
|
||||||
approach</link> can be used to create a looping animation. The
|
approach</link> can be used to create a looping animation, as
|
||||||
approach for each is covered below.</para>
|
described in the following sections.</para>
|
||||||
|
|
||||||
<para>The animation implemented in each case is a simple repeated
|
<para>The animation implemented in each case is a simple repeated
|
||||||
movement of a rectangle from the right to the left of the state,
|
movement of a rectangle from the right (<code>x = 150.0</code>)
|
||||||
then back, like this:</para>
|
to the left (<code>x = 50.0</code>) of the stage, and back again,
|
||||||
|
looped; like this (just a few iterations):</para>
|
||||||
|
|
||||||
<inlinemediaobject>
|
<inlinemediaobject>
|
||||||
<videoobject>
|
<videoobject>
|
||||||
@ -2334,63 +2335,215 @@ clutter_animator_start (animator);
|
|||||||
</alt>
|
</alt>
|
||||||
</inlinemediaobject>
|
</inlinemediaobject>
|
||||||
|
|
||||||
<section>
|
<section id="animations-looping-solutions-implicit">
|
||||||
<title>Solution 1: looping an implicit animation</title>
|
<title>Solution 1: looping an implicit animation</title>
|
||||||
|
|
||||||
<para>Implicit animations (started using
|
<para>Implicit animations, started using
|
||||||
<function>clutter_actor_animate()</function>) can be looped via
|
<function>clutter_actor_animate()</function>, can be looped via
|
||||||
their associated <type>ClutterTimeline</type>.</para>
|
their associated <type>ClutterTimeline</type>.</para>
|
||||||
|
|
||||||
<para>First, create a <type>ClutterTimeline</type> which is
|
<para>Create a <type>ClutterTimeline</type> which is
|
||||||
set to loop:</para>
|
set to loop:</para>
|
||||||
|
|
||||||
<informalexample>
|
<informalexample>
|
||||||
<programlisting>
|
<programlisting>
|
||||||
???
|
ClutterTimeline *timeline = clutter_timeline_new (1000);
|
||||||
|
clutter_timeline_set_loop (timeline, TRUE);
|
||||||
</programlisting>
|
</programlisting>
|
||||||
</informalexample>
|
</informalexample>
|
||||||
|
|
||||||
<para>Use this timeline when starting the animation on an
|
<para>Use this timeline when starting an implicit animation on an
|
||||||
actor:</para>
|
actor; in this case, to animate the actor's <varname>x</varname>
|
||||||
|
coordinate from its initial value to <code>50.0</code>:</para>
|
||||||
|
|
||||||
<informalexample>
|
<informalexample>
|
||||||
<programlisting>
|
<programlisting>
|
||||||
???
|
/* assume <varname>actor</varname> is a <type>ClutterActor</type> 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);
|
||||||
</programlisting>
|
</programlisting>
|
||||||
</informalexample>
|
</informalexample>
|
||||||
|
|
||||||
<para>NB at the end of the timeline, before the next iteration
|
<para>One further technique is to swop the timeline's
|
||||||
of the loop, the actor will "jump" back
|
direction to create a "closed loop" animation (one which returns
|
||||||
to where it was when the animation started. To prevent this
|
to its origin at the end of each iteration). See
|
||||||
happening, you can invert the direction of the timeline
|
<link linkend="animations-looping-discussion-closed-loop">this
|
||||||
each time an iteration completes. This will then make the
|
section</link> for details.</para>
|
||||||
animation run "backwards" on the next iteration.
|
|
||||||
<link linkend="animations-looping-example-1">This example</link>
|
<para><link linkend="animations-looping-example-1">The full
|
||||||
demonstrates how to do run an animation forwards and backwards
|
code example</link> shows how to run an implicit animation on
|
||||||
on a loop. See <link linkend="animations-inversion">this recipe</link>
|
a loop.</para>
|
||||||
for more details.</para>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<title>Solution 2: looping with <type>ClutterAnimator</type></title>
|
<title>Solution 2: looping with <type>ClutterAnimator</type></title>
|
||||||
|
|
||||||
<para>???</para>
|
<para>A <type>ClutterAnimator</type> animation can also be looped
|
||||||
|
via its <type>ClutterTimeline</type>. However, as
|
||||||
|
<type>ClutterAnimator</type> 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.</para>
|
||||||
|
|
||||||
|
<para>Creating the timeline and setting it to loop is the same
|
||||||
|
as for implicit animations:</para>
|
||||||
|
|
||||||
|
<informalexample>
|
||||||
|
<programlisting>
|
||||||
|
ClutterTimeline *timeline = clutter_timeline_new (2000);
|
||||||
|
clutter_timeline_set_loop (timeline, TRUE);
|
||||||
|
</programlisting>
|
||||||
|
</informalexample>
|
||||||
|
|
||||||
|
<para>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 <emphasis>single</emphasis> 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
|
||||||
|
<type>ClutterAnimator</type>).</para>
|
||||||
|
|
||||||
|
<para>Next, create a <type>ClutterAnimator</type> which animates
|
||||||
|
the actor from right to left, then left to right:</para>
|
||||||
|
|
||||||
|
<informalexample>
|
||||||
|
<programlisting>
|
||||||
|
/* assume <varname>actor</varname> is a <type>ClutterActor</type> 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);
|
||||||
|
</programlisting>
|
||||||
|
</informalexample>
|
||||||
|
|
||||||
|
<para>Finally, start the animation:</para>
|
||||||
|
|
||||||
|
<informalexample>
|
||||||
|
<programlisting>
|
||||||
|
clutter_animator_start (animator);
|
||||||
|
</programlisting>
|
||||||
|
</informalexample>
|
||||||
|
|
||||||
|
<para>See <link linkend="animations-looping-example-2">the full
|
||||||
|
example</link> for more details.</para>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<title>Solution 3: looping with <type>ClutterState</type></title>
|
<title>Solution 3: looping with <type>ClutterState</type></title>
|
||||||
|
|
||||||
<para>you could use an looping timeline to cycle a ClutterState;
|
|
||||||
but you don't need to because ClutterState has implicit timelines you
|
|
||||||
don't need to interact with directly (for this case)???</para>
|
|
||||||
|
|
||||||
<para>You can loop <type>ClutterState</type> animations by
|
<para>You can loop <type>ClutterState</type> animations by
|
||||||
creating a cycle of states which
|
creating a cycle of states which
|
||||||
<ulink url="http://en.wikipedia.org/wiki/Ouroboros">"swallows
|
<ulink url="http://en.wikipedia.org/wiki/Ouroboros">"swallows
|
||||||
its own tail"</ulink>: i.e. goes from a start state, through
|
its own tail"</ulink>: i.e. goes from a start state, through
|
||||||
intermediate state(s), back to the start state, then again
|
intermediate state(s), back to the start state, then again
|
||||||
through the intermediate states(s), back to the start state,
|
through the intermediate states(s), back to the start state,
|
||||||
ad infinitum.</para>
|
etc., ad infinitum.</para>
|
||||||
|
|
||||||
|
<para>For the animation we're implementing, there are two states
|
||||||
|
the actor transitions between:</para>
|
||||||
|
|
||||||
|
<orderedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>The actor's <varname>x</varname> value
|
||||||
|
is <code>150.0</code> (the start/end state, on the right
|
||||||
|
of the stage).</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>The actor's <varname>x</varname> value is
|
||||||
|
<code>50.0</code> (the intermediate state, on the left
|
||||||
|
of the stage).</para>
|
||||||
|
</listitem>
|
||||||
|
</orderedlist>
|
||||||
|
|
||||||
|
<para>Here is how to add those states to a
|
||||||
|
<type>ClutterState</type> instance:</para>
|
||||||
|
|
||||||
|
<informalexample>
|
||||||
|
<programlisting>
|
||||||
|
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);
|
||||||
|
</programlisting>
|
||||||
|
</informalexample>
|
||||||
|
|
||||||
|
<para>You also need a handler to move the <type>ClutterState</type>
|
||||||
|
to its next state, called each time a state transition
|
||||||
|
is completed:</para>
|
||||||
|
|
||||||
|
<informalexample>
|
||||||
|
<programlisting>
|
||||||
|
/* handler to move the <type>ClutterState</type> 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");
|
||||||
|
}
|
||||||
|
</programlisting>
|
||||||
|
</informalexample>
|
||||||
|
|
||||||
|
<para>Then connect the <type>ClutterState's</type>
|
||||||
|
<code>completed</code> signal to the handler, so that each time
|
||||||
|
a state is reached, the transition to the next state begins:</para>
|
||||||
|
|
||||||
|
<informalexample>
|
||||||
|
<programlisting>
|
||||||
|
/* connect the <type>ClutterState</type> <code>completed</code> signal to the handler */
|
||||||
|
g_signal_connect (transitions,
|
||||||
|
"completed",
|
||||||
|
G_CALLBACK (next_state),
|
||||||
|
NULL);
|
||||||
|
</programlisting>
|
||||||
|
</informalexample>
|
||||||
|
|
||||||
|
<para>Finally, put the <type>ClutterState</type> into the start
|
||||||
|
state to begin the animation:</para>
|
||||||
|
|
||||||
|
<informalexample>
|
||||||
|
<programlisting>
|
||||||
|
clutter_state_warp_to_state (transitions, "right");
|
||||||
|
</programlisting>
|
||||||
|
</informalexample>
|
||||||
|
|
||||||
|
<para>See <link linkend="animations-looping-example-3">the full
|
||||||
|
example</link> for more details.</para>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
@ -2398,7 +2551,122 @@ clutter_animator_start (animator);
|
|||||||
<section>
|
<section>
|
||||||
<title>Discussion</title>
|
<title>Discussion</title>
|
||||||
|
|
||||||
<para>interrupting a looped animation???</para>
|
<para>We use two different approaches to looping in the solutions:</para>
|
||||||
|
|
||||||
|
<orderedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>Setting the <type>ClutterTimeline</type> to loop
|
||||||
|
(via <function>clutter_timeline_set_loop()</function>). This
|
||||||
|
is the best approach where the timeline is explicit (for
|
||||||
|
<type>ClutterAnimator</type> and implicit animations).</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>Cycling through states in a <type>ClutterState</type>. 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.</para>
|
||||||
|
</listitem>
|
||||||
|
</orderedlist>
|
||||||
|
|
||||||
|
<para>The following sections cover some other aspects of looping
|
||||||
|
animations.</para>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<title>Looping a fixed number of times</title>
|
||||||
|
|
||||||
|
<para><type>ClutterTimeline</type> 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.</para>
|
||||||
|
|
||||||
|
<para>For example, you could use a static counter to keep track
|
||||||
|
of the iteration count:</para>
|
||||||
|
|
||||||
|
<informalexample>
|
||||||
|
<programlisting>
|
||||||
|
static guint counter = 0;
|
||||||
|
</programlisting>
|
||||||
|
</informalexample>
|
||||||
|
|
||||||
|
<para>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
|
||||||
|
<type>ClutterAnimator</type> solution, you would connect the
|
||||||
|
<code>completed</code> signal of the timeline
|
||||||
|
to a callback function:</para>
|
||||||
|
|
||||||
|
<informalexample>
|
||||||
|
<programlisting>
|
||||||
|
g_signal_connect (timeline,
|
||||||
|
"completed",
|
||||||
|
G_CALLBACK (timeline_completed_cb),
|
||||||
|
NULL);
|
||||||
|
</programlisting>
|
||||||
|
</informalexample>
|
||||||
|
|
||||||
|
<para>And implement a callback function which resets the counter and
|
||||||
|
stops the timeline if more than two iterations have been counted:</para>
|
||||||
|
|
||||||
|
<informalexample>
|
||||||
|
<programlisting>
|
||||||
|
static void
|
||||||
|
timeline_completed_cb (ClutterTimeline *timeline,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
counter++;
|
||||||
|
|
||||||
|
if (counter > 2)
|
||||||
|
{
|
||||||
|
counter = 0;
|
||||||
|
clutter_timeline_stop (timeline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</programlisting>
|
||||||
|
</informalexample>
|
||||||
|
|
||||||
|
<para>Note that it's simple to count iterations and
|
||||||
|
control the timeline using <type>ClutterAnimator</type> or
|
||||||
|
<type>ClutterState</type>, 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.</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="animations-looping-discussion-closed-loop">
|
||||||
|
<title>Creating a "closed loop" with an implicit animation</title>
|
||||||
|
|
||||||
|
<para>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
|
||||||
|
<link linkend="animations-looping-solutions-implicit">earlier
|
||||||
|
solution</link>, the actor's initial <varname>x</varname> value was
|
||||||
|
<code>150.0</code>; so the default behaviour on each iteration
|
||||||
|
of the loop would be to animate the actor to <code>x = 50.0</code>
|
||||||
|
then jump it immediately back to <code>x = 150.0</code>, before
|
||||||
|
continuing the loop.</para>
|
||||||
|
|
||||||
|
<para>To prevent this happening, you can create a "closed" loop:
|
||||||
|
animate the actor's properties away from their initial values, then
|
||||||
|
back again.</para>
|
||||||
|
|
||||||
|
<para>This could be done manually, by creating two separate
|
||||||
|
animations, one the inverse of the other, and chaining them together.</para>
|
||||||
|
|
||||||
|
<para>However, a simpler solution is to run forward through the timeline
|
||||||
|
once; then invert its direction when the end of timeline is reached.
|
||||||
|
The animation continues, but in reverse. Once the backward iteration
|
||||||
|
completes, set the timeline to run forward again. Keep changing the
|
||||||
|
timeline's direction each time it completes. This
|
||||||
|
is the approach used in <link linkend="animations-looping-example-1">the
|
||||||
|
example</link>, which results in a smooth, repeated right to left,
|
||||||
|
left to right motion.</para>
|
||||||
|
|
||||||
|
<para>See <link linkend="animations-inversion">this
|
||||||
|
recipe</link> for more details about inverting a timeline.</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section id="animations-looping-examples">
|
<section id="animations-looping-examples">
|
||||||
|
Loading…
Reference in New Issue
Block a user