Ecore Animator

Ecore provides a facility for animations called Ecore_Animator. Ecore animators use the Ecore main loop for creating animations, running a specific action on each tick of a timer in the main loop.

Table of Contents

Create an Ecore Animation

To create an Ecore animation:

  1. Determine the duration of the animation.
  2. Define a callback function that performs the animation with that duration.

To use Ecore animators in your application, you must include the <Ecore.h> file. This file is included by default if you are already using the <Elementary.h> file in your application. You then declare an Ecore_Animator* variable and use the variable in the ecore_animator_* functions.

The following example shows how to create a simple animation with a finite duration:

static Eina_Bool
_do_animation(void *data, double pos)
{
   evas_object_move(data, 100 * pos, 100 * pos);
   // Do some more animating...
}
ecore_animator_timeline_add(2, _do_animation, my_evas_object);

In the above example, we create a linear transition to move my_evas_object from position (0,0) to position (100,100) in 2 seconds.

Creating an Animation with a Finite Duration

Most of the time, you will want to create animations that last for a predefined time.

The ecore_animator_timeline_add() function allows you to define an animator that is automatically deleted after the animation is finished:

Ecore_Animator* ecore_animator_timeline_add(double            runtime,
                                            Ecore_Timeline_Cb func,
                                            const void        *data
)
  • runtime is the duration of the animation in seconds. The duration is not affected by frame rate.
  • func is the callback function that performs the animation.
  • data is the parameter passed to the callback function. This is usually the Evas object to animate.
The callback function can return ECORE_CALLBACK_RENEW to keep the animator running or ECORE_CALLBACK_CANCEL> to stop the animator and have it be deleted automatically at any time. The callback function is also passed a timeline position parameter with a value between 0.0 (start) to 1.0 (end) to indicate where along the timeline the animator is running.

The following example performs a linear horizontal translation of 500 pixels in 8 seconds:

static Eina_Bool
_my_animation(void *data, double pos)
{
   Evas_Object *obj = data;                       // Get the target object
   int x, y, w, h;                                // Target object geometry
   evas_object_geometry_get(obj, &x, &y, &w, &h); // Get current object position and size attributes
   evas_object_move(obj, 500 * pos, y);           // Linear translation of the Evas object
}
ecore_animator_timeline_add(8, _my_animation, my_evas_object);

Position Mappings

The Ecore_Pos_Map position mappings are used to define the evolution of a given position in accordance with the desired effects. The value ranges from 0.0 to 1.0 on a given timeline. This position variation allows you to apply dynamic changes to any attribute of your Evas object, such as position, width, height, scale, angle, and color.

Ecore supports the following position mappings (with the listed v1 and v2 parameters):

  • ECORE_POS_MAP_LINEAR: linear 0.0 - 1.0
    • v1: not used
    • v2: not used
  • ECORE_POS_MAP_ACCELERATE: start slow, then speed up
    • v1: not used
    • v2: not used
  • ECORE_POS_MAP_DECELERATE: start fast, then slow down
    • v1: not used
    • v2: not used
  • ECORE_POS_MAP_SINUSOIDAL: start slow, speed up, then slow down at the end
    • v1: not used
    • v2: not used
  • ECORE_POS_MAP_ACCELERATE_FACTOR: start slow, then speed up
    • v1: power factor: 0.0 is linear, 1.0 is standard acceleration, 2.0 is a much more pronounced acceleration (squared), 4.0 is cubed, and so on
    • v2: not used
  • ECORE_POS_MAP_DECELERATE_FACTOR: start fast, then slow down
    • v1: power factor: 0.0 is linear, 1.0 is standard deceleration, 2.0 is a much more pronounced deceleration (squared), 3.0 is cubed, and so on
    • v2: not used
  • ECORE_POS_MAP_SINUSOIDAL_FACTOR: start slow, speed up, then slow down at the end
    • v1: power factor: 0.0 is linear, 1.0 is a standard sinusoidal, 2.1 is a much more pronounced sinusoidal (squared), 3.0 is cubed, and so on
    • v2: not used
  • ECORE_POS_MAP_DIVISOR_INTERP: start at gradient * v1, interpolated via power of v2 curve
    • v1: multiplication factor for gradient
    • v2: curve value
  • ECORE_POS_MAP_BOUNCE: start at 0.0, then “drop” like a ball bouncing to the ground at 1.0, and bounce v2 times, with a decay factor of v1
    • v1: bounce decay factor
    • v2: number of bounces
  • ECORE_POS_MAP_SPRING: start at 0.0, then “wobble” like a spring until rest position at 1.0, and wobble v2 times, with a decay factor of v1
    • v1: wobble decay factor
    • v2: number of wobbles

Using Position Mappings

When using the animation callback function, the animator passes a timeline position parameter with a value between 0.0 (start) and 1.0 (end) to indicate where along the timeline the animator is running.

If you want to create a non-linear animation, map the position value to one of several curves and mappings:

double ecore_animator_pos_map(double pos,
                              Ecore_Pos_Map map,
                              double v1,
                              double v2
                             )
  • pos is the current position value, which ranges from 0.0 to 1.0.
  • map is the position mapping you want to apply.
  • v1 is the first parameter (v1) to pass to the position mapping.
  • v2 is the second parameter (v2) to pass to the position mapping.
The v1 and v2 arguments are specific to the chosen position mapping. For example, if you are using ECORE_POS_MAP_BOUNCE, v1 represents the bouncing level and v2 the number of bounces.

The following example performs a transition that bounces 7 times, diminishing by a factor of 1.8 over 5 seconds:

static Eina_Bool
_my_animation_callback(void *data, double pos)
{
   Evas_Object *obj = data;                                           // Get the target object
   int x, y, w, h;                                                    // Target object geometry
   double frame = pos;                                                // Actual position variation
   frame = ecore_animator_pos_map(pos, ECORE_POS_MAP_BOUNCE, 1.8, 7); //Get frame relative position depending on desired effect
   evas_object_geometry_get(obj, &x, &y, &w, &h);                     // Get current object position and size attributes
   evas_object_move(obj, x, 600 * frame);                             // Move the Evas object according to desired effect
 
   return EINA_TRUE;
}
ecore_animator_timeline_add(5, _my_animation_callback, my_evas_object);

Creating an Infinite Animation

If you want the animation to run for an unspecified amount of time, use the ecore_animator_add() function. This function works the same way as the ecore_animation_timeline_add() function, except its interval is based on frame rate. Using frame rate as the basis benefits performance, especially if you define multiple animations, since you may want to have a different timer for each callback function.

Ecore_Animator* ecore_animator_add(Ecore_Task_Cb func,
                                   const void    *data
                                  )
  • func is the callback function that performs the animation.
  • data is the parameter passed to the callback function. This is usually the Evas object to animate.
The function returns a pointer to an Ecore_Animator object, which you can use to adjust the animation.

The following example creates a rectangle sliding from left to right and back again. When the rectangle hits one edge of the screen, it changes direction.

static Eina_Bool
_slide_back_and_forth(void *data)
{
   typedef enum {LEFT, RIGHT} direction_t;    // Direction datatype
   static int x = 0;                          // Initial position
   static direction_t direction = RIGHT;      // Initial direction
   if (x >= 250)
      direction = LEFT;                       // Change direction
   else if (x <= 0)
      direction = RIGHT;                      // Change direction
   if (direction == RIGHT)
      evas_object_move(data, ++x, 350);       // Slide to right
   else if (direction == LEFT)
      evas_object_move(data, --x, 350);       // Slide to left
 
   return EINA_TRUE;
}
int
main(int argc, char *argv[])
{
   // Declarations
 
   // Ecore Evas init
 
   // Draw Evas objects
 
   // Animations go here
   anim = ecore_animator_add(_slide_back_and_forth, rectangle);
   // Ecore main loop
 
   // Free memory
}
To use this code, you have to merge it with the Ecore transition example above.

Chaining Animations

You may sometimes want to delay animating an object. This can be useful if, for example, you want to start an animation only after another one has finished.

You can simply set a delay to the second animation equal to the duration of the first animation:

static int runtime = 5;
static int delay = runtime;
 
static Eina_Bool
_start_fold_animation(void *data)
{
   ecore_animator_timeline_add(runtime, _fold_animation, data);
 
   return EINA_FALSE;
}
 
static Eina_Bool
_start_unfold_animation(void *data)
{
   ecore_animator_timeline_add(runtime, _unfold_animation, data);
 
   return EINA_FALSE;
}
 
_start_fold_animation(my_evas_object);
ecore_timer_add(delay, _start_unfold_animation, my_evas_object);

Pausing and Resuming Animations

You can pause and resume Ecore animations. To pause a running animation, use the ecore_animator_freeze() function:

The parameter is the Ecore_Animator to pause.

ecore_animator_freeze(Ecore_Animator *animator)

To resume the paused animation, use the ecore_animator_thaw() function:

ecore_animation_thaw(Ecore_Animator *animator)

The parameter is the Ecore_Animator to resume.

The following example pauses a transition after 5 seconds and resumes it after 5 more seconds:

static Eina_Bool
_freeze_animation(void *data)
{
   ecore_animator_freeze(data);
 
   return EINA_FALSE;
}
 
static Eina_Bool
_thaw_animation(void *data)
{
   ecore_animator_thaw(data);
 
   return EINA_FALSE;
}
 
ecore_timer_add(5, _freeze_animation, animator);
ecore_timer_add(10, _thaw_animation, animator);

Freeing Up Memory

When you create an animation that does not have a timeout, you will have to manually free up the memory allocated to the Ecore_Animator object. By comparison, if the animation has a timeout, Ecore implements the mechanisms to automatically delete the animator from the list of pointers: When your animation callback returns 0 or ECORE_CALLBACK_CANCEL, the animator manager takes care of freeing up the allocated memory.

To manually free up the memory, delete the pointer by using the ecore_animator_del() function:

ecore_animator_del(Ecore_Animator *animator)

The argument is the Ecore_Animator whose memory allocation to free up.

Regardless of the type of animation, it is good practice to always ensure that the allocated memory is freed up before the program exits:

if (animator != NULL)
   ecore_animator_del(animator);

Frametime

In most cases, you will want to use the default timer ECORE_ANIMATOR_SOURCE_TIMER. This timer ticks every “frametime” seconds and allows you to perform transitions within a predefined timeline. The timer uses the system clock to tick over every Nth second, with the default being 1/30th of a second.

To tweak performance, you can change the frametime value:

The argument is the new frametime value.

ecore_animator_frametime_set(double frametime)
Too small a value can cause performance issues, whereas too high a value can cause your animation to seem jerky.

If you want to get the current frametime value, use the ecore_animator_frametime_get() function.

Custom Timer

You may want to specify a custom timer to match your animation to third-party events. For example, the filling speed of a progress bar will mainly depend on the time it takes for a task to complete and the velocity at which the remaining time estimation evolves. This kind of animation requires you to use a custom timer.

To use a custom timer, first set ECORE_ANIMATOR_SOURCE_CUSTOM as the timer source, and then drive the timer based on an input tick source (such as another application via IPC or a vertical blanking interrupt):

void ecore_animator_custom_source_tick_begin_callback_set(Ecore_Cb func,
                                                     const void *data
                                                    )
  • func is the callback function to call on the tick start.
  • data is the data to pass to the callback function.
void ecore_animator_custom_source_tick_end_callback_set(Ecore_Cb func,
                                                        const void *data
                                                       )
  • func is the callback function to call on the tick end.
  • data is the data to pass to the callback function to set the functions that will be called to start and stop the ticking source.

Next, trigger a tick over one frame:

ecore_animator_custom_tick(void)

The following example supposes a progress bar that will be refreshed every time some progress occurs:

ecore_animator_source_set(ECORE_ANIMATOR_SOURCE_CUSTOM);
void _on_progress_update()
{
   // Called when some progress occurs
   ecore_animator_custom_tick(); // Tick (next frame in progress bar animation)
}

Finally, to get the current animator source, use the ecore_animator_source_get() function.