Merge remote branch 'elliot/cookbook-animations-looping'
* elliot/cookbook-animations-looping: cookbook: Recipe for "looping animations" cookbook: Clarify how signals are emitted during looped animation cookbook: First draft for looping animations recipe cookbook: Recipe skeleton for "looping animations" cookbook: Looping animation examples
This commit is contained in:
commit
9f2c31547c
@ -64,6 +64,7 @@ VIDEO_FILES = \
|
|||||||
videos/animations-reuse.ogv \
|
videos/animations-reuse.ogv \
|
||||||
videos/animations-moving-anchors.ogv \
|
videos/animations-moving-anchors.ogv \
|
||||||
videos/animations-moving-depth.ogv \
|
videos/animations-moving-depth.ogv \
|
||||||
|
videos/animations-looping.ogv \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
|
@ -2305,4 +2305,402 @@ clutter_animator_start (animator);
|
|||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section id="animations-looping">
|
||||||
|
<title>Looping an animation</title>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<title>Problem</title>
|
||||||
|
|
||||||
|
<para>You want to loop an animation so it plays multiple times.</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<title>Solutions</title>
|
||||||
|
|
||||||
|
<para>Each <link linkend="animations-introduction-api">animation
|
||||||
|
approach</link> can be used to create a looping animation, as
|
||||||
|
described in the following sections.</para>
|
||||||
|
|
||||||
|
<para>The animation implemented in each case is a simple repeated
|
||||||
|
movement of a rectangle from the right (<code>x = 150.0</code>)
|
||||||
|
to the left (<code>x = 50.0</code>) of the stage, and back again,
|
||||||
|
looped; like this (just a few iterations):</para>
|
||||||
|
|
||||||
|
<inlinemediaobject>
|
||||||
|
<videoobject>
|
||||||
|
<videodata fileref="videos/animations-looping.ogv"/>
|
||||||
|
</videoobject>
|
||||||
|
<alt>
|
||||||
|
<para>Video showing simple looped movement of an actor</para>
|
||||||
|
</alt>
|
||||||
|
</inlinemediaobject>
|
||||||
|
|
||||||
|
<section id="animations-looping-solutions-implicit">
|
||||||
|
<title>Solution 1: looping an implicit animation</title>
|
||||||
|
|
||||||
|
<para>Implicit animations, started using
|
||||||
|
<function>clutter_actor_animate()</function>, can be looped via
|
||||||
|
their associated <type>ClutterTimeline</type>.</para>
|
||||||
|
|
||||||
|
<para>Create a <type>ClutterTimeline</type> which is
|
||||||
|
set to loop:</para>
|
||||||
|
|
||||||
|
<informalexample>
|
||||||
|
<programlisting>
|
||||||
|
ClutterTimeline *timeline = clutter_timeline_new (1000);
|
||||||
|
clutter_timeline_set_loop (timeline, TRUE);
|
||||||
|
</programlisting>
|
||||||
|
</informalexample>
|
||||||
|
|
||||||
|
<para>Use this timeline when starting an implicit animation on an
|
||||||
|
actor; in this case, to animate the actor's <varname>x</varname>
|
||||||
|
coordinate from its initial value to <code>50.0</code>:</para>
|
||||||
|
|
||||||
|
<informalexample>
|
||||||
|
<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>
|
||||||
|
</informalexample>
|
||||||
|
|
||||||
|
<para>One further technique is to swop the timeline's
|
||||||
|
direction to create a "closed loop" animation (one which returns
|
||||||
|
to its origin at the end of each iteration). See
|
||||||
|
<link linkend="animations-looping-discussion-closed-loop">this
|
||||||
|
section</link> for details.</para>
|
||||||
|
|
||||||
|
<para><link linkend="animations-looping-example-1">The full
|
||||||
|
code example</link> shows how to run an implicit animation on
|
||||||
|
a loop.</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<title>Solution 2: looping with <type>ClutterAnimator</type></title>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<title>Solution 3: looping with <type>ClutterState</type></title>
|
||||||
|
|
||||||
|
<para>You can loop <type>ClutterState</type> animations by
|
||||||
|
creating a cycle of states which
|
||||||
|
<ulink url="http://en.wikipedia.org/wiki/Ouroboros">"swallows
|
||||||
|
its own tail"</ulink>: 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.</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>
|
||||||
|
<title>Discussion</title>
|
||||||
|
|
||||||
|
<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 id="animations-looping-examples">
|
||||||
|
<title>Full examples</title>
|
||||||
|
|
||||||
|
<example id="animations-looping-example-1">
|
||||||
|
<title>Looping an implicit animation</title>
|
||||||
|
<programlisting>
|
||||||
|
<xi:include href="examples/animations-looping-implicit.c" parse="text">
|
||||||
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
||||||
|
</xi:include>
|
||||||
|
</programlisting>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<example id="animations-looping-example-2">
|
||||||
|
<title>Looping with <type>ClutterAnimator</type></title>
|
||||||
|
<programlisting>
|
||||||
|
<xi:include href="examples/animations-looping-animator.c" parse="text">
|
||||||
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
||||||
|
</xi:include>
|
||||||
|
</programlisting>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<example id="animations-looping-example-3">
|
||||||
|
<title>Looping with <type>ClutterState</type></title>
|
||||||
|
<programlisting>
|
||||||
|
<xi:include href="examples/animations-looping-state.c" parse="text">
|
||||||
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
||||||
|
</xi:include>
|
||||||
|
</programlisting>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
</chapter>
|
</chapter>
|
||||||
|
@ -4,6 +4,9 @@ NULL =
|
|||||||
|
|
||||||
noinst_PROGRAMS = \
|
noinst_PROGRAMS = \
|
||||||
animations-complex \
|
animations-complex \
|
||||||
|
animations-looping-animator \
|
||||||
|
animations-looping-implicit \
|
||||||
|
animations-looping-state \
|
||||||
animations-moving-animator \
|
animations-moving-animator \
|
||||||
animations-moving-implicit \
|
animations-moving-implicit \
|
||||||
animations-moving-state \
|
animations-moving-state \
|
||||||
@ -55,6 +58,9 @@ AM_CFLAGS = \
|
|||||||
AM_LDFLAGS = $(CLUTTER_LIBS) -export-dynamic
|
AM_LDFLAGS = $(CLUTTER_LIBS) -export-dynamic
|
||||||
|
|
||||||
animations_complex_SOURCES = animations-complex.c
|
animations_complex_SOURCES = animations-complex.c
|
||||||
|
animations_looping_animator_SOURCES = animations-looping-animator.c
|
||||||
|
animations_looping_implicit_SOURCES = animations-looping-implicit.c
|
||||||
|
animations_looping_state_SOURCES = animations-looping-state.c
|
||||||
animations_moving_animator_SOURCES = animations-moving-animator.c
|
animations_moving_animator_SOURCES = animations-moving-animator.c
|
||||||
animations_moving_implicit_SOURCES = animations-moving-implicit.c
|
animations_moving_implicit_SOURCES = animations-moving-implicit.c
|
||||||
animations_moving_state_SOURCES = animations-moving-state.c
|
animations_moving_state_SOURCES = animations-moving-state.c
|
||||||
|
66
doc/cookbook/examples/animations-looping-animator.c
Normal file
66
doc/cookbook/examples/animations-looping-animator.c
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <clutter/clutter.h>
|
||||||
|
|
||||||
|
static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff };
|
||||||
|
static const ClutterColor red_color = { 0xff, 0x00, 0x00, 0xff };
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
key_pressed_cb (ClutterActor *actor,
|
||||||
|
ClutterEvent *event,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
ClutterTimeline *timeline = CLUTTER_TIMELINE (user_data);
|
||||||
|
|
||||||
|
if (!clutter_timeline_is_playing (timeline))
|
||||||
|
clutter_timeline_start (timeline);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc,
|
||||||
|
char *argv[])
|
||||||
|
{
|
||||||
|
ClutterActor *stage;
|
||||||
|
ClutterActor *actor;
|
||||||
|
ClutterTimeline *timeline;
|
||||||
|
ClutterAnimator *animator;
|
||||||
|
|
||||||
|
clutter_init (&argc, &argv);
|
||||||
|
|
||||||
|
stage = clutter_stage_get_default ();
|
||||||
|
clutter_actor_set_size (stage, 300, 200);
|
||||||
|
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
|
||||||
|
g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
|
||||||
|
|
||||||
|
actor = clutter_rectangle_new_with_color (&red_color);
|
||||||
|
clutter_actor_set_size (actor, 100, 100);
|
||||||
|
clutter_actor_set_position (actor, 150, 50);
|
||||||
|
|
||||||
|
timeline = clutter_timeline_new (2000);
|
||||||
|
clutter_timeline_set_loop (timeline, TRUE);
|
||||||
|
|
||||||
|
animator = clutter_animator_new ();
|
||||||
|
clutter_animator_set_timeline (animator, timeline);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor);
|
||||||
|
|
||||||
|
g_signal_connect (stage,
|
||||||
|
"key-press-event",
|
||||||
|
G_CALLBACK (key_pressed_cb),
|
||||||
|
timeline);
|
||||||
|
|
||||||
|
clutter_actor_show (stage);
|
||||||
|
|
||||||
|
clutter_main ();
|
||||||
|
|
||||||
|
g_object_unref (animator);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
91
doc/cookbook/examples/animations-looping-implicit.c
Normal file
91
doc/cookbook/examples/animations-looping-implicit.c
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <clutter/clutter.h>
|
||||||
|
|
||||||
|
static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff };
|
||||||
|
static const ClutterColor red_color = { 0xff, 0x00, 0x00, 0xff };
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
ClutterActor *actor;
|
||||||
|
ClutterTimeline *timeline;
|
||||||
|
} State;
|
||||||
|
|
||||||
|
static void
|
||||||
|
invert_timeline_cb (ClutterTimeline *timeline,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
ClutterTimelineDirection direction = clutter_timeline_get_direction (timeline);
|
||||||
|
|
||||||
|
if (direction == CLUTTER_TIMELINE_FORWARD)
|
||||||
|
direction = CLUTTER_TIMELINE_BACKWARD;
|
||||||
|
else
|
||||||
|
direction = CLUTTER_TIMELINE_FORWARD;
|
||||||
|
|
||||||
|
clutter_timeline_set_direction (timeline, direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
key_pressed_cb (ClutterActor *actor,
|
||||||
|
ClutterEvent *event,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
State *state = (State *) user_data;
|
||||||
|
|
||||||
|
/* only start animating if actor isn't animating already */
|
||||||
|
if (clutter_actor_get_animation (state->actor) == NULL)
|
||||||
|
clutter_actor_animate_with_timeline (state->actor,
|
||||||
|
CLUTTER_LINEAR,
|
||||||
|
state->timeline,
|
||||||
|
"x", 50.0,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc,
|
||||||
|
char *argv[])
|
||||||
|
{
|
||||||
|
State *state = g_new0 (State, 1);
|
||||||
|
|
||||||
|
ClutterActor *stage;
|
||||||
|
|
||||||
|
clutter_init (&argc, &argv);
|
||||||
|
|
||||||
|
stage = clutter_stage_get_default ();
|
||||||
|
clutter_actor_set_size (stage, 300, 200);
|
||||||
|
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
|
||||||
|
g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
|
||||||
|
|
||||||
|
state->actor = clutter_rectangle_new_with_color (&red_color);
|
||||||
|
clutter_actor_set_size (state->actor, 100, 100);
|
||||||
|
clutter_actor_set_position (state->actor, 150, 50);
|
||||||
|
|
||||||
|
state->timeline = clutter_timeline_new (1000);
|
||||||
|
clutter_timeline_set_loop (state->timeline, TRUE);
|
||||||
|
|
||||||
|
g_signal_connect (stage,
|
||||||
|
"key-press-event",
|
||||||
|
G_CALLBACK (key_pressed_cb),
|
||||||
|
state);
|
||||||
|
|
||||||
|
/* the animation will not emit a "completed" signal,
|
||||||
|
* as it is set to loop; but the timeline emits "completed"
|
||||||
|
* at the end of each iteration of the loop
|
||||||
|
*/
|
||||||
|
g_signal_connect (state->timeline,
|
||||||
|
"completed",
|
||||||
|
G_CALLBACK (invert_timeline_cb),
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
clutter_container_add_actor (CLUTTER_CONTAINER (stage), state->actor);
|
||||||
|
|
||||||
|
clutter_actor_show (stage);
|
||||||
|
|
||||||
|
clutter_main ();
|
||||||
|
|
||||||
|
g_object_unref (state->timeline);
|
||||||
|
g_free (state);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
83
doc/cookbook/examples/animations-looping-state.c
Normal file
83
doc/cookbook/examples/animations-looping-state.c
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <clutter/clutter.h>
|
||||||
|
|
||||||
|
static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff };
|
||||||
|
static const ClutterColor red_color = { 0xff, 0x00, 0x00, 0xff };
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
key_pressed_cb (ClutterActor *actor,
|
||||||
|
ClutterEvent *event,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
ClutterState *transitions = CLUTTER_STATE (user_data);
|
||||||
|
|
||||||
|
if (!clutter_timeline_is_playing (clutter_state_get_timeline (transitions)))
|
||||||
|
next_state (transitions, NULL);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc,
|
||||||
|
char *argv[])
|
||||||
|
{
|
||||||
|
ClutterActor *stage;
|
||||||
|
ClutterActor *actor;
|
||||||
|
ClutterState *transitions;
|
||||||
|
|
||||||
|
clutter_init (&argc, &argv);
|
||||||
|
|
||||||
|
stage = clutter_stage_get_default ();
|
||||||
|
clutter_actor_set_size (stage, 300, 200);
|
||||||
|
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
|
||||||
|
g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
|
||||||
|
|
||||||
|
actor = clutter_rectangle_new_with_color (&red_color);
|
||||||
|
clutter_actor_set_position (actor, 150, 50);
|
||||||
|
clutter_actor_set_size (actor, 100, 100);
|
||||||
|
|
||||||
|
transitions = clutter_state_new ();
|
||||||
|
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);
|
||||||
|
|
||||||
|
clutter_state_warp_to_state (transitions, "right");
|
||||||
|
|
||||||
|
g_signal_connect (stage,
|
||||||
|
"key-press-event",
|
||||||
|
G_CALLBACK (key_pressed_cb),
|
||||||
|
transitions);
|
||||||
|
|
||||||
|
g_signal_connect (transitions,
|
||||||
|
"completed",
|
||||||
|
G_CALLBACK (next_state),
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor);
|
||||||
|
|
||||||
|
clutter_actor_show (stage);
|
||||||
|
|
||||||
|
clutter_main ();
|
||||||
|
|
||||||
|
g_object_unref (transitions);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
BIN
doc/cookbook/videos/animations-looping.ogv
Normal file
BIN
doc/cookbook/videos/animations-looping.ogv
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user