mirror of
https://github.com/brl/mutter.git
synced 2024-12-26 04:42:14 +00:00
7ed3517504
New recipe covering how to animate rotation of an actor (in all axes). Covers various factors affecting rotation animation (like orientation of axes, parent rotation/orientation), as well as trying to make rotations easier to visualise (e.g. describing how rotation direction is affected by those factors, how a rotation can be expected to look when animated). Uses implicit animations for code examples. Also refers to a full code example which uses ClutterState.
1015 lines
36 KiB
XML
1015 lines
36 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>
|
|
<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>
|
|
|
|
</chapter>
|