2. Inverting Animations

2.1. Problem

You want to have an animation exactly mirroring another one that you just played.

2.2. Solution

Reverse the direction of the ClutterTimeline associated with the animation.

For example, here's how to invert an implicit animation which moves an actor along the x axis. The direction of the animation is inverted when the movement along the x axis is completed; it is also inverted if the mouse button is pressed on the actor.

First, set up the animation:

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);

Next, add a function for inverting the timeline:

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);
}

Then add a function which calls _invert_timeline when the animation completes. More importantly, the callback should stop emission of the "completed" signal by the animation. This prevents the ClutterAnimation underlying the implicit animation from being unreferenced; which in turn allows it to be inverted:

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);
}

Finally, the click callback function uses the same _invert_timeline function if the animation is playing; but if the animation is stopped, it will start it instead:

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);
    }
}

2.3. Discussion

If you are using ClutterAnimator rather than implicit animations, clutter_animator_get_timeline() enables you to get the underlying timeline; you could then use the techniques shown above to invert it.

ClutterState enables a different approach to "inverting" an animation: rather than having a single animation which you invert, you would define two or more keys for an actor (or set of actors) and transition between them.

For the example above, you would define two keys: one for the actor's initial position; and a second for the actor at x = 300.0. You would also define the transition between them: 2000 milliseconds with a CLUTTER_EASE_IN_OUT_CUBIC easing mode.

With the states defined, you would then use clutter_state_set_state() inside callbacks to animate the actor between the two x positions. Behind the scenes, ClutterState would handle the animations and timelines for you.