Mobile native Wearable native

Ecore Animator: Creating Ecore Animations

This tutorial demonstrates how you can use Ecore animators to simplify the creation of animations. Using Ecore animators, you can manually create your own animations by changing and manipulating Evas object attributes. Ecore animators work like timers, running callback functions over a given duration (an animation timeline).

Warm-up

Become familiar with the Ecore, Elementary, and Evas API basics by learning about:

Setting Up the Application

In this part of the tutorial, we create a simple application that manipulates and animates an Evas object. We use a "Basic UI Application" as the basis for the application.

First, we set up the UI components we are going to use in the application:

typedef struct appdata 
{
   // Main window
   Evas_Object *win;
   // Application title
   Evas_Object *label;

   // Buttons
   Evas_Object *bt1;
   Evas_Object *bt2;
   Evas_Object *bt3;

   // Animation target
   Evas_Object *target;
} appdata_s;

We then create the actual UI components in the create_base_gui() function, starting with the main window and application title:

// Main window
ad->win = elm_win_util_standard_add(PACKAGE, PACKAGE);
elm_win_autodel_set(ad->win, EINA_TRUE);

if (elm_win_wm_rotation_supported_get(ad->win)) 
{
   int rots[4] = { 0, 90, 180, 270 };
   elm_win_wm_rotation_available_rotations_set(ad->win, (const int *)(&rots), 4);
}

evas_object_smart_callback_add(ad->win, "delete,request", win_delete_request_cb, NULL);

// Application title
ad->label = elm_label_add(ad->win);
elm_object_text_set(ad->label, "Ecore Animator Tutorial");
evas_object_size_hint_weight_set(ad->label, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_win_resize_object_add(ad->win, ad->label);
evas_object_show(ad->label);

Next, we create the animation target, which is an Evas object of type Elm_Image. The image that is used in the source code is stored in the res/images folder of the application. The image that is used in the EDC file is stored in the edje/images folder.

// Animation target
// Setting the image path
char buf[PATH_MAX];
snprintf(buf, sizeof(buf), "/opt/usr/apps/%s/res/images/tizen-logo.png", PACKAGE);
// Adding the image
ad->target = elm_image_add(ad->win);
// Setting the image path
if (!elm_image_file_set(ad->target, buf, NULL))
   printf("error: could not load image \"%s\"\n", buf);
evas_object_size_hint_weight_set(ad->target, EVAS_HINT_FILL, EVAS_HINT_FILL);
// Moving the image
evas_object_move(ad->target, 130, 100);
// Resizing the image
evas_object_resize(ad->target, 200, 100);
// Showing the image
evas_object_show(ad->target);

All images are available in the /opt/usr/apps/<PACKAGE_NAME>/res/images folder of the application. The package name is defined at the begin of the application code:

#if !defined(PACKAGE)
#define PACKAGE "org.tizen.ecoreanimator"
#endif

The image path is set by calling the elm_image_file_set() function. This function takes as arguments the ad->target Evas object and the path of the image file, built with the snprintf() function and stored in a buffer.

// Setting the image path
char buf[PATH_MAX];
snprintf(buf, sizeof(buf), "/opt/usr/apps/%s/res/images/tizen-logo.png", PACKAGE);
// Adding the image
ad->target = elm_image_add(ad->win);
// Setting the image path
if (!elm_image_file_set(ad->target, buf, NULL))
   printf("error: could not load image \"%s\"\n", buf);
evas_object_size_hint_weight_set(ad->target, EVAS_HINT_FILL, EVAS_HINT_FILL);

The remaining code moves and resizes the image:

//Moving the image
evas_object_move(ad->target, 130, 100);
//Resizing the image
evas_object_resize(ad->target, 200, 100);
//Showing the image

Creating a Rotation Effect

After the animation target is created, we can create the first button and the associated rotation animation:

// Button 1
ad->bt1 = elm_button_add(ad->win);
elm_object_text_set(ad->bt1, "Rotate");
evas_object_size_hint_weight_set(ad->bt1, EVAS_HINT_FILL, EVAS_HINT_FILL);
evas_object_move(ad->bt1, 25, 0);
evas_object_resize(ad->bt1, 90, 70);
evas_object_smart_callback_add(ad->bt1, "clicked", _btn_rotate_cb, ad);
evas_object_show(ad->bt1);

We create the button for triggering the rotation effect. The button is placed and resized, and associated with the _btn_rotate_cb() callback function. This callback function calls the animation on the animation target.

static void _btn_rotate_cb(void *data, Evas_Object *btn, void *ev)
{
   appdata_s *ad = data;

   ecore_animator_timeline_add(1, _do_rotate, ad->target);
}

In this callback function, we create an Ecore animator timeline using the ecore_animator_timeline_add() function. This function adds an animator that runs for a limited time: we run the _do_rotate() animation callback function for 1 second on the ad->target Evas object.

Next, we write the animation callback function that actually runs the animation. This callback is an Ecore_Timeline_Cb function, meaning it returns an Eina_Bool value and takes as arguments some data and the current position along the animation timeline (pos).

To create the rotation animation, we use an Evas Map. The map handles the necessary map points and allows you to manipulate the target Evas object on the X, Y, and Z axes.

static Eina_Bool
_do_rotate(void *data, double pos)
{
   // Get the animation target
   Evas_Object *obj = data;
   // Declaration of an `Evas_Map`
   Evas_Map *m;
   // Variables to store the target size and position
   int x, y, w, h;

   // Getting the size and position of the target
   evas_object_geometry_get(obj, &x, &y, &w, &h);
   // Creation of an `Evas_Map` of 4 points
   m = evas_map_new(4);
   // Populate source and destination map points to match exactly object.
   evas_map_util_points_populate_from_object(m, obj);
   // Create a rotation of 360° with x+(w/2) "x" center and y +(h/2) "y" center.
   evas_map_util_rotate(m, 360.0 * pos, x + (w / 2), y + (h / 2));
   // Setting the object to "animate" in the `Evas_Map`
   evas_object_map_set(obj, m);
   // Starting the Animation
   evas_object_map_enable_set(obj, EINA_TRUE);
   // Free used memory
   evas_map_free(m);

   return EINA_TRUE;
}

In the animation callback function, we first declare the Evas Map. To implement the rotation, we need to set an X and Y center, so we create 4 integer variables to store the size and position of the target. This information is provided by the evas_object_geometry_get() function, which returns the X and Y coordinates and the weight and height of the target Evas object. Now we have all the required data to build the animation.

We create an Evas Map consisting of four points, and we populate these points with the animation target:

// Creation of an Evas_Map of 4 points
m = evas_map_new(4);
// Populate source and destination map points to match the object.
evas_map_util_points_populate_from_object(m, obj);

Now we can define the rotation using the evas_map_util_rotate() function:

// Create a rotation of 360° with x+(w/2) "x" center and y +(h/2) "y" center.
evas_map_util_rotate(m, 360.0 * pos, x + (w / 2), y + (h / 2));

The animation callback function will be called at several points along the timeline, which is why we multiply the rotation angle (360°) by the timeline position (pos) to get the actual animation angle. If we do not do this, we will never see the animation take place. We then join the target object to the map and run the animation:

// Setting the object to "animate" in the Evas Map
evas_object_map_set(obj, m);
// Starting the Animation
evas_object_map_enable_set(obj, EINA_TRUE);

Each call to the animation callback function will rotate the object (360 * timeline position) degrees.

Finally, we free up the memory allocated to the Evas Map:

// Free used memory
evas_map_free(m);

Creating a Zoom Effect

The next animation is a zoom, for which we also use an Evas Map.

First, we create the button in the create_base_gui() function:

// Button 2
ad->bt2 = elm_button_add(ad->win);
elm_object_text_set(ad->bt2, "Zoom");
evas_object_size_hint_weight_set(ad->bt2, EVAS_HINT_FILL, EVAS_HINT_FILL);
evas_object_move(ad->bt2, 315, 0);
evas_object_resize(ad->bt2, 90, 70);
evas_object_smart_callback_add(ad->bt2, "clicked", _btn_zoom_cb, ad);
evas_object_show(ad->bt2);

We then create the button callback function with a new timeline:

static void _btn_zoom_cb(void *data, Evas_Object *btn, void *ev)
{
   appdata_s *ad = data;

   ecore_animator_timeline_add(1, _do_zoom, ad->target);
}

Next, we create the _do_zoom() animation callback function, which is almost identical to the _do_rotate() callback function, except that we use the evas_map_util_zoom() function to create the animation:

static Eina_Bool
_do_zoom(void *data, double pos)
{
   Evas_Object *obj = data;
   Evas_Map *m;
   int x, y, w, h;

   evas_object_geometry_get(obj, &x, &y, &w, &h);
   m = evas_map_new(4);
   evas_map_util_points_populate_from_object(m, obj);
   evas_map_util_zoom(m, 2 * pos, 2 * pos, x , y);
   evas_object_map_set(obj, m);
   evas_object_map_enable_set(obj, EINA_TRUE);
   evas_map_free(m);

   return EINA_TRUE;
}

The evas_map_util_zoom() function takes the following arguments:

  • The map to change
  • The horizontal zoom factor
  • The vertical zoom factor
  • The horizontal position (X coordinate) of the zooming center
  • The vertical position (Y coordinate) of the zooming center

Here, we use a horizontal and vertical zoom factor of 2, and the X and Y coordinates of the target as the horizontal and vertical center coordinates.

The _do_zoom() callback function is called at several points along the animation timeline, which is why we multiply the horizontal and vertical zoom factor values by the timeline position. Each call will zoom more than the previous one, thereby creating the animation effect.

Creating a 3D Rotation Effect

The last animation is a 3D rotation. For this one, we are going to rotate the Evas object on all three axes (X, Y, Z).

First, we create the button and its callback function:

// Button 3
ad->bt3 = elm_button_add(ad->win);
elm_object_text_set(ad->bt3, "3D");
evas_object_size_hint_weight_set(ad->bt3, EVAS_HINT_FILL, EVAS_HINT_FILL);
evas_object_move(ad->bt3, 170, 0);
evas_object_resize(ad->bt3, 90, 70);
evas_object_smart_callback_add(ad->bt3, "clicked", _btn_3d_cb, ad);
evas_object_show(ad->bt3);
static void _btn_3d_cb(void *data, Evas_Object *btn, void *ev)
{
   appdata_s *ad = data;

   ecore_animator_timeline_add(1, _do_3d, ad->target);
}

Next, we create the _do_3d() animation callback function, which is very similar to the rotate and zoom callback functions. To create the animation, we use the evas_map_util_3d_rotate() function, which allows you to rotate any Evas object on all three axes.

static Eina_Bool
_do_3d(void *data, double pos)
{
   Evas_Object *obj = data;
   Evas_Map *m;
   int x, y, w, h;

   evas_object_geometry_get(obj, &x, &y, &w, &h);
   m = evas_map_new(4);
   evas_map_util_points_populate_from_object(m, obj);
   evas_map_util_3d_rotate(m, pos * 360, pos * 360, pos * 360, x + (w / 3), y + 60, 0);
   evas_object_map_set(obj, m);
   evas_object_map_enable_set(obj, EINA_TRUE);
   evas_map_free(m);

   return EINA_TRUE;
}

The evas_map_util_3d_rotate() function takes the following arguments:

  • The map to change
  • The angle (0-360°) to rotate around the X axis
  • The angle (0-360°) to rotate around the Y axis
  • The angle (0-360°) to rotate around the Z axis
  • The X coordinate of the rotation center
  • The Y coordinate of the rotation center
  • The Z coordinate of the rotation center

Here, we rotate 360 degrees around each axis. The horizontal (X) rotation center is the X position of the target plus its width divided by 2. The vertical (Y) rotation center is the Y position of the target plus 60. The Z rotation center is 0.

As with the rotation and zoom animations, we multiply the angles by the timeline position to gently rotate the target on each call to the _do_3d() callback function along the timeline.

Creating Drop and Bounce Effects

To finish, we add drop and bounce effects to our buttons at application start. To do this, we create one timeline per button after creating the buttons in the create_base_gui() function:

ecore_animator_timeline_add(2, _do_drop, ad->bt1);
ecore_animator_timeline_add(2.3, _do_drop, ad->bt2);
ecore_animator_timeline_add(2.5, _do_drop, ad->bt3);

We call the same _do_drop() animation callback function for each timeline. In this callback, instead of using an Evas Map, we simply change the position of the target using the evas_object_move() function:

static Eina_Bool
_do_drop(void *data, double pos)
{
   Evas_Object *obj = data;
   int x, y, w, h;
   double frame = pos;
   frame = ecore_animator_pos_map(pos, ECORE_POS_MAP_BOUNCE, 2, 4);

   evas_object_geometry_get(obj, &x, &y, &w, &h);
   evas_object_move(obj, x, 600 * frame);

   return EINA_TRUE;
}

To get the bounce effect, we use the ecore_animator_pos_map() function, which maps an input position from 0.0 to 1.0 along the timeline to a position on a different curve. The curve can be of different types, such as LINEAR, SINUSOIDAL, and BOUNCE. This function takes the following arguments:

  • The input position to map
  • The mapping to use (LINEAR, SINUSOIDAL, and so on)
  • v1, which is the first parameter used by the mapping
  • v2, which is the second parameter used by the mapping

The ECORE_POS_MAP_BOUNCE map we use starts at 0.0, then drops like a ball bouncing to the ground at 1.0, bouncing v2 times with a decay factor of v1. Here, we bounce 4 times with a decay factor of 2:

frame = ecore_animator_pos_map(pos, ECORE_POS_MAP_BOUNCE, 2, 4);

This frame is used in the move function to create the animation. The value increases on each _do_drop() call along the timeline, which produces a nice drop of the buttons from their initial position to 600 pixels on the vertical axis.

Go to top