2010-07-01 10:29:46 +00:00
|
|
|
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
|
|
|
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
|
|
|
|
2010-07-21 13:50:32 +00:00
|
|
|
<chapter id="animations" xmlns:xi="http://www.w3.org/2003/XInclude">
|
2010-07-01 10:29:46 +00:00
|
|
|
<title>Animations</title>
|
|
|
|
|
|
|
|
<epigraph>
|
2010-07-12 16:15:16 +00:00
|
|
|
<attribution>Walt Disney</attribution>
|
|
|
|
<para>Animation can explain whatever the mind of man can conceive.</para>
|
2010-07-01 10:29:46 +00:00
|
|
|
</epigraph>
|
|
|
|
|
|
|
|
<section id="animations-introduction">
|
|
|
|
<title>Introduction</title>
|
|
|
|
|
2010-07-12 16:15:16 +00:00
|
|
|
<para>Clutter actors have a variety of <emphasis>properties</emphasis>
|
|
|
|
(position, size, rotation in 3D space, scale, opacity) which govern
|
|
|
|
their visual appearance in the UI. They may also have
|
|
|
|
<emphasis>constraints</emphasis> on how they are aligned
|
|
|
|
and/or positioned relative to each other.</para>
|
|
|
|
|
|
|
|
<para>The Clutter animation API provides a means of changing
|
|
|
|
properties and constraints as a function of time: moving, scaling,
|
|
|
|
rotating, changing opacity and colour, modifying postional
|
|
|
|
constraints, etc.</para>
|
|
|
|
|
|
|
|
<note><para>Clutter also makes it possible to animate non-visual
|
|
|
|
properties if desired.</para></note>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>High level overview</title>
|
|
|
|
|
|
|
|
<para>Here are the main concepts behind animation in Clutter:</para>
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>
|
|
|
|
<para>An <emphasis>animation</emphasis> changes one or more
|
|
|
|
properties of one or more actors over time: their rotation in
|
|
|
|
a particular dimension (<varname>x</varname>, <varname>y</varname>,
|
|
|
|
<varname>z</varname>), scale, size, opacity etc.</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>An animation has an associated <emphasis>timeline</emphasis>.
|
|
|
|
Think of this as analogous to the "thing" you're controlling when
|
|
|
|
you watch a video on the internet: it's what you control with
|
|
|
|
the play/pause button and what is measured by the bar
|
|
|
|
showing how far through the video you are. As with the
|
|
|
|
controls on a video player, you can play/pause/skip a Clutter
|
|
|
|
timeline; you can also rewind it, loop it, and play it
|
|
|
|
backwards.</para>
|
|
|
|
<note>
|
|
|
|
<para>If a timeline is reversed, the progress along the
|
|
|
|
timeline is still measured the same way as it is in the forward
|
|
|
|
direction: so if you start from the end of the timeline and run
|
|
|
|
it backwards for 75% of its length, the progress is reported
|
|
|
|
as 0.25 (i.e. 25% of the way from the start of the
|
|
|
|
timeline).</para>
|
|
|
|
</note>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>The <emphasis>duration</emphasis> of a timeline
|
|
|
|
(e.g. 500 milliseconds, 1 second, 10 seconds) specifies how
|
|
|
|
long its animation will last. The timeline can be inspected
|
|
|
|
to find out how much of it has elapsed, either as a value in
|
|
|
|
milliseconds or as a fraction (between 0 and 1) of the total
|
|
|
|
length of the timeline.</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>An animation is divided into <emphasis>frames</emphasis>.
|
|
|
|
The number of frames which make up the animation isn't
|
|
|
|
constant: it depends on various factors, like how powerful
|
|
|
|
your machine is, the state of the drivers for your hardware,
|
|
|
|
and the load on he system. So you won't always get the same
|
|
|
|
number of frames in an animation of a particular duration.</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>The change to a property in an animation occurs over
|
|
|
|
the course of the timeline: the start value of the property
|
|
|
|
heads toward some target value. When it reaches the end of
|
|
|
|
the timeline, the property should have reached the target
|
|
|
|
value.</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>Exactly how the property changes over the course of the
|
|
|
|
timeline is governed by an <emphasis>alpha</emphasis>. This
|
|
|
|
is the trickiest idea to explain, so it has its own section
|
|
|
|
below.</para>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
</section>
|
|
|
|
|
2010-07-16 11:34:44 +00:00
|
|
|
<section id="animations-introduction-alphas">
|
2010-07-12 16:15:16 +00:00
|
|
|
<title>Alphas</title>
|
|
|
|
|
|
|
|
<para>An alpha is generated for each frame of the animation.
|
|
|
|
The alpha varies between -1.0 and 2.0, and changes during the
|
|
|
|
course of the animation's timeline; ideally, the value should
|
|
|
|
start at 0.0 and reach 1.0 by the end of the timeline.</para>
|
|
|
|
|
|
|
|
<para>The alpha for any given frame of the animation is determined
|
|
|
|
by an <emphasis>alpha function</emphasis>. Usually, the alpha
|
|
|
|
function will return a value based on progress along the timeline.
|
|
|
|
However, the alpha function doesn't have to respect or pay
|
|
|
|
attention to the timeline: it can be entirely random if desired.</para>
|
|
|
|
|
|
|
|
<para>To work out the value of a property at a given frame
|
|
|
|
somewhere along the timeline for a given alpha:</para>
|
|
|
|
|
|
|
|
<orderedlist>
|
|
|
|
<listitem>
|
|
|
|
<para>Determine the difference between the start value and
|
|
|
|
the target end value for the property.</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>Multiply the difference by the alpha for the current
|
|
|
|
frame.</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>Add the result to the start value.</para>
|
|
|
|
</listitem>
|
|
|
|
</orderedlist>
|
|
|
|
|
|
|
|
<para>The shape of the plot of the alpha function over time is
|
|
|
|
called its <emphasis>easing mode</emphasis>. Clutter provides
|
|
|
|
various modes ranging from <constant>CLUTTER_LINEAR</constant>
|
|
|
|
(the alpha value is equal to progress along the timeline),
|
|
|
|
to modes based on various polynomial and exponential functions,
|
|
|
|
to modes providing elastic and bounce shapes. See the
|
|
|
|
ClutterAlpha documentation for examples of the shapes produced
|
|
|
|
by these functions. There is also a good interactive demo
|
|
|
|
of the modes on
|
|
|
|
<ulink url="http://www.robertpenner.com/easing/easing_demo.html">Robert Penner's site</ulink>.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>Most of the time, you can use the built-in Clutter easing
|
|
|
|
modes to get the kind of animation effect you want. However,
|
|
|
|
in some cases you may want to provide your own alpha function.
|
|
|
|
Here's an example (based on the quintic ease in mode from
|
|
|
|
<filename>clutter-alpha.c</filename>):</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
static gdouble
|
|
|
|
_alpha_ease_in_sextic (ClutterAlpha *alpha,
|
|
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
|
|
{
|
|
|
|
ClutterTimeline *timeline = clutter_alpha_get_timeline (alpha);
|
|
|
|
gdouble p = clutter_timeline_get_progress (timeline);
|
|
|
|
|
|
|
|
return p * p * p * p * p * p;
|
|
|
|
}
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>An alpha function just has to have a specified method
|
|
|
|
signature and return a <type>gdouble</type> value when called.
|
|
|
|
As stated above, you'd typically base the return value on the
|
|
|
|
timeline progress; the function above shows how you get the
|
|
|
|
timeline associated with the alpha, so you can apply the alpha
|
|
|
|
function to it.</para>
|
|
|
|
</section>
|
|
|
|
|
2010-07-21 13:50:32 +00:00
|
|
|
<section id="animations-introduction-api">
|
2010-07-12 16:15:16 +00:00
|
|
|
<title>Clutter's animation API</title>
|
|
|
|
|
|
|
|
<para>All of the animation approaches in Clutter use the same
|
|
|
|
basic underpinnings (as explained above), but the API provides
|
|
|
|
varying levels of abstraction and/or ease of use on top of those
|
|
|
|
underpinnings.</para>
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>
|
|
|
|
<para><emphasis>Implicit animations</emphasis> (created using
|
|
|
|
<function>clutter_actor_animate()</function> and related
|
|
|
|
functions) are useful where you want to apply
|
|
|
|
a simple or one-off animation to an actor. They enable you
|
|
|
|
to animate one or more properties using a single easing mode;
|
|
|
|
however, you only specify the target values for the properties
|
|
|
|
you're animating, not the start values.</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para><emphasis>ClutterAnimator</emphasis> provides support
|
|
|
|
for declarative animations (defined using <type>ClutterScript</type>).
|
|
|
|
You can animate multiple actors with this approach, and
|
|
|
|
have more control over the easing modes used during an
|
|
|
|
animation: while implicit animations only allow a single
|
|
|
|
easing mode for all properties, <type>ClutterAnimator</type>
|
|
|
|
supports <emphasis>multiple</emphasis> easing modes for
|
|
|
|
<emphasis>each</emphasis> property; <emphasis>key frames</emphasis>
|
|
|
|
are used to indicate where in the animation each easing mode
|
|
|
|
should be applied.</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para><emphasis>ClutterState</emphasis> enables you to describe
|
|
|
|
<emphasis>states</emphasis>: property values across one or
|
|
|
|
more actors, plus the easing modes used to transition to
|
|
|
|
those values. It can also be combined with <type>ClutterAnimator</type>
|
|
|
|
for finer grained definition of transitions if desired.</para>
|
|
|
|
<para>States are particularly useful if you need actors to
|
|
|
|
animate between a known set of positions/sizes/opacities etc.
|
|
|
|
during their lifecycles (e.g. animating a list of items in
|
|
|
|
a menu, or for animations in a picture viewer where you
|
|
|
|
click on thumbnails to display a full view of a photograph).</para>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
<para>The recipes in this section show when and where it is
|
|
|
|
appropriate to use each of these approaches.</para>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
2010-07-01 10:29:46 +00:00
|
|
|
</section>
|
|
|
|
|
2010-07-12 14:34:23 +00:00
|
|
|
<section id="animations-inversion">
|
2010-07-01 10:29:46 +00:00
|
|
|
<title>Inverting Animations</title>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Problem</title>
|
|
|
|
|
|
|
|
<para>You want to have an animation exactly mirroring another one
|
|
|
|
that you just played.</para>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Solution</title>
|
|
|
|
|
2010-07-12 14:34:23 +00:00
|
|
|
<para>Reverse the direction of the <type>ClutterTimeline</type>
|
|
|
|
associated with the animation.</para>
|
|
|
|
|
|
|
|
<para>For example, here's how to invert an implicit
|
|
|
|
animation which moves an actor along the <varname>x</varname>
|
|
|
|
axis. The direction of the animation is inverted when the
|
|
|
|
movement along the <varname>x</varname> axis is completed; it is
|
|
|
|
also inverted if the mouse button is pressed on the actor.</para>
|
|
|
|
|
|
|
|
<para>First, set up the animation:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
ClutterAnimation *animation;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* animate actor to x = 300.0;
|
|
|
|
* the implicit animation functions return a ClutterAnimation
|
|
|
|
* which we can use to invert the timeline
|
|
|
|
*/
|
|
|
|
animation = clutter_actor_animate (actor,
|
|
|
|
CLUTTER_EASE_IN_OUT_CUBIC,
|
|
|
|
2000,
|
|
|
|
"x", 300.0,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
/* callback for when the animation completes */
|
|
|
|
g_signal_connect (animation,
|
|
|
|
"completed",
|
|
|
|
G_CALLBACK (_animation_done_cb),
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* callback for when the mouse button is pressed on the actor;
|
|
|
|
* note the animation is passed as user data, so we can
|
|
|
|
* get at the timeline
|
|
|
|
*/
|
|
|
|
g_signal_connect (actor,
|
|
|
|
"button-press-event",
|
|
|
|
G_CALLBACK (_on_click_cb),
|
|
|
|
animation);
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>Next, add a function for inverting the timeline:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
static void
|
|
|
|
_invert_timeline (ClutterTimeline *timeline)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>Then add a function which calls <function>_invert_timeline</function>
|
|
|
|
when the animation completes. More importantly, the callback should
|
|
|
|
stop emission of the "completed" signal by the animation. This
|
|
|
|
prevents the <type>ClutterAnimation</type> underlying the implicit
|
|
|
|
animation from being unreferenced; which in turn allows it to be
|
|
|
|
inverted:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
static void
|
|
|
|
_animation_done_cb (ClutterAnimation *animation,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
/* stop the completed signal before the ClutterAnimation is unreferenced */
|
|
|
|
g_signal_stop_emission_by_name (animation, "completed");
|
|
|
|
|
|
|
|
/* invert the timeline associated with the animation */
|
|
|
|
ClutterTimeline *timeline = clutter_animation_get_timeline (animation);
|
|
|
|
_invert_timeline (timeline);
|
|
|
|
}
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>Finally, the click callback function uses the same
|
|
|
|
<function>_invert_timeline</function> function if the animation
|
|
|
|
is playing; but if the animation is stopped, it will
|
|
|
|
start it instead:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
static void
|
|
|
|
_on_click_cb (ClutterActor *actor,
|
|
|
|
ClutterEvent *event,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
ClutterAnimation *animation = (ClutterAnimation *)user_data;
|
|
|
|
|
|
|
|
ClutterTimeline *timeline = clutter_animation_get_timeline (animation);
|
|
|
|
|
|
|
|
if (clutter_timeline_is_playing (timeline))
|
|
|
|
{
|
|
|
|
_invert_timeline (timeline);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
clutter_timeline_start (timeline);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
2010-07-01 10:29:46 +00:00
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Discussion</title>
|
|
|
|
|
2010-07-12 15:59:38 +00:00
|
|
|
<para>If you are using <type>ClutterAnimator</type> rather than
|
2010-07-12 14:34:23 +00:00
|
|
|
implicit animations, <function>clutter_animator_get_timeline()</function>
|
|
|
|
enables you to get the underlying timeline; you could then use
|
|
|
|
the techniques shown above to invert it.</para>
|
|
|
|
|
|
|
|
<para><type>ClutterState</type> enables a different approach
|
|
|
|
to "inverting" an animation: rather than having a single animation
|
|
|
|
which you invert, you would define two or more
|
|
|
|
<emphasis>keys</emphasis> for an actor (or set of actors) and
|
|
|
|
transition between them.</para>
|
|
|
|
|
|
|
|
<para>For the example above, you would define two keys:
|
|
|
|
one for the actor's initial position; and a second for the actor
|
|
|
|
at <code>x = 300.0</code>. You would also define the
|
|
|
|
transition between them: 2000 milliseconds with a
|
|
|
|
<constant>CLUTTER_EASE_IN_OUT_CUBIC</constant> easing mode.</para>
|
|
|
|
|
|
|
|
<para>With the states defined, you would then use
|
|
|
|
<function>clutter_state_set_state()</function> inside callbacks to
|
|
|
|
animate the actor between the two <varname>x</varname> positions.
|
|
|
|
Behind the scenes, <type>ClutterState</type> would handle the
|
|
|
|
animations and timelines for you.</para>
|
|
|
|
|
2010-07-01 10:29:46 +00:00
|
|
|
</section>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
2010-07-16 11:34:44 +00:00
|
|
|
<section id="animations-fading">
|
|
|
|
<title>Fading an actor out of or into view</title>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Problem</title>
|
|
|
|
|
|
|
|
<para>You want to animate an actor so that it fades out of or into
|
|
|
|
view.</para>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Solution</title>
|
|
|
|
|
|
|
|
<para>Animate the actor's opacity property.</para>
|
|
|
|
|
2010-07-16 11:48:56 +00:00
|
|
|
<para>You can do this using any of the approaches provided
|
2010-07-16 11:34:44 +00:00
|
|
|
by the animation API. Here's how to fade out an actor (until it's
|
|
|
|
completely transparent) using implicit animations:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
/* fade out actor over 4000 milliseconds */
|
|
|
|
clutter_actor_animate (actor,
|
|
|
|
CLUTTER_EASE_OUT_CUBIC,
|
|
|
|
4000,
|
|
|
|
"opacity", 0,
|
|
|
|
NULL);
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>Here's an example of a rectangle fading out using this
|
|
|
|
animation:</para>
|
|
|
|
|
|
|
|
<inlinemediaobject>
|
|
|
|
<videoobject>
|
|
|
|
<videodata fileref="videos/animations-fading-out.ogv"/>
|
|
|
|
</videoobject>
|
|
|
|
<alt>
|
|
|
|
<para>Video showing an actor fading out using implicit
|
|
|
|
animations</para>
|
|
|
|
</alt>
|
|
|
|
</inlinemediaobject>
|
|
|
|
|
|
|
|
<para><constant>CLUTTER_EASE_OUT_CUBIC</constant> is one of the
|
|
|
|
Clutter easing modes; see
|
|
|
|
<link linkend="animations-introduction-alphas">the introduction</link>
|
|
|
|
for more details about what these are and how to choose one.</para>
|
|
|
|
|
|
|
|
<para>Here's an example of the transitions you could use to
|
|
|
|
fade an actor in and out using <type>ClutterState</type>:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
ClutterState *transitions = clutter_state_new ();
|
|
|
|
|
|
|
|
/* all transitions last for 2000 milliseconds */
|
|
|
|
clutter_state_set_duration (transitions, NULL, NULL, 2000);
|
|
|
|
|
|
|
|
/* transition from any state to "fade-out" state */
|
|
|
|
clutter_state_set (transitions,
|
|
|
|
NULL, /* from state (NULL means "any") */
|
|
|
|
"fade-out", /* to state */
|
|
|
|
actor, "opacity", CLUTTER_EASE_OUT_QUAD, 0,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
/* transition from any state to "fade-in" state */
|
|
|
|
clutter_state_set (transitions, NULL, "fade-in",
|
|
|
|
actor, "opacity", CLUTTER_EASE_OUT_QUAD, 255,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
/* put the actor into the "fade-out" state with no animation */
|
|
|
|
clutter_state_warp_to_state (transitions, "fade-out");
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>You would then trigger an animated state change as events
|
|
|
|
occur in the application (e.g. mouse button clicks):</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
clutter_state_set_state (transitions, "fade-in");
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>Here's an example of this animation fading in then out again:</para>
|
|
|
|
|
|
|
|
<inlinemediaobject>
|
|
|
|
<videoobject>
|
|
|
|
<videodata fileref="videos/animations-fading-in-then-out.ogv"/>
|
|
|
|
</videoobject>
|
|
|
|
<alt>
|
|
|
|
<para>Video showing an actor fading in then out using
|
|
|
|
<type>ClutterState</type></para>
|
|
|
|
</alt>
|
|
|
|
</inlinemediaobject>
|
|
|
|
|
|
|
|
<note>
|
|
|
|
<para><type>ClutterState</type> is most useful where you
|
|
|
|
need to animate an actor backwards and forwards between multiple
|
|
|
|
states (e.g. fade an actor in and out of view). Where you just
|
|
|
|
want to fade an actor in or out once,
|
|
|
|
<function>clutter_actor_animate()</function> is adequate.</para>
|
|
|
|
</note>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Discussion</title>
|
|
|
|
|
|
|
|
<para>Reducing an actor's transparency to zero does not make it
|
|
|
|
inactive: the actor will still be reactive even if it's not
|
|
|
|
visible (responding to key events, mouse clicks etc.).
|
|
|
|
To make it really "disappear", you could use
|
|
|
|
<function>clutter_actor_hide()</function> once you'd made the actor
|
|
|
|
fully transparent.</para>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
2010-07-21 13:50:32 +00:00
|
|
|
<section id="animations-rotating">
|
|
|
|
<title>Rotating an actor</title>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Problem</title>
|
|
|
|
|
|
|
|
<para>You want to animate rotation of an actor. Some example cases
|
|
|
|
where you might want to do this:</para>
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>
|
|
|
|
<para>To rotate an image so it's the right way up for
|
|
|
|
viewing.</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>To make actors more or less prominent, rotating them
|
|
|
|
towards or away from the view point.</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>To turn an actor "around" and display different UI
|
|
|
|
elements "behind" it.</para>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Solution</title>
|
|
|
|
|
|
|
|
<para>Animate one of the <varname>rotation-angle-(x|y|z)</varname>
|
|
|
|
properties of the actor.</para>
|
|
|
|
|
|
|
|
<para>The most "obvious" (and probably most commonly used) rotation is
|
|
|
|
in the <emphasis>z axis</emphasis> (parallel
|
|
|
|
to the 2D surface of the UI). The other rotation axes
|
|
|
|
(<emphasis>x</emphasis> and <emphasis>y</emphasis>)
|
|
|
|
are less obvious, as they rotate the actor in the depth dimension,
|
|
|
|
"away from" or "towards" the view point.</para>
|
|
|
|
|
|
|
|
<para>Examples of each type of rotation are given below. While the
|
|
|
|
examples use <link linkend="animations-introduction-api">implicit
|
|
|
|
animations</link>, it is also possible to use
|
|
|
|
<type>ClutterAnimator</type> and <type>ClutterState</type> to animate
|
|
|
|
rotations: see <link linkend="animations-rotating-example">the
|
|
|
|
full example at the end of this recipe</link> for some
|
|
|
|
<type>ClutterState</type> code.</para>
|
|
|
|
|
|
|
|
<note>
|
|
|
|
<para>I've added an inaccurate (but hopefully useful) metaphor to
|
|
|
|
each rotation axis ("wheel", "letter box", "door"), to make it
|
|
|
|
easier to remember the effect you get from animating in that axis
|
|
|
|
(and when the rotation center is inside the actor).</para>
|
|
|
|
</note>
|
|
|
|
|
|
|
|
<para><emphasis>Rotating on the z axis</emphasis> ("wheel")</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
clutter_actor_animate (actor,
|
|
|
|
CLUTTER_LINEAR, /* easing mode */
|
|
|
|
1000, /* duration in milliseconds */
|
|
|
|
"rotation-angle-z", 90.0, /* target rotation angle in degrees */
|
|
|
|
NULL);
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>The above code animating a texture:</para>
|
|
|
|
|
|
|
|
<inlinemediaobject>
|
|
|
|
<videoobject>
|
|
|
|
<videodata fileref="videos/animations-rotating-z-90.ogv"/>
|
|
|
|
</videoobject>
|
|
|
|
<alt>
|
|
|
|
<para>Video showing an actor rotating to 90 degrees on the
|
|
|
|
z axis</para>
|
|
|
|
</alt>
|
|
|
|
</inlinemediaobject>
|
|
|
|
|
|
|
|
<para>By default, the center of the rotation is derived from
|
|
|
|
the anchor point of the actor; unless you've changed the anchor
|
|
|
|
point, the default is the top-left corner of the actor. See the
|
|
|
|
Discussion section below for more about setting the rotation center.</para>
|
|
|
|
|
|
|
|
<note>
|
|
|
|
<para>An animated rotation moves an actor <emphasis>to</emphasis>
|
|
|
|
the specified rotation angle; it <emphasis>does not</emphasis>
|
|
|
|
increment or decrement the actor's current rotation angle by
|
|
|
|
the amount specified.</para>
|
|
|
|
</note>
|
|
|
|
|
|
|
|
<para><emphasis>Rotating on the <code>x</code> axis</emphasis>
|
|
|
|
("letter box")</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
clutter_actor_animate (actor,
|
|
|
|
CLUTTER_LINEAR,
|
|
|
|
1000,
|
|
|
|
"rotation-angle-x", -45.0,
|
|
|
|
NULL);
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>The above code animating a texture:</para>
|
|
|
|
|
|
|
|
<inlinemediaobject>
|
|
|
|
<videoobject>
|
|
|
|
<videodata fileref="videos/animations-rotating-x-minus-45.ogv"/>
|
|
|
|
</videoobject>
|
|
|
|
<alt>
|
|
|
|
<para>Video showing an actor rotating to -45 degrees on the
|
|
|
|
x axis</para>
|
|
|
|
</alt>
|
|
|
|
</inlinemediaobject>
|
|
|
|
|
|
|
|
<para>Notice how the texture rotates away from the view point,
|
|
|
|
and also how perspective effects are applied (as the actor is rotating
|
|
|
|
"into" the depth dimension).</para>
|
|
|
|
|
|
|
|
<para><emphasis>Rotating on the <code>y</code> axis</emphasis>
|
|
|
|
("door")</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
clutter_actor_animate (actor,
|
|
|
|
CLUTTER_LINEAR,
|
|
|
|
1000,
|
|
|
|
"rotation-angle-y", 45.0,
|
|
|
|
NULL);
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>The above code animating a texture:</para>
|
|
|
|
|
|
|
|
<inlinemediaobject>
|
|
|
|
<videoobject>
|
|
|
|
<videodata fileref="videos/animations-rotating-y-45.ogv"/>
|
|
|
|
</videoobject>
|
|
|
|
<alt>
|
|
|
|
<para>Video showing an actor rotating to 45 degrees on the
|
|
|
|
y axis</para>
|
|
|
|
</alt>
|
|
|
|
</inlinemediaobject>
|
|
|
|
|
|
|
|
<para>Again, the rotation is into the depth dimension, so
|
|
|
|
you get perspective effects.</para>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Discussion</title>
|
|
|
|
|
|
|
|
<para>It can sometimes be difficult to predict exactly
|
|
|
|
how a particular rotation animation will appear when applied.
|
|
|
|
Often the only way to find out is to experiment. However,
|
|
|
|
the sections below outline some of the most common factors which
|
|
|
|
affect animated rotations, with the aim of minimising the
|
|
|
|
experimentation you need to do.</para>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Setting the rotation center for an animation</title>
|
|
|
|
|
|
|
|
<para>The examples in the previous section used the default
|
|
|
|
center of rotation for each axis. However, it is possible to
|
|
|
|
change the rotation center for an axis, in turn changing
|
|
|
|
the appearance of the animation.</para>
|
|
|
|
|
|
|
|
<note>
|
|
|
|
<para>Rotation center coordinates are relative to the
|
|
|
|
actor's coordinates, not to the coordinates of the actor's
|
|
|
|
container or the stage.</para>
|
|
|
|
</note>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Setting a rotation center inside an actor</title>
|
|
|
|
|
|
|
|
<para>You can set the center for rotation on the x or y axes
|
|
|
|
like this:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
/*
|
|
|
|
* only required for y axis rotation;
|
|
|
|
* here set to the mid point of the actor's y axis
|
|
|
|
*/
|
|
|
|
gfloat x_center = clutter_actor_get_height (actor) * 0.5;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* only required for x axis rotation;
|
|
|
|
* here set to the mid point of the actor's x axis
|
|
|
|
*/
|
|
|
|
gfloat y_center = clutter_actor_get_width (actor) * 0.5;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* depth for the rotation center: positive numbers
|
|
|
|
* are closer to the view point, negative ones
|
|
|
|
* are further away
|
|
|
|
*/
|
|
|
|
gfloat z_center = 0.0;
|
|
|
|
|
|
|
|
/* set rotation center */
|
|
|
|
clutter_actor_set_rotation (actor,
|
|
|
|
CLUTTER_X_AXIS, /* or CLUTTER_Y_AXIS */
|
|
|
|
0.0, /* set the rotation to this angle */
|
|
|
|
x_center,
|
|
|
|
y_center,
|
|
|
|
z_center);
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>Because z axis rotations are more common, Clutter
|
|
|
|
provides some convenience functions to set the rotation
|
|
|
|
center for this axis:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
clutter_actor_set_z_rotation_from_gravity (actor,
|
|
|
|
0.0,
|
|
|
|
CLUTTER_GRAVITY_CENTER);
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para><constant>CLUTTER_GRAVITY_CENTER</constant> makes the
|
|
|
|
center of the actor the rotation center for
|
|
|
|
the z axis. See the <type>ClutterGravity</type> enumeration for
|
|
|
|
acceptable values for this parameter.</para>
|
|
|
|
|
|
|
|
<note>
|
|
|
|
<para>Setting the rotation center for the z axis using gravity
|
|
|
|
is recommended, as Clutter will automatically recompute the
|
|
|
|
rotation center if the actor's size changes. For the x and y
|
|
|
|
axes, you have to do this computation yourself if you
|
|
|
|
want an actor's center of rotation to stay in the same place
|
|
|
|
if it is resized.</para>
|
|
|
|
</note>
|
|
|
|
|
|
|
|
<para>Rotation on the x axis around an actor's center:</para>
|
|
|
|
|
|
|
|
<inlinemediaobject>
|
|
|
|
<videoobject>
|
|
|
|
<videodata fileref="videos/animations-rotating-x-centered.ogv"/>
|
|
|
|
</videoobject>
|
|
|
|
<alt>
|
|
|
|
<para>Video showing an actor rotating around its center
|
|
|
|
on the x axis</para>
|
|
|
|
</alt>
|
|
|
|
</inlinemediaobject>
|
|
|
|
|
|
|
|
<para>Rotation on the y axis around an actor's center:</para>
|
|
|
|
|
|
|
|
<inlinemediaobject>
|
|
|
|
<videoobject>
|
|
|
|
<videodata fileref="videos/animations-rotating-y-centered.ogv"/>
|
|
|
|
</videoobject>
|
|
|
|
<alt>
|
|
|
|
<para>Video showing an actor rotating around its center
|
|
|
|
on the y axis</para>
|
|
|
|
</alt>
|
|
|
|
</inlinemediaobject>
|
|
|
|
|
|
|
|
<para>Rotation on the z axis around an actor's center:</para>
|
|
|
|
|
|
|
|
<inlinemediaobject>
|
|
|
|
<videoobject>
|
|
|
|
<videodata fileref="videos/animations-rotating-z-centered.ogv"/>
|
|
|
|
</videoobject>
|
|
|
|
<alt>
|
|
|
|
<para>Video showing an actor rotating around its center
|
|
|
|
on the z axis</para>
|
|
|
|
</alt>
|
|
|
|
</inlinemediaobject>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Setting the rotation center outside an actor</title>
|
|
|
|
|
|
|
|
<para>Rather than rotating the actor around a point inside
|
|
|
|
itself, the rotation center can be moved to a position
|
|
|
|
outside the actor. (In the case of the z axis,
|
|
|
|
any rotation center setting is outside the actor as its depth
|
|
|
|
is 0.) When animated, the actor will describe an arc around the
|
|
|
|
rotation center, as if it's swinging from an invisible thread.</para>
|
|
|
|
|
|
|
|
<para>The same code as shown above can be used to set the
|
|
|
|
rotation center: just set the rotation center coordinates to
|
|
|
|
negative numbers (outside the actor). However, you can't use the
|
|
|
|
gravity functions if the rotation center falls outside an actor.</para>
|
|
|
|
|
|
|
|
<para>For example, here's a rotation to -180 degrees in the x
|
|
|
|
axis, with the y rotation center set to -96 (the same as the height
|
|
|
|
of the actor):</para>
|
|
|
|
|
|
|
|
<inlinemediaobject>
|
|
|
|
<videoobject>
|
|
|
|
<videodata fileref="videos/animations-rotating-x-minus-180-with-y-minus-96.ogv"/>
|
|
|
|
</videoobject>
|
|
|
|
<alt>
|
|
|
|
<para>Video showing an actor rotating to -180 degrees on
|
|
|
|
the x axis with y rotation center set to -96</para>
|
|
|
|
</alt>
|
|
|
|
</inlinemediaobject>
|
|
|
|
|
|
|
|
<para>Similarly, moving the z rotation center (for a rotation
|
|
|
|
in the x or y axis) will cause the actor to swing "into" or "out
|
|
|
|
of" the UI. Its final apparent size may be different, as it could
|
|
|
|
reach a different depth in the UI by the end of the
|
|
|
|
animation.</para>
|
|
|
|
|
|
|
|
<para>For example, here's a rotation to -180 in the x axis,
|
|
|
|
with the z rotation center set to -96 (the same as the height
|
|
|
|
of the actor):</para>
|
|
|
|
|
|
|
|
<inlinemediaobject>
|
|
|
|
<videoobject>
|
|
|
|
<videodata fileref="videos/animations-rotating-x-minus-180-with-z-minus-96.ogv"/>
|
|
|
|
</videoobject>
|
|
|
|
<alt>
|
|
|
|
<para>Video showing an actor rotating to -180 degrees on
|
|
|
|
the x axis with z rotation center set to -96</para>
|
|
|
|
</alt>
|
|
|
|
</inlinemediaobject>
|
|
|
|
|
|
|
|
<para>The apparent final size of the actor is reduced, as it
|
|
|
|
has rotated away from the view point.</para>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
2010-09-13 09:28:16 +00:00
|
|
|
<section id="animations-rotating-discussion-direction">
|
2010-07-21 13:50:32 +00:00
|
|
|
<title>Direction of rotation</title>
|
|
|
|
|
|
|
|
<para>The apparent direction of an animated rotation depends on
|
|
|
|
two things:</para>
|
|
|
|
|
|
|
|
<orderedlist>
|
|
|
|
<listitem>
|
|
|
|
<para>Whether the angle of rotation is positive or negative.</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>The rotation of the container(s) the actor is inside.</para>
|
|
|
|
</listitem>
|
|
|
|
</orderedlist>
|
|
|
|
|
|
|
|
<para>In the case of the sign of the rotation, here's what
|
|
|
|
happens for each axis and rotation angle sign (positive or
|
|
|
|
negative).</para>
|
|
|
|
|
|
|
|
<informaltable>
|
|
|
|
<thead>
|
|
|
|
<tr>
|
|
|
|
<th>Axis</th>
|
|
|
|
<th>Sign of rotation angle</th>
|
|
|
|
<th>Effect on actor</th>
|
|
|
|
</tr>
|
|
|
|
</thead>
|
|
|
|
<tbody>
|
|
|
|
<tr>
|
|
|
|
<td>z</td>
|
|
|
|
<td>+</td>
|
|
|
|
<td>
|
|
|
|
Clockwise spin about the <code>x,y</code> center of
|
|
|
|
rotation.
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td>z</td>
|
|
|
|
<td>-</td>
|
|
|
|
<td>
|
|
|
|
Anti-clockwise spin about the <code>x,y</code>
|
|
|
|
center of rotation.
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td>x</td>
|
|
|
|
<td>+</td>
|
|
|
|
<td>
|
|
|
|
The top swings away from the view point and the
|
|
|
|
bottom swings towards it. If y rotation center == 0,
|
|
|
|
the top is fixed; if y rotation center == the actor's
|
|
|
|
height, the bottom is fixed.
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td>x</td>
|
|
|
|
<td>-</td>
|
|
|
|
<td>
|
|
|
|
The bottom swings away from the view point and the
|
|
|
|
top swings towards it. If y rotation center == 0,
|
|
|
|
the top is fixed; if y rotation center == the actor's
|
|
|
|
height, the bottom is fixed.
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td>y</td>
|
|
|
|
<td>+</td>
|
|
|
|
<td>
|
|
|
|
The right-hand side swings away from the view point and
|
|
|
|
the left-hand side swings towards it. When x rotation
|
|
|
|
center == 0, the left-hand side if fixed; when x
|
|
|
|
rotation center == the actor's width, the right-hand
|
|
|
|
side is fixed.
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td>y</td>
|
|
|
|
<td>-</td>
|
|
|
|
<td>
|
|
|
|
The right-hand side swings towards the view point and
|
|
|
|
the left-hand side swings away from it. When x rotation
|
|
|
|
center == 0, the left-hand side if fixed; when x
|
|
|
|
rotation center == the actor's width, the right-hand
|
|
|
|
side is fixed.
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
</tbody>
|
|
|
|
</informaltable>
|
|
|
|
|
|
|
|
<para>If an actor's container is rotated, this may affect the
|
|
|
|
appearance of rotation animations applied to the actor. In
|
|
|
|
particular, if an actor's container has been rotated
|
|
|
|
by 180 degrees in one axis, the direction of that actor's
|
|
|
|
rotation may appear reversed.</para>
|
|
|
|
|
|
|
|
<para>For example, the video below shows an actor being animated
|
|
|
|
to 90 degrees on the z axis, then back to 0 degrees;
|
|
|
|
the actor's container is then rotated by 180 degrees in the y
|
|
|
|
axis; then the same rotation 90 degree rotation is applied
|
|
|
|
to the actor again. Note that the first time the animation
|
|
|
|
is applied, the rotation is clockwise; but the second time (as
|
|
|
|
the actor is effectively "reversed"), it is anti-clockwise.</para>
|
|
|
|
|
|
|
|
<inlinemediaobject>
|
|
|
|
<videoobject>
|
|
|
|
<videodata fileref="videos/animations-rotating-container-reverses-direction.ogv"/>
|
|
|
|
</videoobject>
|
|
|
|
<alt>
|
|
|
|
<para>Video showing how an actor's apparent rotation is
|
|
|
|
affected by the rotation of its parent</para>
|
|
|
|
</alt>
|
|
|
|
</inlinemediaobject>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Apparent vs. actual rotation</title>
|
|
|
|
|
|
|
|
<para>There is a difference between an actor's <emphasis>apparent</emphasis>
|
|
|
|
rotation (how much an actor appears to be rotating, from the
|
|
|
|
perspective of someone looking at the UI) and its
|
|
|
|
<emphasis>actual</emphasis> rotation (how much that actor is
|
|
|
|
really rotating).</para>
|
|
|
|
|
|
|
|
<para>For example, if you rotate an actor and its container
|
|
|
|
simultaneously, each by 90 degrees in the same direction, the
|
|
|
|
actor will appear to have rotated by 180 degrees by the end
|
|
|
|
of the animation. However, calling the
|
|
|
|
<function>clutter_actor_get_rotation()</function> function
|
|
|
|
for that axis on the actor still returns a rotation of 90
|
|
|
|
degrees.</para>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Orientation of rotation axes</title>
|
|
|
|
|
|
|
|
<para>The rotation axes remain fixed in the same place on
|
|
|
|
the actor regardless of its rotation, even though from the viewer's
|
|
|
|
perspective they may appear to move.</para>
|
|
|
|
|
|
|
|
<para>For example, when rotation in the z axis is 0 degrees,
|
|
|
|
the actor's x axis is horizontal (across the UI) from both the
|
|
|
|
actor's and the viewer's perspective. However, if you rotate the
|
|
|
|
actor by 90 degrees in the z axis, the x axis is now vertical from
|
|
|
|
<emphasis>the viewer's</emphasis> perspective, but still horizontal
|
|
|
|
across the actor from <emphasis>the actor's</emphasis>
|
|
|
|
perspective.</para>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Full example</title>
|
|
|
|
|
|
|
|
<example id="animations-rotating-example">
|
|
|
|
<title>Rotating an actor around x, y, and z axes using
|
|
|
|
<type>ClutterState</type></title>
|
|
|
|
<programlisting>
|
|
|
|
<xi:include href="examples/animations-rotating.c" parse="text">
|
|
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
|
|
</xi:include>
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
2010-09-03 13:31:44 +00:00
|
|
|
<section id="animations-complex">
|
|
|
|
<title>Creating complex animations with
|
|
|
|
<type>ClutterAnimator</type></title>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Problem</title>
|
|
|
|
|
|
|
|
<para>You want to create a complex animation involving one or more
|
|
|
|
actors. The animation will consist of a sequence of transitions
|
|
|
|
over multiple properties on each actor.</para>
|
|
|
|
|
|
|
|
<para>An example might be moving several actors between points,
|
|
|
|
with different types of movement for each part of the path, while
|
|
|
|
transforming each actor (e.g. scaling or rotating it).</para>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Solution</title>
|
|
|
|
|
|
|
|
<para>Use a <type>ClutterAnimator</type> to define the animation.</para>
|
|
|
|
|
|
|
|
<para>Because there are many complex animations you
|
|
|
|
<emphasis>could</emphasis> implement, the example below does
|
|
|
|
this:</para>
|
|
|
|
|
|
|
|
<inlinemediaobject>
|
|
|
|
<videoobject>
|
|
|
|
<videodata fileref="videos/animations-complex.ogv"/>
|
|
|
|
</videoobject>
|
|
|
|
<alt>
|
|
|
|
<para>Video showing a complex animation of an actor
|
|
|
|
using <type>ClutterAnimator</type></para>
|
|
|
|
</alt>
|
|
|
|
</inlinemediaobject>
|
|
|
|
|
|
|
|
<para>Although this uses a single actor, the animation is complex
|
|
|
|
enough to make it difficult to implement with implicit animations
|
|
|
|
or <type>ClutterState</type> (see
|
|
|
|
<link linkend="animations-complex-why-clutteranimator">the Discussion
|
|
|
|
section</link> for reasons why).</para>
|
|
|
|
|
|
|
|
<para>Here is a JSON definition of the stage, actors, and
|
|
|
|
the <type>ClutterAnimator</type> for this
|
|
|
|
animation:</para>
|
|
|
|
|
|
|
|
<example id="animations-complex-example-1">
|
|
|
|
<title>JSON definition of a complex animation using
|
|
|
|
<type>ClutterAnimator</type></title>
|
|
|
|
<programlisting>
|
|
|
|
<xi:include href="examples/animations-complex.json" parse="text">
|
|
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
|
|
</xi:include>
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
|
|
|
|
<note>
|
|
|
|
<para>The core to understanding this example is understanding
|
|
|
|
how to define keys for a <type>CutterAnimator</type>. As
|
|
|
|
this is an involved topic, further explanation
|
|
|
|
is given in <link linkend="animations-complex-discussion-keys">the
|
|
|
|
Discussion section</link>.</para>
|
|
|
|
</note>
|
|
|
|
|
|
|
|
<para>The program for loading this JSON definition from a file
|
|
|
|
is as follows:</para>
|
|
|
|
|
|
|
|
<example id="animations-complex-example-2">
|
|
|
|
<title>Simple program for loading a JSON script;
|
|
|
|
any key press starts the animation</title>
|
|
|
|
<programlisting>
|
|
|
|
<xi:include href="examples/animations-complex.c" parse="text">
|
|
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
|
|
</xi:include>
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
|
|
|
|
<note>
|
|
|
|
<para>It is also possible to use the <type>ClutterAnimator</type>
|
|
|
|
C API to define keys for an animation, but this will
|
|
|
|
typically be much more verbose than the JSON equivalent.</para>
|
|
|
|
|
|
|
|
<para>One other advantage of JSON is that it is much simpler
|
|
|
|
to tweak and test an animation, as you don't have to recompile
|
|
|
|
the application each time you edit it (you just load
|
|
|
|
the new JSON file).</para>
|
|
|
|
</note>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section id="animations-complex-discussion">
|
|
|
|
<title>Discussion</title>
|
|
|
|
|
|
|
|
<para>You can think of <type>ClutterAnimator</type>
|
|
|
|
as a way to give directions to actors. For example,
|
|
|
|
you could give a real (human) actor a direction like "move
|
|
|
|
downstage; when you get there, stop and
|
|
|
|
rotate 90 degrees to your right". In code,
|
|
|
|
this might equate to a transition in the <varname>x</varname>
|
|
|
|
and <varname>y</varname> properties of the actor, followed by a
|
|
|
|
rotation in one axis.</para>
|
|
|
|
|
|
|
|
<note>
|
|
|
|
<para><type>ClutterAnimator</type> can give
|
|
|
|
"directions" to any type of GObject, but we concentrate
|
|
|
|
on animating <type>ClutterActors</type> in this section.</para>
|
|
|
|
</note>
|
|
|
|
|
|
|
|
<para>Each direction like this has an implicit
|
|
|
|
timeline, spanning the length of time the direction should
|
|
|
|
take to fulfil (you set the length of the timeline through
|
|
|
|
the <varname>duration</varname> property of the
|
|
|
|
<type>ClutterAnimator</type>). But within that timeline, you may
|
|
|
|
change the proportion of time spent on each action: "move
|
|
|
|
downstage quickly, then slowly rotate 90 degrees
|
|
|
|
to your right". The direction is the same, but we've
|
|
|
|
specified how much of the timeline should be devoted to each
|
|
|
|
action.</para>
|
|
|
|
|
|
|
|
<para>In <type>ClutterAnimator</type>, this concept is
|
|
|
|
captured by <emphasis>key frames</emphasis>. A
|
|
|
|
key frame represents a point somewhere along the timeline,
|
|
|
|
with one or more target property values for one or more actors.
|
|
|
|
A <type>ClutterAnimator</type> manages the transitions
|
|
|
|
between property values for each object, ensuring that
|
|
|
|
the target values are reached when the associated key frame
|
|
|
|
is reached.</para>
|
|
|
|
|
|
|
|
<para>To change the amount of time a transition
|
|
|
|
should take, you change the percentage of the timeline
|
|
|
|
between key frames. Using our real stage directions as an
|
|
|
|
example, you might define the key frames like this:</para>
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>
|
|
|
|
<para><emphasis>0.2 (after 20% of the timeline):</emphasis>
|
|
|
|
arrive downstage</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para><emphasis>1.0 (by the end of the timeline):</emphasis>
|
|
|
|
achieve a 90 degree rotation to the right</para>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
<para>See
|
|
|
|
<link linkend="animations-complex-discussion-keys">this
|
|
|
|
section</link> for more details about keys and key frames.</para>
|
|
|
|
|
|
|
|
<para>Finally, a direction might be further refined with
|
|
|
|
a description of the kind of movement to use:
|
|
|
|
rather than saying "move downstage quickly, then
|
|
|
|
slowly rotate 90 degrees to your right" a director could say:
|
|
|
|
"start off slowly, but build up to a run;
|
|
|
|
run downstage quickly; then stop and start rotating
|
|
|
|
slowly to your right, gradually speeding up, turn a little more, then slow
|
|
|
|
down gradually; you should end up rotated 90 degrees to your right"
|
|
|
|
(this granularity of description is closer to what you might
|
|
|
|
see in dance notation like
|
|
|
|
<ulink href="http://en.wikipedia.org/wiki/Labanotation">Laban</ulink>;
|
|
|
|
though of course you can't animate human opacity, scale, dimensions
|
|
|
|
etc...).</para>
|
|
|
|
|
|
|
|
<para><type>ClutterAnimator</type> gives you this level of
|
|
|
|
granularity. Each transition to a property value between
|
|
|
|
key frames can have a separate <emphasis>easing mode</emphasis>:
|
|
|
|
for example, starting off slowly and building to a constant
|
|
|
|
speed equates to an "ease in" mode; starting slowly, speeding
|
|
|
|
up, maintaining a constant speed, then gradually slowing down
|
|
|
|
equates to "ease in and ease out".</para>
|
|
|
|
|
|
|
|
<para>To summarise: creating a complex animation means deciding:</para>
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>
|
|
|
|
<para>Which properties need to change on which actors?</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>What target value should each property transition to?</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>How quickly (by which key frame) should the property
|
|
|
|
reach the target value?</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>What "shape" (easing mode) should the change to
|
|
|
|
the target value follow?</para>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
<section id="animations-complex-discussion-keys">
|
|
|
|
<title>Understanding keys and key frames</title>
|
|
|
|
|
|
|
|
<para>A <type>ClutterAnimator</type> maintains a list of
|
|
|
|
<varname>properties</varname> objects, each being a unique pair
|
|
|
|
of <varname>object</varname> (an object to be animated) +
|
|
|
|
<varname>name</varname> (name of the property
|
|
|
|
to be animated on that object).</para>
|
|
|
|
|
|
|
|
<para>Each <varname>properties</varname> object in turn has a
|
|
|
|
list of keys, with each key having three elements:</para>
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>
|
|
|
|
<para>The <emphasis>key frame</emphasis>, expressed as a fraction
|
|
|
|
(between 0.0 and 1.0) of the duration of the animation. At this
|
|
|
|
point, the named property should reach a target value.</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>The <emphasis>easing mode</emphasis> to use to transition
|
|
|
|
the property to that value.</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>The <emphasis>target value</emphasis> the property
|
|
|
|
should transition to.</para>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
<para>For example:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
{
|
|
|
|
"object" : "rectangle",
|
|
|
|
"name" : "x",
|
|
|
|
"ease-in" : true,
|
|
|
|
"keys" : [
|
|
|
|
[ 0.0, "linear", 0.0 ],
|
|
|
|
[ 0.1, "easeInCubic", 150.0 ],
|
|
|
|
[ 0.8, "linear", 150.0 ],
|
|
|
|
[ 1.0, "easeInCubic", 0.0 ]
|
|
|
|
]
|
|
|
|
}
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>defines a sequence of transitions for the <varname>x</varname>
|
|
|
|
property (position on the x axis) of the <code>rectangle</code>
|
|
|
|
object, as follows:</para>
|
|
|
|
|
|
|
|
<orderedlist>
|
|
|
|
<listitem>
|
|
|
|
<para><emphasis>[ 0.0, "linear", 0.0 ]</emphasis>:
|
|
|
|
At the start of the animation, <code>x</code> should be
|
|
|
|
0.0; <code>linear</code> is used as the easing mode, as there
|
|
|
|
is no transition here.</para>
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
<para><emphasis>[ 0.1, "easeInCubic", 150.0 ]</emphasis>:
|
|
|
|
By 10% of the way through the animation,
|
|
|
|
<code>x</code> should reach a value of <code>150.0</code>.
|
|
|
|
This moves the rectangle horizontally across the stage.</para>
|
|
|
|
|
|
|
|
<para>The <code>easeInCubic</code> easing mode means that
|
|
|
|
the transition to the new value starts slow and speeds up.
|
|
|
|
This makes the movement look more "natural".</para>
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
<para><emphasis>[ 0.8, "linear", 150.0 ]</emphasis>:
|
|
|
|
From 10% of the way through the animation to 80%
|
|
|
|
of the way through, the <code>x</code> value remains at
|
|
|
|
<code>150.0</code>. This makes the rectangle stay still
|
|
|
|
on the x axis throughout this period.</para>
|
|
|
|
|
|
|
|
<para>It's important to specify interim key frames if
|
|
|
|
in a later key frame you intend to change the value again
|
|
|
|
(as is done for the <code>x</code> value here). Otherwise
|
|
|
|
you can get premature transitions to a value over longer
|
|
|
|
periods than you intended. By specifying the interim
|
|
|
|
key frames where the value remains constant, you ensure
|
|
|
|
that it doesn't change before you want it to.</para>
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
<para><emphasis>[ 1.0, "easeInCubic", 0.0 ]</emphasis>:
|
|
|
|
From 80% of the way through the animation to the end,
|
|
|
|
the <code>x</code> value should transition back to
|
|
|
|
<code>0.0</code>. This moves the actor back to its
|
|
|
|
starting position on the x axis. Again, an <code>easeInCubic</code>
|
|
|
|
easing mode is used to make the transition appear more natural.</para>
|
|
|
|
</listitem>
|
|
|
|
</orderedlist>
|
|
|
|
|
|
|
|
<para>There are two more properties you can set for each
|
|
|
|
object/property pair:</para>
|
|
|
|
|
|
|
|
<orderedlist>
|
|
|
|
<listitem>
|
|
|
|
<para>Set <varname>ease-in</varname> to <code>true</code> to
|
|
|
|
animate to the target value at the first key frame. If
|
|
|
|
<varname>ease-in</varname> is false, the animation will
|
|
|
|
"jump" to the target value instead (if the target value is
|
|
|
|
different from the current value).</para>
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
<para>Set <varname>interpolation</varname> to either
|
|
|
|
<code>"linear"</code> (the default) or <code>"cubic"</code>.
|
|
|
|
This sets how <type>ClutterAnimator</type> transitions between
|
|
|
|
key frames; in effect, it further modulates any easing modes
|
|
|
|
set on individual keys: if set to <code>"cubic"</code>, you
|
|
|
|
get a slightly more natural and gentle transition between
|
|
|
|
key frames than you do if set to <code>"linear"</code>.</para>
|
|
|
|
</listitem>
|
|
|
|
</orderedlist>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section id="animations-complex-why-clutteranimator">
|
|
|
|
<title>Why <type>ClutterAnimator</type>?</title>
|
|
|
|
|
|
|
|
<para>Why use <type>ClutterAnimator</type> and not the other
|
|
|
|
<link linkend="animations-introduction-api">Clutter animation
|
|
|
|
approaches</link> for complex animations?</para>
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>
|
|
|
|
<para><emphasis>Implicit animations</emphasis> can animate
|
|
|
|
properties on a single actor; however, you can only specify a
|
|
|
|
single transition for each property. Also, it's not possible
|
|
|
|
to describe complex movement along a path in a single implicit
|
|
|
|
animation: you would have to chain several animations together
|
|
|
|
to do that.</para>
|
|
|
|
|
|
|
|
<para>To animate multiple actors, you'd also need multiple
|
|
|
|
implicit animations, one for each actor. These animations would
|
|
|
|
also need to be synchronized (for example, by sharing a
|
|
|
|
single timeline).</para>
|
|
|
|
|
|
|
|
<para>So it would be possible, but more difficult than
|
|
|
|
an implementation using <type>ClutterAnimator</type>.</para>
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
<para><emphasis><type>ClutterState</type></emphasis> can
|
|
|
|
be used for complex animations: each state can describe
|
|
|
|
transitions for multiple actors and multiple properties.
|
|
|
|
However, to make continuous movement (as in the example),
|
|
|
|
you would need to write a state for each movement between a
|
|
|
|
pair of points; then add a callback so that when each state
|
|
|
|
is reached, the animation moves onto the next state. This
|
|
|
|
adds some code (a handler for the <code>completed</code>
|
|
|
|
signal emitted by the <type>ClutterState</type> to set
|
|
|
|
the next state). This could work OK for a few states,
|
|
|
|
but doesn't scale as well as <type>ClutterAnimator</type>
|
|
|
|
if you have many transitions.</para>
|
|
|
|
|
|
|
|
<note>
|
|
|
|
<para><type>ClutterState</type> and
|
|
|
|
<type>ClutterAnimator</type> are not mutually exclusive. If
|
|
|
|
you generally need to transition between several known states
|
|
|
|
(e.g. hiding/revealing menus which stay in the same place,
|
|
|
|
moving between two UI layouts), but want to create a
|
|
|
|
complex animation between states, you can use
|
|
|
|
<type>ClutterAnimators</type> to define the transitions: see
|
|
|
|
the documentation for
|
|
|
|
<function>clutter_state_set_animator()</function> for
|
|
|
|
details.</para>
|
|
|
|
</note>
|
|
|
|
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
<para><type>ClutterAnimator</type> is a good fit for complex
|
|
|
|
animations, and probably the best fit for the most complex:
|
|
|
|
it is the simplest way to encode a sequence of transitions
|
|
|
|
for a list of object/property pairs which can be treated
|
|
|
|
as a single animation. This is largely because
|
|
|
|
<type>ClutterAnimator</type> is effectively managing the
|
|
|
|
chaining together of the individual transitions into a whole.</para>
|
|
|
|
|
|
|
|
<para>One other feature of <type>ClutterAnimator</type> which
|
|
|
|
isn't demonstrated here is how it enables transitions to overlap.
|
|
|
|
For example, let's say you wanted an actor
|
|
|
|
to move along a complex path (e.g. described by five pairs of
|
|
|
|
x,y coordinates); but during that movement, you
|
|
|
|
wanted the actor to continuously transition to a scale of
|
|
|
|
4.0 on both the x and y axes.</para>
|
|
|
|
|
|
|
|
<para>To achieve this with <type>ClutterState</type>, you would
|
|
|
|
need to set up five transitions (one to move to each pair of
|
|
|
|
x,y coordinates); plus a callback to chain the state transitions
|
|
|
|
together; and within each transition, you'd have to figure out a
|
|
|
|
percentage of the scaling to apply, so that the actor
|
|
|
|
was at a scale of 4.0 on reaching the final state.</para>
|
|
|
|
|
|
|
|
<para>With <type>ClutterAnimator</type>, you can treat the
|
|
|
|
movement between the coordinates and the scaling separately
|
|
|
|
within the same animation, but overlap their key frames. This
|
|
|
|
makes coding overlapping animations of different properties
|
|
|
|
much more straightforward. See
|
|
|
|
<link linkend="animations-complex-example-3">this JSON
|
|
|
|
definition</link> for an example of how to do this.</para>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Full example</title>
|
|
|
|
|
|
|
|
<example id="animations-complex-example-3">
|
|
|
|
<title>Running multiple transition sequences with
|
|
|
|
different key frames in parallel using
|
|
|
|
<type>ClutterAnimator</type></title>
|
|
|
|
|
|
|
|
<note>
|
|
|
|
<para>This JSON file can be loaded with the same code
|
|
|
|
as used for <link linkend="animations-complex-example-2">this
|
|
|
|
example</link>, by passing the JSON file name on the command line:</para>
|
|
|
|
|
|
|
|
<screen>
|
|
|
|
<prompt>$</prompt> <command>./animations-complex animations-complex-overlapping.json</command>
|
|
|
|
</screen>
|
|
|
|
</note>
|
|
|
|
|
|
|
|
<programlisting>
|
|
|
|
<xi:include href="examples/animations-complex-overlapping.json" parse="text">
|
|
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
|
|
</xi:include>
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
2010-09-13 13:29:37 +00:00
|
|
|
<section id="animations-reuse">
|
|
|
|
<title>Reusing a complex animation on different actors</title>
|
|
|
|
|
|
|
|
<section id="animations-reuse-problem">
|
|
|
|
<title>Problem</title>
|
|
|
|
|
|
|
|
<para>You want to apply the same complex animation to several
|
|
|
|
different actors.</para>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section id="animations-reuse-solution">
|
|
|
|
<title>Solution</title>
|
|
|
|
|
|
|
|
<para>Instead of animating each actor separately, create a
|
|
|
|
<emphasis>rig</emphasis>: an empty container with an associated
|
|
|
|
animation, which will be animated in lieu of
|
|
|
|
animating the actor directly. Do this as follows:</para>
|
|
|
|
|
|
|
|
<orderedlist>
|
|
|
|
<listitem>
|
|
|
|
<para>Initialise the stage and actors, including those
|
|
|
|
to be animated.</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>Define a <type>ClutterContainer</type> and a
|
|
|
|
<type>ClutterAnimator</type> animation to animate it.</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>When you need to animate an actor:</para>
|
|
|
|
|
|
|
|
<orderedlist>
|
|
|
|
<listitem>
|
|
|
|
<para>Create an instance of the rig and its animator.</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>Reparent the actor to the rig.</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>Run the rig's animation.</para>
|
|
|
|
</listitem>
|
|
|
|
</orderedlist>
|
|
|
|
</listitem>
|
|
|
|
</orderedlist>
|
|
|
|
|
|
|
|
<para>For this solution, we're using
|
|
|
|
<ulink url="http://json.org/">JSON</ulink> to define the
|
|
|
|
animation and the user interface elements. For more
|
|
|
|
details about this approach, see
|
|
|
|
<link linkend="script-introduction">the chapter
|
|
|
|
on <type>ClutterScript</type></link>.</para>
|
|
|
|
|
|
|
|
<para>Here's an extract of the JSON definition for the stage and
|
|
|
|
one of five rectangles placed at its left edge (the full definition
|
|
|
|
is in <link linkend="animations-reuse-example-1">the
|
|
|
|
appendix</link>):</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
[
|
|
|
|
{
|
|
|
|
"type" : "ClutterStage",
|
|
|
|
"id" : "stage",
|
|
|
|
|
|
|
|
... stage properties, signal handlers etc. ...
|
|
|
|
|
|
|
|
"children" : [
|
|
|
|
{
|
|
|
|
"type" : "ClutterRectangle",
|
|
|
|
"id" : "rect1",
|
|
|
|
"color" : "white",
|
|
|
|
"width" : 50,
|
|
|
|
"height" : 50,
|
|
|
|
"y" : 50,
|
|
|
|
"reactive" : true,
|
|
|
|
"signals" : [
|
|
|
|
{ "name" : "button-press-event", "handler" : "foo_button_pressed_cb" }
|
|
|
|
]
|
|
|
|
},
|
|
|
|
|
|
|
|
... more children defined here ...
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>The key point to note is how a signal handler is defined
|
|
|
|
for the <code>button-press-event</code>, so that the
|
|
|
|
<function>foo_button_pressed_cb()</function> function will trigger
|
|
|
|
the animation when a (mouse) button is pressed on each rectangle.</para>
|
|
|
|
|
|
|
|
<para>The second JSON definition includes the rig
|
|
|
|
(an empty <type>ClutterGroup</type>) and a
|
|
|
|
<type>ClutterAnimator</type> to animate it. The animation moves the
|
|
|
|
container across the stage and scales it to twice its original
|
|
|
|
size. (This is the <link linkend="animations-reuse-example-2">same
|
|
|
|
code as in the appendix</link>):</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<xi:include href="examples/animations-reuse-animation.json" parse="text">
|
|
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
|
|
</xi:include>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>The remaining parts of the application code load
|
|
|
|
the user interface definition, setting up the stage and rectangles;
|
|
|
|
and define the callback. The full code is
|
|
|
|
<link linkend="animations-reuse-example-3">in the appendix</link>,
|
|
|
|
but below is the most important part, the callback function:</para>
|
|
|
|
|
|
|
|
<informalexample>
|
|
|
|
<programlisting>
|
|
|
|
<![CDATA[
|
|
|
|
gboolean
|
|
|
|
foo_button_pressed_cb (ClutterActor *actor,
|
|
|
|
ClutterEvent *event,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
ClutterScript *ui = CLUTTER_SCRIPT (user_data);
|
|
|
|
ClutterStage *stage = CLUTTER_STAGE (clutter_script_get_object (ui, "stage"));
|
|
|
|
|
|
|
|
ClutterScript *script;
|
|
|
|
ClutterActor *rig;
|
|
|
|
ClutterAnimator *animator;
|
|
|
|
|
|
|
|
/* load the rig and its animator from a JSON file */
|
|
|
|
script = clutter_script_new ();
|
|
|
|
|
|
|
|
/* use a function defined statically in this source file to load the JSON */
|
|
|
|
load_script_from_file (script, ANIMATION_FILE);
|
|
|
|
|
|
|
|
clutter_script_get_objects (script,
|
|
|
|
"rig", &rig,
|
|
|
|
"animator", &animator,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
/* remove the button press handler from the rectangle */
|
|
|
|
g_signal_handlers_disconnect_matched (actor,
|
|
|
|
G_SIGNAL_MATCH_FUNC,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
NULL,
|
|
|
|
foo_button_pressed_cb,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
/* add a callback to clean up the script when the rig is destroyed */
|
|
|
|
g_object_set_data_full (G_OBJECT (rig), "script", script, g_object_unref);
|
|
|
|
|
|
|
|
/* add the rig to the stage */
|
|
|
|
clutter_container_add_actor (CLUTTER_CONTAINER (stage), rig);
|
|
|
|
|
|
|
|
/* place the rig at the same coordinates on the stage as the rectangle */
|
|
|
|
clutter_actor_set_position (rig,
|
|
|
|
clutter_actor_get_x (actor),
|
|
|
|
clutter_actor_get_y (actor));
|
|
|
|
|
|
|
|
/* put the rectangle into the top-left corner of the rig */
|
|
|
|
clutter_actor_reparent (actor, rig);
|
|
|
|
|
|
|
|
clutter_actor_set_position (actor, 0, 0);
|
|
|
|
|
|
|
|
/* animate the rig */
|
|
|
|
clutter_animator_start (animator);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
]]>
|
|
|
|
</programlisting>
|
|
|
|
</informalexample>
|
|
|
|
|
|
|
|
<para>The code creates a new rig and associated animation
|
|
|
|
at the point when the rectangle is clicked. It then positions the
|
|
|
|
rig at the same coordinates as the rectangle, reparents
|
|
|
|
the rectangle to the rig, and starts the rig's animation.</para>
|
|
|
|
|
|
|
|
<note>
|
|
|
|
<para>The signal handler has to be declared non-static and
|
|
|
|
you must use <code>-export-dynamic</code> as an option to the
|
|
|
|
compiler, otherwise the function isn't visible to
|
|
|
|
<type>ClutterScript</type> (as outlined
|
|
|
|
<link linkend="script-signals">in this recipe</link>).</para>
|
|
|
|
</note>
|
|
|
|
|
|
|
|
<para>This is what the animation looks like:</para>
|
|
|
|
|
|
|
|
<inlinemediaobject>
|
|
|
|
<videoobject>
|
|
|
|
<videodata fileref="videos/animations-reuse.ogv"/>
|
|
|
|
</videoobject>
|
|
|
|
<alt>
|
|
|
|
<para>Video of a simple reusable animation</para>
|
|
|
|
</alt>
|
|
|
|
</inlinemediaobject>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section id="animations-reuse-discussion">
|
|
|
|
<title>Discussion</title>
|
|
|
|
|
|
|
|
<para>The above solution reparents an actor to be animated
|
|
|
|
into a rig (an empty placeholder). The rig is a container
|
|
|
|
which acts as a temporary parent for the actor we
|
|
|
|
<emphasis>really</emphasis> want to animate. By animating the rig,
|
|
|
|
it appears as though the actor inside it is being animated (but
|
|
|
|
<link linkend="animations-reuse-discussion-rig-not-actor">see
|
|
|
|
these caveats</link>). This means the same animation can be
|
|
|
|
easily applied to different actors: create an
|
|
|
|
instance of the rig, reparent an actor to it, then
|
|
|
|
run the rig's animation. This is simpler than creating
|
|
|
|
a separate animation for each actor individually, or
|
|
|
|
reusing a single <type>ClutterAnimator</type> on different
|
|
|
|
actors (see
|
|
|
|
<link linkend="animations-reuse-discussion-one-or-many">this
|
|
|
|
section</link>).</para>
|
|
|
|
|
|
|
|
<para>Using JSON enhances the animation's reusability (it's even
|
|
|
|
potentially reusable in another application), makes the code
|
|
|
|
simpler (an animation can be loaded directly from the script),
|
|
|
|
and makes refactoring easier (the animation can be modified
|
|
|
|
without recompiling the application code). However, it also puts
|
|
|
|
some minor limitations on the animation's reusability; namely, you
|
|
|
|
can only set absolute property values in a JSON animation
|
|
|
|
definition. This makes JSON less useful in cases where
|
|
|
|
you need to animate properties relative to their starting
|
|
|
|
values: for example, "move 50 pixels along the x axis" or
|
|
|
|
"rotate by 10 degrees more on the z axis". (This type of animation
|
|
|
|
is probably less portable anyway.) In such cases, the programmable
|
|
|
|
API may be a better option: see the <type>ClutterAnimator</type>
|
|
|
|
documentation for examples.</para>
|
|
|
|
|
|
|
|
<section id="animations-reuse-discussion-one-or-many">
|
|
|
|
<title>One animation vs. many</title>
|
|
|
|
|
|
|
|
<para>In the sample code, a new instance of the rig and its
|
|
|
|
animation are created for each actor. One side effect of this
|
|
|
|
is that all of the actors can animate simultaneously with the
|
|
|
|
"same" animation. If you don't want this behaviour, but still
|
|
|
|
want to use a rig approach, you could create a single instance
|
|
|
|
of the rig and its animation. Then, you could reparent each actor
|
|
|
|
to it in turn.</para>
|
|
|
|
|
|
|
|
<para>To ensure that the rig only animates one actor (or group
|
|
|
|
of actors) at a time, you could track whether the rig is
|
|
|
|
currently animating (e.g. by examining the animation's
|
|
|
|
timeline with <function>clutter_animator_get_timeline()</function>).
|
|
|
|
Then, if the animation is running, prevent any other actor
|
|
|
|
from being reparented to the rig.</para>
|
|
|
|
|
|
|
|
<para>Note that you would also need to "reset" the rig each time the
|
|
|
|
animation completed (move it back to the right start values for
|
|
|
|
its properties), ready to animate the next actor.</para>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section id="animations-reuse-discussion-rig-not-actor">
|
|
|
|
<title>Caveats about animating a rig instead of an actor</title>
|
|
|
|
|
|
|
|
<para>There are a few issues to be aware of in cases
|
|
|
|
where you animate a rig with contained actors, rather than
|
|
|
|
animating the actor directly:</para>
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>
|
|
|
|
<para>Animating a rig doesn't <emphasis>always</emphasis>
|
|
|
|
produce the same visual effect as animating an actor directly.
|
|
|
|
For example, compare the following cases:</para>
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>
|
|
|
|
<para>You rotate an actor by 180 degrees in the
|
|
|
|
<code>y</code> axis, then by 90 degrees in the
|
|
|
|
<code>z</code> axis. The actor appears to rotate in
|
|
|
|
a <emphasis>clockwise</emphasis> direction.</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>You rotate the parent container of an actor
|
|
|
|
by 180 degrees in the <code>y</code> axis; then rotate
|
|
|
|
the actor by 90 degrees in the <code>z</code> axis.
|
|
|
|
The actor appears to rotate in an
|
|
|
|
<emphasis>anti-clockwise</emphasis> direction. By
|
|
|
|
rotating the container, the "back" of the
|
|
|
|
actor faces the view point, so the actor's movement
|
|
|
|
appears reversed. See
|
|
|
|
<link linkend="animations-rotating-discussion-direction">this
|
|
|
|
recipe</link> for more details.</para>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
<para>There may be other situations where you get similar
|
|
|
|
discrepancies.</para>
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
<para>Animating a rig doesn't change an actor's properties,
|
|
|
|
but animating the actor does.</para>
|
|
|
|
|
|
|
|
<para>When you animate a container rather than the actor
|
|
|
|
directly, the reported properties of the actor may not
|
|
|
|
reflect its visual appearance. For example, if you apply
|
|
|
|
a scale animation to a container, the final scale of
|
|
|
|
actors inside it (as returned by
|
|
|
|
<function>clutter_actor_get_scale()</function>) will not
|
|
|
|
reflect the scaling applied to their container; whereas
|
|
|
|
directly animating the actors would cause their scale
|
|
|
|
properties to change.</para>
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
<para>Reparenting an actor to a rig can cause the actor
|
|
|
|
to "jump" to the rig's position, unless you align the
|
|
|
|
actor to the rig first.</para>
|
|
|
|
|
|
|
|
<para>Note that in the sample code, the position of the actor
|
|
|
|
(<code>x</code>, <code>y</code> coordinates) is copied to
|
|
|
|
the rig before the reparenting happens. The actor is then
|
|
|
|
reparented to the rig, and positioned in the rig's
|
|
|
|
top-left corner. So the actor appears to be in the same
|
|
|
|
position, but is now actually inside a rig at the actor's old
|
|
|
|
position.</para>
|
|
|
|
|
|
|
|
<para>Why bother to do this? Because the rig has a default
|
|
|
|
position of <code>0,0</code> (top-left of <emphasis>its</emphasis>
|
|
|
|
container, the stage). If you reparent the actor to the rig,
|
|
|
|
without first copying the actor's position to the rig, the
|
|
|
|
actor appears to "jump" to the rig's position.</para>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section id="animations-reuse-examples">
|
|
|
|
<title>Full example</title>
|
|
|
|
|
|
|
|
<note>
|
|
|
|
<para>The three separate code examples in this section
|
|
|
|
constitute a single application which implements the above
|
|
|
|
solution.</para>
|
|
|
|
</note>
|
|
|
|
|
|
|
|
<example id="animations-reuse-example-1">
|
|
|
|
<title><type>ClutterScript</type> JSON defining several
|
|
|
|
rectangles with signal handlers</title>
|
|
|
|
<programlisting>
|
|
|
|
<xi:include href="examples/animations-reuse-ui.json" parse="text">
|
|
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
|
|
</xi:include>
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
|
|
|
|
<example id="animations-reuse-example-2">
|
|
|
|
<title><type>ClutterScript</type> JSON describing a "rig"
|
|
|
|
and a <type>ClutterAnimator</type> animation</title>
|
|
|
|
<programlisting>
|
|
|
|
<xi:include href="examples/animations-reuse-animation.json" parse="text">
|
|
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
|
|
</xi:include>
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
|
|
|
|
<example id="animations-reuse-example-3">
|
|
|
|
<title>Loading <type>ClutterScript</type> from JSON files
|
|
|
|
in response to events in a user interface</title>
|
|
|
|
<programlisting>
|
|
|
|
<xi:include href="examples/animations-reuse.c" parse="text">
|
|
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
|
|
</xi:include>
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
2010-07-01 10:29:46 +00:00
|
|
|
</chapter>
|