/usr/share/gtk-doc/html/clutter-cookbook/animations-reuse.html is in libclutter-1.0-doc 1.20.0-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 | <html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>6. Reusing a complex animation on different actors</title><link rel="stylesheet" type="text/css" href="style.css"><meta name="generator" content="DocBook XSL Stylesheets V1.78.1"><link rel="home" href="index.html" title="The Clutter Cookbook"><link rel="up" href="animations.html" title="Chapter 5. Animations"><link rel="prev" href="animations-complex.html" title="5. Creating complex animations with ClutterAnimator"><link rel="next" href="animations-moving.html" title="7. Moving actors"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">6. Reusing a complex animation on different actors</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="animations-complex.html">Prev</a> </td><th width="60%" align="center">Chapter 5. Animations</th><td width="20%" align="right"> <a accesskey="n" href="animations-moving.html">Next</a></td></tr></table><hr></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="animations-reuse"></a>6. Reusing a complex animation on different actors</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="animations-reuse-problem"></a>6.1. Problem</h3></div></div></div><p>You want to apply the same complex animation to several
different actors.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="animations-reuse-solution"></a>6.2. Solution</h3></div></div></div><p>Instead of animating each actor separately, create a
<span class="emphasis"><em>rig</em></span>: an empty container with an associated
animation, which will be animated in lieu of
animating the actor directly. Do this as follows:</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>Initialise the stage and actors, including those
to be animated.</p></li><li class="listitem"><p>Define a <span class="type">ClutterContainer</span> and a
<span class="type">ClutterAnimator</span> animation to animate it.</p></li><li class="listitem"><p>When you need to animate an actor:</p><div class="orderedlist"><ol class="orderedlist" type="a"><li class="listitem"><p>Create an instance of the rig and its animator.</p></li><li class="listitem"><p>Reparent the actor to the rig.</p></li><li class="listitem"><p>Run the rig's animation.</p></li></ol></div></li></ol></div><p>For this solution, we're using
<a class="ulink" href="http://json.org/" target="_top">JSON</a> to define the
animation and the user interface elements. For more
details about this approach, see
<a class="link" href="script.html#script-introduction" title="1. Introduction">the chapter
on <span class="type">ClutterScript</span></a>.</p><p>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 <a class="link" href="animations-reuse.html#animations-reuse-example-1" title="Example 5.5. ClutterScript JSON defining several rectangles with signal handlers">the
appendix</a>):</p><div class="informalexample"><pre class="programlisting">[
{
"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 ...
]
}
]</pre></div><p>The key point to note is how a signal handler is defined
for the <code class="code">button-press-event</code>, so that the
<code class="function">foo_button_pressed_cb()</code> function will trigger
the animation when a (mouse) button is pressed on each rectangle.</p><p>The second JSON definition includes the rig
(an empty <span class="type">ClutterGroup</span>) and a
<span class="type">ClutterAnimator</span> to animate it. The animation moves the
container across the stage and scales it to twice its original
size. (This is the <a class="link" href="animations-reuse.html#animations-reuse-example-2" title='Example 5.6. ClutterScript JSON describing a "rig" and a ClutterAnimator animation'>same
code as in the appendix</a>):</p><div class="informalexample"><pre class="programlisting">[
{
"type" : "ClutterGroup",
"id" : "rig"
},
{
"type" : "ClutterAnimator",
"id" : "animator",
"duration" : 2000,
"properties" : [
{
"object" : "rig",
"name" : "x",
"ease-in" : true,
"keys" : [
[ 0.0, "linear", 0.0 ],
[ 1.0, "easeOutCubic", 150.0 ]
]
},
{
"object" : "rig",
"name" : "scale-x",
"ease-in" : true,
"keys" : [
[ 0.5, "linear", 1.0 ],
[ 1.0, "easeOutBack", 2.0 ]
]
},
{
"object" : "rig",
"name" : "scale-y",
"ease-in" : true,
"keys" : [
[ 0.5, "linear", 1.0 ],
[ 1.0, "easeOutBack", 2.0 ]
]
}
]
}
]
</pre></div><p>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
<a class="link" href="animations-reuse.html#animations-reuse-example-3" title="Example 5.7. Loading ClutterScript from JSON files in response to events in a user interface">in the appendix</a>,
but below is the most important part, the callback function:</p><div class="informalexample"><pre class="programlisting">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_actor_add_child (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 */
g_object_ref (actor);
clutter_actor_remove_child (clutter_actor_get_parent (actor), actor);
clutter_actor_add_child (rig, actor);
clutter_actor_set_position (actor, 0, 0);
/* animate the rig */
clutter_animator_start (animator);
return TRUE;
}</pre></div><p>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.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>The signal handler has to be declared non-static and
you must use <code class="code">-export-dynamic</code> as an option to the
compiler, otherwise the function isn't visible to
<span class="type">ClutterScript</span> (as outlined
<a class="link" href="script-signals.html" title="3. Connecting to signals in ClutterScript">in this recipe</a>).</p></div><p>This is what the animation looks like:</p><p><video controls="controls" src="videos/animations-reuse.ogv"><a href="videos/animations-reuse.ogv"></a></video></p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="animations-reuse-discussion"></a>6.3. Discussion</h3></div></div></div><p>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
<span class="emphasis"><em>really</em></span> want to animate. By animating the rig,
it appears as though the actor inside it is being animated (but
<a class="link" href="animations-reuse.html#animations-reuse-discussion-rig-not-actor" title="6.3.2. Caveats about animating a rig instead of an actor">see
these caveats</a>). 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 <span class="type">ClutterAnimator</span> on different
actors (see
<a class="link" href="animations-reuse.html#animations-reuse-discussion-one-or-many" title="6.3.1. One animation vs. many">this
section</a>).</p><p>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 <span class="type">ClutterAnimator</span>
documentation for examples.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="animations-reuse-discussion-one-or-many"></a>6.3.1. One animation vs. many</h4></div></div></div><p>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.</p><p>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 <code class="function">clutter_animator_get_timeline()</code>).
Then, if the animation is running, prevent any other actor
from being reparented to the rig.</p><p>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.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="animations-reuse-discussion-rig-not-actor"></a>6.3.2. Caveats about animating a rig instead of an actor</h4></div></div></div><p>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:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>Animating a rig doesn't <span class="emphasis"><em>always</em></span>
produce the same visual effect as animating an actor directly.
For example, compare the following cases:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem"><p>You rotate an actor by 180 degrees in the
<code class="code">y</code> axis, then by 90 degrees in the
<code class="code">z</code> axis. The actor appears to rotate in
a <span class="emphasis"><em>clockwise</em></span> direction.</p></li><li class="listitem"><p>You rotate the parent container of an actor
by 180 degrees in the <code class="code">y</code> axis; then rotate
the actor by 90 degrees in the <code class="code">z</code> axis.
The actor appears to rotate in an
<span class="emphasis"><em>anti-clockwise</em></span> direction. By
rotating the container, the "back" of the
actor faces the view point, so the actor's movement
appears reversed. See
<a class="link" href="animations-rotating.html#animations-rotating-discussion-direction" title="4.3.2. Direction of rotation">this
recipe</a> for more details.</p></li></ul></div><p>There may be other situations where you get similar
discrepancies.</p></li><li class="listitem"><p>Animating a rig doesn't change an actor's properties,
but animating the actor does.</p><p>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
<code class="function">clutter_actor_get_scale()</code>) will not
reflect the scaling applied to their container; whereas
directly animating the actors would cause their scale
properties to change.</p></li><li class="listitem"><p>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.</p><p>Note that in the sample code, the position of the actor
(<code class="code">x</code>, <code class="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.</p><p>Why bother to do this? Because the rig has a default
position of <code class="code">0,0</code> (top-left of <span class="emphasis"><em>its</em></span>
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.</p></li></ul></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="animations-reuse-examples"></a>6.4. Full example</h3></div></div></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>The three separate code examples in this section
constitute a single application which implements the above
solution.</p></div><div class="example"><a name="animations-reuse-example-1"></a><p class="title"><b>Example 5.5. <span class="type">ClutterScript</span> JSON defining several
rectangles with signal handlers</b></p><div class="example-contents"><pre class="programlisting">[
{
"type" : "ClutterStage",
"id" : "stage",
"width" : 300,
"height" : 200,
"color" : "#333355ff",
"signals" : [
{ "name" : "destroy", "handler" : "clutter_main_quit" }
],
"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" }
]
},
{
"type" : "ClutterRectangle",
"id" : "rect2",
"color" : "blue",
"width" : 50,
"height" : 50,
"y" : 50,
"reactive" : true,
"signals" : [
{ "name" : "button-press-event", "handler" : "foo_button_pressed_cb" }
]
},
{
"type" : "ClutterRectangle",
"id" : "rect3",
"color" : "green",
"width" : 50,
"height" : 50,
"y" : 50,
"reactive" : true,
"signals" : [
{ "name" : "button-press-event", "handler" : "foo_button_pressed_cb" }
]
},
{
"type" : "ClutterRectangle",
"id" : "rect4",
"color" : "red",
"width" : 50,
"height" : 50,
"y" : 50,
"reactive" : true,
"signals" : [
{ "name" : "button-press-event", "handler" : "foo_button_pressed_cb" }
]
},
{
"type" : "ClutterRectangle",
"id" : "rect5",
"color" : "grey",
"width" : 50,
"height" : 50,
"y" : 50,
"reactive" : true,
"signals" : [
{ "name" : "button-press-event", "handler" : "foo_button_pressed_cb" }
]
}
]
}
]
</pre></div></div><br class="example-break"><div class="example"><a name="animations-reuse-example-2"></a><p class="title"><b>Example 5.6. <span class="type">ClutterScript</span> JSON describing a "rig"
and a <span class="type">ClutterAnimator</span> animation</b></p><div class="example-contents"><pre class="programlisting">[
{
"type" : "ClutterGroup",
"id" : "rig"
},
{
"type" : "ClutterAnimator",
"id" : "animator",
"duration" : 2000,
"properties" : [
{
"object" : "rig",
"name" : "x",
"ease-in" : true,
"keys" : [
[ 0.0, "linear", 0.0 ],
[ 1.0, "easeOutCubic", 150.0 ]
]
},
{
"object" : "rig",
"name" : "scale-x",
"ease-in" : true,
"keys" : [
[ 0.5, "linear", 1.0 ],
[ 1.0, "easeOutBack", 2.0 ]
]
},
{
"object" : "rig",
"name" : "scale-y",
"ease-in" : true,
"keys" : [
[ 0.5, "linear", 1.0 ],
[ 1.0, "easeOutBack", 2.0 ]
]
}
]
}
]
</pre></div></div><br class="example-break"><div class="example"><a name="animations-reuse-example-3"></a><p class="title"><b>Example 5.7. Loading <span class="type">ClutterScript</span> from JSON files
in response to events in a user interface</b></p><div class="example-contents"><pre class="programlisting">#include <stdlib.h>
#include <clutter/clutter.h>
#define UI_FILE "animations-reuse-ui.json"
#define ANIMATION_FILE "animations-reuse-animation.json"
static gboolean
load_script_from_file (ClutterScript *script,
gchar *filename)
{
GError *error = NULL;
clutter_script_load_from_file (script, filename, &error);
if (error != NULL)
{
g_critical ("Error loading ClutterScript file %s\n%s", filename, error->message);
g_error_free (error);
exit (EXIT_FAILURE);
}
return TRUE;
}
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;
}
int
main (int argc, char *argv[])
{
ClutterScript *script;
ClutterActor *stage;
if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
return 1;
script = clutter_script_new ();
load_script_from_file (script, UI_FILE);
clutter_script_connect_signals (script, script);
clutter_script_get_objects (script,
"stage", &stage,
NULL);
clutter_actor_show (stage);
clutter_main ();
g_object_unref (script);
return EXIT_SUCCESS;
}
</pre></div></div><br class="example-break"></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="animations-complex.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="animations.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="animations-moving.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">5. Creating complex animations with
<span class="type">ClutterAnimator</span> </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> 7. Moving actors</td></tr></table></div></body></html>
|