cookbook: Added "inverting an animation" recipe
authorElliot Smith <elliot.smith@intel.com>
Mon, 12 Jul 2010 14:34:23 +0000 (15:34 +0100)
committerElliot Smith <elliot.smith@intel.com>
Mon, 12 Jul 2010 14:45:49 +0000 (15:45 +0100)
Added a new recipe (based on the skeleton in the
animations section of the cookbook) about inverting
an animation by reversing the direction of its timeline.

Uses clutter_actor_animate() as the basic approach,
but mentions ClutterState and ClutterAnimator as well.

doc/cookbook/animations.xml

index 1c31006..4cdcf84 100644 (file)
@@ -15,7 +15,7 @@
     <para>introduction</para>
   </section>
 
-  <section>
+  <section id="animations-inversion">
     <title>Inverting Animations</title>
 
     <section>
     <section>
       <title>Solution</title>
 
-      <para>...</para>
+      <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>...</para>
+      <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>