mirror of
https://github.com/brl/mutter.git
synced 2024-11-27 02:20:43 -05:00
72caef0de0
The disconnect_matched() function is a bit too complicated, and its simpler wrapper disconnect_by_func() is functionally equivalent in the cases used by the cookbook examples.
3326 lines
126 KiB
XML
3326 lines
126 KiB
XML
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
|
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
|
|
|
<chapter id="animations" xmlns:xi="http://www.w3.org/2003/XInclude">
|
|
<title>Animations</title>
|
|
|
|
<epigraph>
|
|
<attribution>Walt Disney</attribution>
|
|
<para>Animation can explain whatever the mind of man can conceive.</para>
|
|
</epigraph>
|
|
|
|
<section id="animations-introduction">
|
|
<title>Introduction</title>
|
|
|
|
<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>
|
|
|
|
<section id="animations-introduction-alphas">
|
|
<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>
|
|
|
|
<section id="animations-introduction-api">
|
|
<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>
|
|
|
|
</section>
|
|
|
|
<section id="animations-inversion">
|
|
<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>
|
|
|
|
<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>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
<title>Discussion</title>
|
|
|
|
<para>If you are using <type>ClutterAnimator</type> rather than
|
|
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>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
<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>
|
|
|
|
<para>You can do this using any of the approaches provided
|
|
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>
|
|
|
|
<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>
|
|
|
|
<section id="animations-rotating-discussion-direction">
|
|
<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>
|
|
|
|
<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>ClutterAnimator</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>
|
|
|
|
<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_by_func (actor,
|
|
G_CALLBACK (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>
|
|
|
|
<section id="animations-moving">
|
|
<title>Moving actors</title>
|
|
|
|
<section>
|
|
<title>Problem</title>
|
|
|
|
<para>You want to animate the movement of one or more actors.
|
|
For example:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>To move user interface elements in response to user input
|
|
(e.g. keyboard control of a character in a game).</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>To move a group of actors "off stage" to make way
|
|
for another group of actors (e.g. paging through
|
|
thumbnails in a photo viewer).</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>To move an actor to a different position in the
|
|
interface (e.g. moving an icon for a trashed file into
|
|
a wastebin).</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
<title>Solutions</title>
|
|
|
|
<para>Animate the actors movement on one or more axes
|
|
(<varname>x</varname>, <varname>y</varname>,
|
|
<varname>z/depth</varname>) using one or more of the approaches
|
|
available in the Clutter API (implicit animations,
|
|
<type>ClutterState</type>, <type>ClutterAnimator</type>).</para>
|
|
|
|
<section>
|
|
<title>Solution 1: Implicit animations</title>
|
|
|
|
<para>This works well for simple movement of a single actor to
|
|
a single set of coordinates. Here is an example of how to animate
|
|
movement of a <type>ClutterActor</type> <varname>actor</varname>
|
|
to position <code>100.0</code> on <varname>x</varname> axis:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
clutter_actor_animate (actor, CLUTTER_LINEAR, 500,
|
|
"x", 100.0,
|
|
NULL);
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>See <link linkend="animations-moving-example-1">this
|
|
example</link> which demonstrates movement in each axis,
|
|
in response to (mouse) button presses.</para>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
<title>Solution 2: <type>ClutterState</type></title>
|
|
|
|
<para>This suits simple, repeated movement of one or more actors
|
|
between sets of coordinates. Here is an example of how to
|
|
create two states for a <type>ClutterState</type> instance to
|
|
move two actors, <varname>actor1</varname> and
|
|
<varname>actor2</varname>:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
ClutterState *transitions = clutter_state_new ();
|
|
|
|
/* all state transitions take 250ms */
|
|
clutter_state_set_duration (transitions, NULL, NULL, 250);
|
|
|
|
/* create a state called move-down which moves both actors to y = 200.0 */
|
|
clutter_state_set (transitions, NULL, "move-down",
|
|
actor1, "y", CLUTTER_EASE_OUT_CUBIC, 200.0,
|
|
actor2, "y", CLUTTER_EASE_OUT_CUBIC, 200.0,
|
|
NULL);
|
|
|
|
/* create a state called move-up which moves both actors to y = 0.0 */
|
|
clutter_state_set (transitions, NULL, "move-up",
|
|
actor1, "y", CLUTTER_EASE_OUT_CUBIC, 0.0,
|
|
actor2, "y", CLUTTER_EASE_OUT_CUBIC, 0.0,
|
|
NULL);
|
|
|
|
/* move the actors by setting the state */
|
|
clutter_state_set (transitions, "move-down");
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>This <link linkend="animations-moving-example-2">full
|
|
example</link> shows how to move and simultaneously
|
|
scale two actors. When a button is pressed on one actor, it is
|
|
moved and scaled to occupy the right-hand side of the stage;
|
|
the other actor is simultaneously moved back to the left-hand
|
|
side of the stage and scaled down.</para>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
<title>Solution 3: <type>ClutterAnimator</type></title>
|
|
|
|
<para>This is a good way to implement complex movement of
|
|
one or more actors between sets of coordinates.</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
ClutterAnimator *animator = clutter_animator_new ();
|
|
|
|
/* the animation takes 500ms */
|
|
clutter_animator_set_duration (animator, 500);
|
|
|
|
/* at the start of the animation, actor should be at 0.0,0.0;
|
|
* half-way through, at 100.0,100.0;
|
|
* by the end, actor should be at 150.0,200.0;
|
|
* note that you can set different easing modes for each
|
|
* part of the animation and for each property at each key
|
|
*/
|
|
clutter_animator_set (animator,
|
|
|
|
/* keys for the start of the animation */
|
|
actor, "x", CLUTTER_LINEAR, 0.0, 0.0,
|
|
actor, "y", CLUTTER_LINEAR, 0.0, 0.0,
|
|
|
|
/* keys for half-way through the animation */
|
|
actor, "x", CLUTTER_EASE_OUT_CUBIC, 0.5, 100.0,
|
|
actor, "y", CLUTTER_EASE_IN_CUBIC, 0.5, 100.0,
|
|
|
|
/* keys for the end of the animation */
|
|
actor, "x", CLUTTER_EASE_OUT_EXPO, 1.0, 150.0,
|
|
actor, "y", CLUTTER_EASE_OUT_CUBIC, 1.0, 200.0,
|
|
|
|
NULL);
|
|
|
|
/* run the animation */
|
|
clutter_animator_start (animator);
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>The <link linkend="animations-moving-example-3">full
|
|
example</link> demonstrates how <type>ClutterAnimator</type>
|
|
can be used to programmatically animate multiple actors: in this
|
|
case, to simultaneously move three actors to random positions
|
|
along the <varname>x</varname> axis. Synchronising the
|
|
movement of three actors simultaneously using implicit
|
|
animations would be possible but awkward;
|
|
<type>ClutterState</type> might be another option,
|
|
but it wasn't really designed for this case: there are no persistent
|
|
states to transition between, as the actor positions are
|
|
generated on each key press.</para>
|
|
|
|
<note>
|
|
<para>If you want to apply the same movement to a group of
|
|
actors, rather than different movements for each actor,
|
|
it's often better to put the actors into a container
|
|
of some kind and move that instead of moving the actors
|
|
individually.</para>
|
|
</note>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
<title>Discussion</title>
|
|
|
|
<section>
|
|
<title>Movement can take an actor "outside" its container</title>
|
|
|
|
<para>Actor movement in the <varname>x</varname> and
|
|
<varname>y</varname> axes is relative to the actor's parent
|
|
container. There is nothing to stop you animating an actor
|
|
until it falls outside the bounds of its container. This
|
|
could result in the actor moving "off" the interface; though it's
|
|
worth remembering that the actor is not unparented or destroyed
|
|
if this happens.</para>
|
|
|
|
<para>To ensure that an actor remains visible, its position
|
|
should remain within the visible area of the container. In practice,
|
|
this means either anywhere in the container, if no clip area
|
|
has been set; or within the container's clip area, if set.</para>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
<title>Anchor points can affect movement</title>
|
|
|
|
<para>An actor's anchor point is defined as an <code>x,y</code>
|
|
coordinate relative to the top-left of the actor. The default
|
|
anchor point for an actor is in its top-left
|
|
corner. However, it is possible to set this to some other
|
|
coordinate, relative to the actor's top-left corner,
|
|
using the <function>clutter_anchor_set_anchor_point()</function>
|
|
function.</para>
|
|
|
|
<para>For example:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
/* set the actor's size to 100px x 100px */
|
|
clutter_actor_set_size (actor, 100, 100);
|
|
|
|
/* set an anchor point half-way along the top of the actor */
|
|
clutter_actor_set_anchor_point (actor, 50.0, 0.0);
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<note>
|
|
<para>In GL terms, the anchor point of an actor is the equivalent
|
|
of applying an additional transformation of <code>-x, -y</code> to
|
|
the actor's modelview. If the anchor point is <code>0, 0</code>,
|
|
i.e. the top-left corner, then the transformation will leave the
|
|
actor in the same place.</para>
|
|
<para>It is important to note that the anchor point will affect the
|
|
position in which an actor is painted, but will not change the
|
|
position or size that its parent allocated for it.</para>
|
|
<para>Finally, the anchor point will affect the other transformations
|
|
that can be applied to an actor: scaling and rotating.</para>
|
|
</note>
|
|
|
|
<para>A positive anchor point within the width/height bounds of the
|
|
actor is inside the actor. An anchor point outside these bounds
|
|
is outside the actor. You can also set a negative
|
|
<varname>x</varname> or <varname>y</varname> value for
|
|
the anchor point, which will again place the point outside
|
|
the actor's bounds.</para>
|
|
|
|
<para>This is important with respect to moving an actor, because
|
|
you are actually moving the anchor point and "dragging" the
|
|
actor along with it.</para>
|
|
|
|
<para>For example: you have an actor with width 50px, and you
|
|
set its <varname>anchor-x</varname> property to <code>25.0</code>.
|
|
If you move that actor on the <varname>x</varname> axis, you are
|
|
effectively moving a point half-way across the top of the
|
|
actor along the <varname>x</varname> axis (which in turn moves the
|
|
actor).</para>
|
|
|
|
<para>Similarly, you could set the same actor's
|
|
<varname>anchor-x</varname> to <code>-25.0</code>. If you then
|
|
moved the actor along the <varname>x</varname> axis, you would
|
|
effectively be moving the point 25px left of the top of the actor
|
|
along that axis.</para>
|
|
|
|
<para>The video below demonstrates the effect on movement of shifting
|
|
the anchor point on the <varname>x</varname> axis. The
|
|
<emphasis>red</emphasis> rectangle has <varname>anchor-x</varname>
|
|
set to <code>25.0</code>; the <emphasis>green</emphasis> rectangle has
|
|
<varname>anchor-x</varname> set to <code>0.0</code> (the default); the
|
|
<emphasis>blue</emphasis> rectangle has <varname>anchor-x</varname>
|
|
set to <code>-25.0</code>.</para>
|
|
|
|
<inlinemediaobject>
|
|
<videoobject>
|
|
<videodata fileref="videos/animations-moving-anchors.ogv"/>
|
|
</videoobject>
|
|
<alt>
|
|
<para>Video showing the effect of anchor point on movement</para>
|
|
</alt>
|
|
</inlinemediaobject>
|
|
|
|
<para> A <type>ClutterAnimator</type> is
|
|
used to move each of the rectangles to <code>x = 225.0</code>.
|
|
Although the three rectangles move to the same position on the
|
|
<varname>x</varname> axis, it's actually the anchor points
|
|
which are at the same position. These all align on the
|
|
<varname>x</varname> axis with the left-hand edge of the green
|
|
rectangle.</para>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
<title>Actors can move in the <varname>z</varname> axis</title>
|
|
|
|
<para>The examples so far have shown how to move actors in
|
|
the <varname>x</varname> and <varname>y</varname> axes; but it
|
|
is also possible to move actors in the <varname>z</varname>
|
|
axis (i.e. move them closer or further away from the view point).
|
|
This lets you move actors under/over each other.</para>
|
|
|
|
<para>To move an actor in the <varname>z</varname> axis, animate
|
|
its <varname>depth</varname> property. Animating to a negative
|
|
depth moves the actor away from the view point; animating to a
|
|
positive depth moves the actor towards the view point.</para>
|
|
|
|
<para>Changing the depth of an actor also causes perspective
|
|
effects: the actor gets smaller and converges on the center
|
|
of the stage as it gets further from the view point, and
|
|
gets larger and diverges from the center of the stage as it gets
|
|
closer. This results in an apparent (but not actual) change in
|
|
the <code>x,y</code> position and scale of the actor.</para>
|
|
|
|
<note>
|
|
<para>Animating the depth of an actor is slightly different
|
|
from animating its x and y coordinates, as depth is relative
|
|
to the whole stage, not just the parent container of the
|
|
actor. This means that perspective effects are with
|
|
respect to the whole stage: so as an actor's depth
|
|
moves below <code>0.0</code>, it converges on the center
|
|
of the stage, and may even apparently move outside its
|
|
container (if the container stays at the same depth).</para>
|
|
</note>
|
|
|
|
<para>The video below demonstrates the effect of animating
|
|
the depth of four actors to a value of <code>-15000.0</code>.
|
|
Note how the actors converge on the center of the stage,
|
|
as well as appearing to change position and scale; also note
|
|
that they appear to move outside the bounds of their parent containers
|
|
(the four yellow <type>ClutterBoxes</type>).</para>
|
|
|
|
<inlinemediaobject>
|
|
<videoobject>
|
|
<videodata fileref="videos/animations-moving-depth.ogv"/>
|
|
</videoobject>
|
|
<alt>
|
|
<para>Video showing perspective effects when animating
|
|
actor depth</para>
|
|
</alt>
|
|
</inlinemediaobject>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
<title>Movement is affected by constraints</title>
|
|
|
|
<para>An actor can have its x,y position constrained by
|
|
the position of other actors through <type>ClutterBindConstraints</type>.
|
|
This can affect movement in two ways:</para>
|
|
|
|
<orderedlist>
|
|
<listitem>
|
|
<para>If an actor has its <varname>x</varname> and/or
|
|
<varname>y</varname> properties
|
|
bound or aligned to another actor's, you can't animate
|
|
those properties.</para>
|
|
|
|
<para>In effect this means that the bound actor can't be
|
|
moved on a bound axis directly, but can only be moved by
|
|
animating the constraint's properties.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>If you move an actor which has other actors bound to
|
|
it, the bound actors will also move. For example, if
|
|
the actor has several other actors whose <varname>x</varname>
|
|
properties are bound to its <varname>x</varname> property,
|
|
moving the actor on the <varname>x</varname> axis will also
|
|
move the bound actors on that axis.</para>
|
|
|
|
<para>Similarly, if some actor is the source for
|
|
alignment constraints on other actors, moving the source
|
|
will cause those other actors to move, so that they remain in
|
|
alignment with it.</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
|
|
<para>For example, consider two actors bound by constraints
|
|
as follows:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
/* the source actor for the constraint */
|
|
ClutterActor *source;
|
|
|
|
/* the actor bound by the constraint */
|
|
ClutterActor *target;
|
|
|
|
/* a constraint to be added to target */
|
|
ClutterConstraint *constraint;
|
|
|
|
/* ...initialize actors etc... */
|
|
|
|
/* create a constraint for binding the x position of some actor to the
|
|
* x position of source
|
|
*/
|
|
constraint = clutter_bind_constraint_new (source, CLUTTER_BIND_X, 0.0);
|
|
|
|
/* add the constraint to target with a name */
|
|
clutter_actor_add_constraint_with_name (target, "bind-x", constraint);
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>Animating <varname>source</varname> on the <varname>x</varname>
|
|
axis also animates <varname>target</varname> on the same axis:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
clutter_actor_animate (source, CLUTTER_LINEAR, 500,
|
|
"x", 250.0,
|
|
NULL);
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>...while this has no effect, as it would violate
|
|
<varname>constraint</varname> (it's best not to animate
|
|
<varname>target's</varname> <varname>x</varname> property
|
|
directly):</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
clutter_actor_animate (target, CLUTTER_LINEAR, 500,
|
|
"x", 250.0,
|
|
NULL);
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>But the constraint's properties can be animated, to change
|
|
how <varname>source</varname> and <varname>target</varname>
|
|
are bound; which in turn moves <varname>target</varname>:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
clutter_actor_animate (target, CLUTTER_LINEAR, 500,
|
|
"@constraints.bind-x.offset", 250.0,
|
|
NULL);
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>Note the <code>@constraints.<constraint name>.<constraint property></code>
|
|
syntax (which is why we needed to use
|
|
<function>clutter_actor_add_constraint_with_name()</function>,
|
|
so that the constraint can be accessed through the actor).
|
|
We are still animating <varname>target</varname>, but really
|
|
we're indirectly animating a property of one of its constraints.</para>
|
|
|
|
<para>Another alternative would be to directly animate
|
|
the constraint's properties through <type>ClutterState</type>
|
|
or <type>ClutterAnimator</type>, rather than using
|
|
pseudo-properties on the actor animation:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
ClutterAnimator *animator = clutter_animator_new ();
|
|
clutter_animator_set_duration (animator, 500);
|
|
|
|
clutter_animator_set (animator,
|
|
constraint, "offset", CLUTTER_LINEAR, 0.0, 0.0,
|
|
constraint, "offset", CLUTTER_LINEAR, 1.0, 250.0,
|
|
NULL);
|
|
|
|
clutter_animator_start (animator);
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>This could be useful if you need to animate
|
|
multiple constraints between multiple values simultaneously.</para>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
<section id="animations-moving-examples">
|
|
<title>Full examples</title>
|
|
|
|
<example id="animations-moving-example-1">
|
|
<title>Simple movement using implicit animations</title>
|
|
<programlisting>
|
|
<xi:include href="examples/animations-moving-implicit.c" parse="text">
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
</xi:include>
|
|
</programlisting>
|
|
</example>
|
|
|
|
<example id="animations-moving-example-2">
|
|
<title>Using <type>ClutterState</type> to repeatedly move
|
|
(and scale) two actors</title>
|
|
<programlisting>
|
|
<xi:include href="examples/animations-moving-state.c" parse="text">
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
</xi:include>
|
|
</programlisting>
|
|
</example>
|
|
|
|
<example id="animations-moving-example-3">
|
|
<title>Using <type>ClutterAnimator</type> to randomly move
|
|
three actors along the <varname>x</varname> axis</title>
|
|
<programlisting>
|
|
<xi:include href="examples/animations-moving-animator.c" parse="text">
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
</xi:include>
|
|
</programlisting>
|
|
</example>
|
|
|
|
</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 repeatedly reverse 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, and have the timeline invert itself when its end is reached.
|
|
The animation then continues, but in reverse. Once the backward iteration
|
|
completes, the timeline sets itself to run forward again, etc.</para>
|
|
|
|
<para>To make a timeline reverse its direction each time it
|
|
completes, use the <function>clutter_timeline_set_auto_reverse()</function>
|
|
function:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
clutter_timeline_set_auto_reverse (timeline, TRUE);
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>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>
|
|
|
|
<section id="animations-scaling">
|
|
<title>Animated scaling</title>
|
|
|
|
<section>
|
|
<title>Problem</title>
|
|
|
|
<para>You want to animate scaling of an actor. Example use
|
|
cases:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>To animate zooming in/out of a texture in an
|
|
image viewer application.</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>To add an animated "bounce" effect (quick scale up
|
|
followed by scale down) to a UI element
|
|
to indicate it has received focus.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Solution</title>
|
|
|
|
<para>Animate the actor's <varname>scale-x</varname> and
|
|
<varname>scale-y</varname> properties to change the scaling on
|
|
the <varname>x</varname> and <varname>y</varname> axes
|
|
respectively.</para>
|
|
|
|
<para>For example, to animate an actor to twice its current scale
|
|
with implicit animations:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
<![CDATA[
|
|
gdouble scale_x;
|
|
gdouble scale_y;
|
|
|
|
/* get the actor's current scale */
|
|
clutter_actor_get_scale (actor, &scale_x, &scale_y);
|
|
|
|
/* animate to twice current scale on both axes */
|
|
clutter_actor_animate (actor, CLUTTER_LINEAR, 1000,
|
|
"scale-x", scale_x * 2,
|
|
"scale-y", scale_y * 2);
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>Alternatively, <type>ClutterAnimator</type> or
|
|
<type>ClutterState</type> can be used to animate an actor's scale
|
|
properties. See <link linkend="animations-scaling-example-1">this
|
|
example</link> which uses <type>ClutterState</type> to animate
|
|
scaling.</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Discussion</title>
|
|
|
|
<para>Scaling an actor is done through its <varname>scale-x</varname>
|
|
and <varname>scale-y</varname> properties, each of which takes
|
|
a <code>double</code> value. A value of less than
|
|
<code>1.0</code> for an axis scales an actor down on that axis,
|
|
reducing its apparent size; values greater than <code>1.0</code>
|
|
scale an actor up, increasing its apparent size.</para>
|
|
|
|
<para>Why "apparent" size? Because scaling applies a transform
|
|
to an actor which changes how it appears on the
|
|
stage, without changing its "real" size. Similarly, scaling an
|
|
actor may transform its position: it could appear to move to a
|
|
different position within its container,
|
|
although it is "really" at its original position. Run
|
|
<link linkend="animations-scaling-example-1">the
|
|
example</link> to see how size and position are
|
|
transformed by scaling.</para>
|
|
|
|
<para>It can be useful to know an actor's
|
|
<emphasis>transformed</emphasis> position and size after scaling:
|
|
for example, if you were implementing a reflowing layout manager
|
|
which used scaling as part of its allocation algorithm.
|
|
Here's an example of how to get these properties for an
|
|
actor:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
<![CDATA[
|
|
gfloat transformed_x;
|
|
gfloat transformed_y;
|
|
|
|
gfloat transformed_width;
|
|
gfloat transformed_height;
|
|
|
|
clutter_actor_get_transformed_position (actor, &transformed_x, &transformed_y);
|
|
clutter_actor_get_transformed_size (actor, &transformed_width, &transformed_height);
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>Note that you can scale an actor on both axes by the same
|
|
amount (uniform scaling), or by a different amount on each axis
|
|
(differential scaling).</para>
|
|
|
|
<para>Use <function>clutter_actor_is_scaled()</function> to determine
|
|
whether scaling has been applied to an actor: this function returns
|
|
<code>FALSE</code> if both <varname>scale-x</varname> and
|
|
<varname>scale-y</varname> are <code>1.0</code>; otherwise, it
|
|
returns <code>TRUE</code>.</para>
|
|
|
|
<section>
|
|
<title>Scaling vs. resizing</title>
|
|
|
|
<para>Scaling changes the <emphasis>apparent</emphasis> size
|
|
of an actor, while leaving its real size unchanged. By contrast,
|
|
resizing changes the <emphasis>real</emphasis> size of the actor,
|
|
by modifying its <varname>width</varname> and
|
|
<varname>height</varname> properties.</para>
|
|
|
|
<para>Resizing and scaling produce the same visual
|
|
effect, as both make an actor appear to be larger or
|
|
smaller. Therefore, for most purposes, they are interchangeable
|
|
if you just want to change an actor's apparent size.</para>
|
|
|
|
<para>So why would you scale an actor rather than resize it?</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>If you've scaled an actor, you can easily reset it
|
|
to its original size, by setting its
|
|
scale back to <code>1.0</code> on both axes. By contrast,
|
|
to reset a resized actor to its original size,
|
|
you would have to track the original size manually: the
|
|
actor doesn't make its original size accessible.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Scaling can easily change the apparent size
|
|
of multiple actors inside a container. For example, say you
|
|
wanted to shrink multiple actors inside a container
|
|
to half their original size. There are two ways you
|
|
could do this:</para>
|
|
|
|
<orderedlist>
|
|
<listitem>
|
|
<para>The hard way would be to resize
|
|
each actor individually. You couldn't just resize the container,
|
|
as resizing a container doesn't resize its children: usually
|
|
they will be clipped so that they are either partially or
|
|
wholly hidden.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>The easy way would be to set the container's scale
|
|
to half its initial value: the actors
|
|
in the container would retain their original sizes, but would
|
|
appear at half size.</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
<title>Scaling, layouts and containers</title>
|
|
|
|
<para>It is possible to scale actors inside containers. For
|
|
example, if you were using a <type>ClutterBox</type>
|
|
which has a <type>ClutterBoxLayout</type> layout manager,
|
|
you could scale the children of that layout.</para>
|
|
|
|
<para>However, you should remain aware that layout managers
|
|
don't take account of the scale of their children, only their
|
|
size. So if you scale up an actor inside a layout manager,
|
|
it may overlap other actors in the layout: the size allocated
|
|
by the layout manager doesn't increase as an actor's scale
|
|
increases.</para>
|
|
|
|
<para>Similarly, scaling an actor down doesn't reduce the space
|
|
it will be allocated by a layout.</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Setting the scale center</title>
|
|
|
|
<para>An actor's scale center is the point around which
|
|
scaling occurs: when you scale the actor, it will "shrink"
|
|
into (if scale < 1.0) or "expand" out of (if scale > 1.0)
|
|
its scale center.</para>
|
|
|
|
<para>You can change an actor's scale center using
|
|
either gravity (a named position on the actor; for example, the
|
|
middle of the top edge of the actor is
|
|
<constant>CLUTTER_GRAVITY_NORTH</constant>); or
|
|
x,y coordinates relative to the actor's anchor point (by default,
|
|
the anchor point for an actor is at <code>0,0</code>).</para>
|
|
|
|
<para>Setting scale gravity has the same consequences as
|
|
setting both the <varname>scale-center-x</varname> and
|
|
<varname>scale-center-y</varname> properties for an actor.
|
|
For example, <constant>CLUTTER_GRAVITY_NORTH_EAST</constant>
|
|
sets the scale center to <code><width of the actor>, 0</code>,
|
|
relative to the actor's anchor point (defaults to the top-right
|
|
corner of the actor). However, the advantage of scale
|
|
gravities is that they change with the actor: so if the
|
|
actor is resized, you don't have to manually reset the scale
|
|
center. This means that <constant>CLUTTER_GRAVITY_NORTH_EAST</constant>
|
|
will always represent the top-right corner of the actor,
|
|
regardless of how it is scaled or resized. The same is true
|
|
of each of the other scale gravities.</para>
|
|
|
|
<para>If you're animating an actor's scale but want a different
|
|
scale center, set it before the animation begins. One way to
|
|
do this is to leave the actor's scale unchanged, but with
|
|
a different scale center:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
<![CDATA[
|
|
gdouble scale_x;
|
|
gdouble scale_y;
|
|
|
|
/* get the actor's current scale */
|
|
clutter_actor_get_scale (actor, &scale_x, &scale_y);
|
|
|
|
/* set scale center using x,y coordinates, leaving scale unchanged;
|
|
* the actor's size here is assumed to be 200x200
|
|
*/
|
|
clutter_actor_set_scale_full (actor,
|
|
scale_x,
|
|
scale_y,
|
|
100.0, /* center x */
|
|
100.0 /* center y */);
|
|
|
|
/* set scale center using gravity, leaving scale unchanged */
|
|
clutter_actor_set_scale_with_gravity (actor,
|
|
scale_x,
|
|
scale_y,
|
|
CLUTTER_GRAVITY_CENTER);
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>Another approach is to set scale center properties
|
|
via GObject, which doesn't require you to figure out the
|
|
actor's scale first:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
<![CDATA[
|
|
/* set scale center using x,y coordinates */
|
|
g_object_set (actor,
|
|
"scale-center-x", 100.0, /* center x */
|
|
"scale-center-y", 100.0, /* center y */
|
|
NULL);
|
|
|
|
/* set scale center using gravity */
|
|
g_object_set (actor,
|
|
"scale-gravity", CLUTTER_GRAVITY_CENTER,
|
|
NULL);
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>Once the scale center is set, you can animate the
|
|
scaling as per usual.</para>
|
|
|
|
<para>It is even possible to animate the
|
|
<varname>scale-center-*</varname> properties, which can
|
|
produce interesting, though slightly
|
|
unpredictable, effects. It's usually better to change the
|
|
scale center before the animation starts.</para>
|
|
|
|
<para><link linkend="animations-scaling-example-1">The
|
|
example</link> cycles through the available scale gravities,
|
|
showing the effect on the animation of each of the scale
|
|
centers.</para>
|
|
|
|
<para>The <link linkend="animations-scaling-example-2">second
|
|
example</link> shows how to combine scaling in and out on a
|
|
texture, in response to mouse button presses. In this case,
|
|
the scale gravity remains at <constant>CLUTTER_GRAVITY_NORTH_WEST</constant>
|
|
(i.e. at the anchor point of the actor). However, the anchor
|
|
point is moved to the coordinates of each double click on button 1
|
|
(usually the left mouse button) or button 3 (usually the right
|
|
mouse button); which in turn automatically moves the scale center
|
|
before the texture is scaled. As a result, the texture
|
|
"expands" or "contracts" around the clicked point,
|
|
while the point remains still.</para>
|
|
|
|
<warning>
|
|
<para>One final caveat about scale centers: if an actor is
|
|
already scaled, the scale center coordinates are relative to
|
|
the <emphasis>real size</emphasis> of the actor, rather than
|
|
its <emphasis>transformed</emphasis> size. This can result in
|
|
a "jumping" effect if you change the scale center on
|
|
a scaled actor.</para>
|
|
|
|
<para>For example, you might set the scale gravity of an actor
|
|
to <constant>CLUTTER_GRAVITY_WEST</constant>, then
|
|
scale the actor to <code>0.5</code> on both axes. Later, you
|
|
change the actor's scale gravity to
|
|
<constant>CLUTTER_GRAVITY_EAST</constant>. The effect of this
|
|
is to "jump" the actor to the right, so its right-hand edge
|
|
is aligned with where it was at scale <code>1.0</code>.</para>
|
|
|
|
<para>If this isn't desirable, you can just retain the scale
|
|
center on a scaled actor, and only change it when the actor
|
|
is unscaled.</para>
|
|
</warning>
|
|
</section>
|
|
|
|
</section>
|
|
|
|
<section id="animations-scaling-examples">
|
|
<title>Full examples</title>
|
|
|
|
<example id="animations-scaling-example-1">
|
|
<title>Animated scaling of an actor using each of the
|
|
scale gravities. Press any key to start the animation.</title>
|
|
<programlisting>
|
|
<xi:include href="examples/animations-scaling.c" parse="text">
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
</xi:include>
|
|
</programlisting>
|
|
</example>
|
|
|
|
<example id="animations-scaling-example-2">
|
|
<title>Animated scaling (up and down) of a texture in response
|
|
to button presses. Call with the path to an image as the
|
|
first argument.</title>
|
|
<programlisting>
|
|
<xi:include href="examples/animations-scaling-zoom.c" parse="text">
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
</xi:include>
|
|
</programlisting>
|
|
</example>
|
|
</section>
|
|
|
|
</section>
|
|
|
|
<section id="animations-path">
|
|
<title>Animating an actor along a curved path</title>
|
|
|
|
<section>
|
|
<title>Problem</title>
|
|
|
|
<para>You want to animate an actor along a curved path: for
|
|
example, to move an actor in a circle or spiral.</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Solution</title>
|
|
|
|
<para>Create a <type>ClutterPath</type> to describe the
|
|
path the actor should move along; then create a
|
|
<type>ClutterPathConstraint</type> based on that path:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
ClutterPath *path;
|
|
ClutterConstraint *constraint;
|
|
|
|
/* create the path */
|
|
path = clutter_path_new ();
|
|
|
|
/* first node is at 30,60 */
|
|
clutter_path_add_move_to (path, 30, 60);
|
|
|
|
/* add a curve to the top-right of the stage, with control
|
|
* points relative to the start point at 30,60
|
|
*/
|
|
clutter_path_add_rel_curve_to (path,
|
|
120, 180,
|
|
180, 120,
|
|
240, 0);
|
|
|
|
/* create a constraint based on the path */
|
|
constraint = clutter_path_constraint_new (path, 0.0);
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<note>
|
|
<para>For more on the types of curve and line segment available,
|
|
see the <type>ClutterPath</type> API documentation.</para>
|
|
</note>
|
|
|
|
<para>Next, add the constraint to an actor; in this case, the
|
|
actor is a red rectangle:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
ClutterActor *rectangle;
|
|
ClutterActor *stage = clutter_stage_new ();
|
|
|
|
/* ...set size stage, color, etc... */
|
|
|
|
const ClutterColor *red_color = clutter_color_new (255, 0, 0, 255);
|
|
|
|
rectangle = clutter_rectangle_new_with_color (red_color);
|
|
clutter_actor_set_size (rectangle, 60, 60);
|
|
|
|
/* add the constraint to the rectangle; note that this
|
|
* puts the rectangle at the start of the path, i.e. at position 30,60;
|
|
* we also give the constraint a name, so we can use it from an implicit
|
|
* animation
|
|
*/
|
|
clutter_actor_add_constraint_with_name (rectangle, "path", constraint);
|
|
|
|
/* add the rectangle to the stage */
|
|
clutter_container_add_actor (CLUTTER_CONTAINER (stage), rectangle);
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>Note how the constraint has to be assigned a name (here, "path")
|
|
to make it accessible via implicit animations.</para>
|
|
|
|
<para>Finally, animate the constraint's <varname>offset</varname>
|
|
property; which in turn moves the actor along the path:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>
|
|
ClutterTimeline *timeline;
|
|
|
|
/* create a timeline with 1000 milliseconds duration, which loops
|
|
* indefinitely and reverses its direction each time it completes
|
|
*/
|
|
timeline = clutter_timeline_new (1000);
|
|
clutter_timeline_set_loop (timeline, TRUE);
|
|
clutter_timeline_set_auto_reverse (timeline, TRUE);
|
|
|
|
/* animate the offset property on the constraint from 0.0 to 1.0;
|
|
* note the syntax used to refer to the constraints metadata for the
|
|
* rectangle actor:
|
|
*
|
|
* "@constraints.<constraint name>.<property>"
|
|
*/
|
|
clutter_actor_animate_with_timeline (rectangle, CLUTTER_LINEAR, timeline,
|
|
"@constraints.path.offset", 1.0,
|
|
NULL);
|
|
|
|
/* ...show the stage, run the mainloop, free memory on exit... */
|
|
</programlisting>
|
|
</informalexample>
|
|
|
|
<para>The <link linkend="animations-path-example-1">full
|
|
example</link> shows how these fragments fit together.
|
|
The animation produced by this example looks
|
|
like this:</para>
|
|
|
|
<inlinemediaobject>
|
|
<videoobject>
|
|
<videodata fileref="videos/animations-path.ogv"/>
|
|
</videoobject>
|
|
<alt>
|
|
<para>Video showing animation of an actor along a curved
|
|
path using <type>ClutterPathConstraint</type></para>
|
|
</alt>
|
|
</inlinemediaobject>
|
|
|
|
<para>The <link linkend="animations-path-example-2">second full
|
|
example</link> animates an actor around a simulated circle
|
|
using a more complex <type>ClutterPath</type>.</para>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
<title>Discussion</title>
|
|
|
|
<para>Animating an actor using <type>ClutterPathConstraint</type>
|
|
is the recommended way to animate actors along curved paths. It
|
|
replaces the older <type>ClutterBehaviourPath</type>.</para>
|
|
|
|
<para>A <type>ClutterPathConstraint</type> constrains an
|
|
actor's <varname>x</varname> and <varname>y</varname> properties
|
|
to a position along such a <type>ClutterPath</type>: a path through
|
|
2D space. The <type>ClutterPath</type> itself is composed of nodes
|
|
(x,y positions in 2D space), connected by straight lines or (cubic)
|
|
<ulink url="http://en.wikipedia.org/wiki/B%C3%A9zier_curve">Bézier
|
|
curves</ulink>.</para>
|
|
|
|
<note>
|
|
<para><type>ClutterPath</type> doesn't have to be used in animations:
|
|
it can also be used in drawing (see the
|
|
<link linkend="actors-non-rectangular">non-rectangular actor
|
|
recipe</link>).</para>
|
|
</note>
|
|
|
|
<para>The actor's position along the path is determined by the constraint's
|
|
<varname>offset</varname> property, which has a
|
|
value between 0.0 and 1.0. When the offset is 0.0, the actor
|
|
is at the beginning of the path; when the actor is at 1.0, the
|
|
actor is at the end of the path. Between 0.0 and 1.0, the actor
|
|
is some fraction of the way along the path.</para>
|
|
|
|
<para>If you immediately set the <varname>offset</varname> for the
|
|
constraint (e.g. to <code>0.5</code>), the actor is instantly placed
|
|
at that position along the path: for <code>offset = 0.5</code>,
|
|
at the halfway point.</para>
|
|
|
|
<para>By contrast, to animate an actor along a path, you
|
|
<emphasis>animate</emphasis> the offset property of a
|
|
<type>ClutterPathConstraint</type>. The actor's position
|
|
along the path is dependent on the progress of the animation:
|
|
when the animation starts, the actor is at the beginning of the path;
|
|
by the end of the animation, it will have reached its end.</para>
|
|
|
|
<para>If you animate the constraint using a linear easing mode,
|
|
the progress of the animation matches progress along the path: at
|
|
half-way through the animation, the actor will be half-way along
|
|
the path.</para>
|
|
|
|
<para>However, if you are using a non-linear easing mode
|
|
(e.g. a quintic or cubic mode), the offset along the path and
|
|
progress through the animation may differ. This is because the
|
|
offset along the path is computed from the alpha value at that
|
|
point in the animation; this in turn depends on the alpha function
|
|
applied by the animation. (See the
|
|
<link linkend="animations-introduction">animations introduction</link>
|
|
for more details about alphas.)</para>
|
|
|
|
<para>One way to think about this is to imagine the actor
|
|
making a journey along the path. The alpha function governs the
|
|
actor's speed, including how it speeds up and slows down
|
|
during its journey. The actor's speed may be constant
|
|
(as in a linear easing mode). Alternatively, the actor's speed
|
|
may not be constant: it might start out fast then slow down
|
|
(ease out); or start slow and speed up (ease in); or start and
|
|
end fast, but slow down in the middle (ease in and ease out); or
|
|
some other more complex arrangement (as in the bounce and elastic
|
|
easing modes). So where the actor is on the path at a particular
|
|
time doesn't directly relate to how long it's been travelling:
|
|
the position is determined both by how long it's been travelling,
|
|
and changes in its speed throughout the journey.</para>
|
|
|
|
<section>
|
|
<title>Other ways to animate along a path</title>
|
|
|
|
<para><type>ClutterPathConstraint</type> is the only
|
|
decent way of animating along curves in a predictable
|
|
and manageable fashion. It can also be used to animate along
|
|
paths composed of straight lines, though this isn't essential: you
|
|
can do straight line animations directly with <type>ClutterAnimator</type>,
|
|
<type>ClutterState</type> or implicit animations. But if
|
|
you need to animate between more than a half a dozen sets of
|
|
points joined by straight lines, <type>ClutterPathConstraint</type>
|
|
makes sense then too.</para>
|
|
|
|
<para>It is also possible to animate actors over very simple, non-Bézier
|
|
curves without using <type>ClutterPathConstraint</type>. This
|
|
can be done by animating the actor's position properties using
|
|
a non-linear easing mode (see the <type>ClutterAlpha</type>
|
|
documentation for available modes, or write your own custom
|
|
alpha function). <link linkend="animations-path-example-3">This
|
|
example</link> shows how to animate two actors on
|
|
curved paths around each other without
|
|
<type>ClutterPathConstraint</type>.</para>
|
|
|
|
<para>However, it is difficult to precisely calculate paths
|
|
with this approach. It is also only practical where you have a
|
|
very simple curve: if you want to chain together several curved
|
|
motions (as in the <link linkend="animations-path-example-2">circle
|
|
example</link>), this quickly becomes unwieldy.</para>
|
|
|
|
<tip>
|
|
<para>
|
|
If you want physics-based animation, look at
|
|
<ulink url="http://git.clutter-project.org/clutter-box2d/">clutter-box2d</ulink>.
|
|
</para>
|
|
</tip>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
<section id="animations-path-examples">
|
|
<title>Full examples</title>
|
|
|
|
<example id="animations-path-example-1">
|
|
<title>Using a <type>ClutterPathConstraint</type> with
|
|
implicit animations to move an actor along a curved path</title>
|
|
<programlisting>
|
|
<xi:include href="examples/animations-path.c" parse="text">
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
</xi:include>
|
|
</programlisting>
|
|
</example>
|
|
|
|
<example id="animations-path-example-2">
|
|
<title>Using a <type>ClutterPathConstraint</type> with
|
|
<type>ClutterAnimator</type> to animate an actor on
|
|
a simulated circular path</title>
|
|
<programlisting>
|
|
<xi:include href="examples/animations-path-circle.c" parse="text">
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
</xi:include>
|
|
</programlisting>
|
|
</example>
|
|
|
|
<example id="animations-path-example-3">
|
|
<title>Animating actors on curved paths using easing modes</title>
|
|
<programlisting>
|
|
<xi:include href="examples/animations-path-easing.c" parse="text">
|
|
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
|
</xi:include>
|
|
</programlisting>
|
|
</example>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
</chapter>
|