## Timelines

Using `clutter_threads_add_timeout()` to control an animation is complicated and does not work in concert with the rest of the operations Clutter must perform for each redraw cycle.

For this reason, Clutter provides ClutterTimeline, a class that allows scheduling animations with a definite duration. Timelines are advanced during the redraw cycle so that animations are ready to be performed at the right time. This also means that animations will not affect the event processing; it also means that if the animation is too complex it will be called with a longer delay, thus not blocking the whole UI.

A Timeline is created with:

    ClutterTimeline *timeline = clutter_timeline_new (duration_in_milliseconds);

The duration of the timeline then be modifed via the "duration" property or by using `clutter_timeline_set_duration()`.

A timeline is started via `clutter_timeline_start()` and its playback further manipulated by the `clutter_timeline_pause()`, `clutter_timeline_stop()`, `clutter_timeline_rewind()` and `clutter_timeline_skip()` functions.

By attaching a handler to the timeline's "new-frame" signal a timeline can then be used to drive an animation by altering an actor's visual properties. The callback looks like:

    void
    on_new_frame (ClutterTimeline *timeline,
                  gint             elapsed_msecs,
                  gpointer         user_data)
    {
    }

The `elapsed_msecs` parameter is set to the amount of time elapsed since the beginning of the timeline, and its value is always between 0 and the "duration" value.

The function `clutter_timeline_get_progress()` can also be used to get a normalised value of the timeline's current position between 0 and 1.

Timelines can also be played in reverse by setting the direction using `clutter_timeline_set_direction()`, and can also have a one-time delay set before they begin playing by using the function `clutter_timeline_set_delay()`.

Timelines can also control a pyshical simulation; the `clutter_timeline_get_delta()` function allows retrieving the number of milliseconds elapsed since the previous callback to ensure the physics engine to be able to take the actual time elapsed between iterations into account.

Example 19. Using a Timeline to drive an animation

Rewrite the example above with a ClutterTimeline instead of using `clutter_threads_add_timeout()`

    #include <clutter/clutter.h>

    static void
    on_new_frame (ClutterTimeline *timeline,
    	      gint             elapsed_msecs,
    	      ClutterActor    *actor)
    {
      gdouble angle = 360 * clutter_timeline_get_progress (timeline);

      clutter_actor_set_rotation (actor, CLUTTER_Z_AXIS,
                                  angle,
                                  clutter_actor_get_width (actor) / 2,
    			      clutter_actor_get_height (actor) / 2,
                                  0);
    }

      ...

      ClutterTimeline *timeline;

      /* one second */
      timeline = clutter_timeline_new (1000);

      /* looping indefinitely */
      clutter_timeline_set_repeat_count (timeline, -1);

      /* get a notification each time a new frame should be drawn */
      g_signal_connect (timeline, "new-frame", G_CALLBACK (on_new_frame), actor);

      /* start the timeline once control returns to the main loop */
      clutter_timeline_start (timeline);