Apocalypse 6

Back to Clutter/Apocalypses

Apocalypse

or Animations in context.

Each animatable instance should be notified whether it is currently being animated.

Each animation should be fully introspectable from the animatable object's perspective: duration, properties involved, easing mode for each property, eventual delays pre/post transition.

An Animation Context ancillary class should be associated to an Animatable instance when the transition starts, and removed when the transition ends.

Each transition should be managed by a Transition object, which determines the policy for the easing: key-frame, state based, chained sequence of functions, etc.

It is responsibility of each Animatable to update the state of the transition, if one is in progress.

Exegesis

Clutter is meant to be, and I quote from the README, a toolkit:

 for creating fast, compelling, portable, and dynamic graphical user interfaces

and yet the default mode of operation for setting an actor's state on the scene graph (position, size, opacity, rotation, scaling, depth, etc.) is *not* dynamic. We assume a static UI, and then animate it.

This is the wrong way to design an API for a toolkit meant to be used to create animated user interfaces. The default mode of operation should be to implicitly animate every state transition, and only allow skipping the animation if the user consciously decides to do so — i.e. the design tenet of the API should be to make The Right Thing™ by default, and make it really hard (or even impossible) to do The Wrong Thing™.

So we should identify "animatable" properties, i.e. those properties that should be implicitly animated by ClutterActor, and use the animation framework we provide to tween the transitions between the current state and the desired state; the implicit animation should happen when setting these properties using the public accessors, and not through some added functionality. For instance, the following:

  clutter_actor_set_position (actor, newX, newY);

should not make the actor jump to the (newX, newY) point; it should tween the actor's position between the current point and the desired point.

Since we have to maintain backward compatibility with existing applications, we still need to mark the transitions explicitly, but we can be smart about it, and treat transition states as a stack that can be pushed and popped, e.g.:

  clutter_actor_save_easing_state (actor);
    
   clutter_actor_set_easing_duration (actor, 500);
   clutter_actor_set_position (actor, newX, newY);
   clutter_actor_set_opacity (actor, newOpacity);
    
  clutter_actor_restore_easing_state (actor);</nowiki>

And we can even start stacking animations, e.g.:

  clutter_actor_save_easing_state (actor);
    
    clutter_actor_set_easing_duration (actor, 500);
    clutter_actor_set_position (actor, newX, newY);
    
    clutter_actor_save_easing_state (actor);
    
      clutter_actor_set_easing_duration (actor, 500);
      clutter_actor_set_easing_mode (actor, CLUTTER_LINEAR);
      clutter_actor_set_opacity (actor, newOpacity);
      clutter_actor_set_depth (actor, newDepth);
    
    clutter_actor_restore_easing_state (actor);
    
  clutter_actor_restore_easing_state (actor);

And so on, and so forth.

The implementation takes advantage of the newly added Transition API, which uses only ClutterTimeline sub-classes and ClutterInterval, to cut down the amount of signal emissions and memory management of object instances; as well of using the ClutterAnimatable interface for custom properties and interpolation of values.

Synopsis

Transition

* The base class for transitions

 public abstract class Transition : Timeline {
   public Interval interval { get; set; };
   public Animatable animatable { get; set; };
   public bool remove_on_complete;

   public virtual compute_value (Animatable a, Interval i, double progress);
 }

PropertyTransition

 public class PropertyTransition : Transition {
   public string property_name { get; set; };
 }

KeyframeTransition

 public class KeyframeTransition : PropertyTransition {
   public double[] keys;
   public Value[] values;
 }

TransitionGroup

 public class TransitionGroup : Transition {
   public void add_transition (Transition t);
   public void remove_transition (Transition t);
 }

Actor

Transition state

 void clutter_actor_save_transition_state (ClutterActor *);
 void clutter_actor_restore_transition_state (ClutterActor *);

The push/pop API is used to create sections; e.g.:

 clutter_actor_save_transition_state (actor);

   clutter_actor_set_easing_mode (actor, CLUTTER_EASE_OUT_CUBIC); // default
   clutter_actor_set_easing_duration (actor, 250); // default
   clutter_actor_set_x (actor, 100);
   clutter_actor_set_y (actor, 100);

   // this will immediately set the width and height
   clutter_actor_save_transition_state (actor);
     clutter_actor_set_easing_duration (actor, 0);
     clutter_actor_set_width (actor, 250);
     clutter_actor_set_height (actor, 250);
   clutter_actor_restore_transition_state (actor);

   // this will animate opacity using the previous transition stat
   clutter_actor_set_opacity (actor, 128);

 clutter_actor_restore_transition_state (actor);

Transition API

 void clutter_actor_set_easing_duration (ClutterActor *, guint msecs);
 guint clutter_actor_get_easing_duration (ClutterActor *);
 void clutter_actor_set_easing_mode (ClutterActor *, ClutterAnimationMode);
 ClutterAnimationMode clutter_actor_get_easing_mode (ClutterActor *);
 void clutter_actor_set_easing_delay (ClutterActor *, guint msecs);
 guint clutter_actor_get_easing_delay (ClutterActor *);

 ClutterTransition *clutter_actor_get_transition (ClutterActor *, const char *path);
 void clutter_actor_add_transition (ClutterActor *, const char *path, ClutterTransition *);
 void clutter_actor_remove_transition (ClutterActor *, const char *path);
 void clutter_actor_remove_all_transitions (ClutterActor *);

Note: The path definition follows the ActorMeta syntax, @section.name.property. if @section.name is omitted, then property refers to a property of self.

Examples

  clutter_actor_set_position (actor, 100, 100);

This will immediately set the ClutterActor:x and ClutterActor:y properties (needed for backward compatibility, until 2.0). Since there is no transition state set, the actor will use an easing-duration of 0.

  clutter_actor_save_easing_state (actor);
  clutter_actor_set_easing_mode (actor, CLUTTER_EASE_OUT_CUBIC);
  clutter_actor_set_easing_duration (actor, 250);
  clutter_actor_set_position (actor, 100, 100);
  clutter_actor_restore_easing_state (actor);

This will tween the ClutterActor:x and ClutterActor:y properties between their current values and the specified target values; if an animation is already in progress, the final values will be updated to the new ones.

  clutter_actor_save_easing_state (actor);
  clutter_actor_set_position (actor, 100, 100);
  clutter_actor_restore_easing_state (actor);

Exactly like the previous example; the default easing mode is CLUTTER_EASE_OUT_CUBIC, and the default duration is 250 milliseconds. save_transition_state() will reset the current animation context to its default values.

  clutter_actor_save_easing_state (actor);
  clutter_actor_set_easing_duration (actor, 0);
  clutter_actor_set_position (actor, 100, 100);
  clutter_actor_restore_easing_state (actor);

Explicitly sets the ClutterActor:easing-duration property to 0, so that the ClutterActor:x and ClutterActor:y properties are immediately applied instead of tweened. This is the preferred way to ensure that an actor state is immediately applied.

Attic/Clutter/Apocalypses/Apocalypse6 (last edited 2024-04-11 15:39:34 by EmmanueleBassi)