introduce efl_canvas_object_animation
authorMarcel Hollerbach <mail@marcel-hollerbach.de>
Thu, 7 Nov 2019 10:24:21 +0000 (11:24 +0100)
committerJaehyun Cho <jae_hyun.cho@samsung.com>
Thu, 21 Nov 2019 06:52:27 +0000 (15:52 +0900)
this brings the animation to the canvas object. All the controls of the
animation now do now require a player object anymore, you can just use
the API that is directly on the Efl.Canvas.Object object.

wip animation player replacement

Differential Revision: https://phab.enlightenment.org/D10615

src/examples/evas/efl-canvas-animation.c [new file with mode: 0644]
src/examples/evas/meson.build
src/lib/evas/Evas_Eo.h
src/lib/evas/canvas/efl_canvas_object.eo
src/lib/evas/canvas/efl_canvas_object_animation.c [new file with mode: 0644]
src/lib/evas/canvas/efl_canvas_object_animation.eo [new file with mode: 0644]
src/lib/evas/canvas/meson.build
src/tests/evas/evas_test_object.c

diff --git a/src/examples/evas/efl-canvas-animation.c b/src/examples/evas/efl-canvas-animation.c
new file mode 100644 (file)
index 0000000..2cc2355
--- /dev/null
@@ -0,0 +1,179 @@
+/**
+ * Example of animation in efl canvas
+ *
+ * You'll need at least one engine built for it (excluding the buffer
+ * one). See stdout/stderr for output.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <Ecore.h>
+#include <Ecore_Evas.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define WIDTH  (640)
+#define HEIGHT (480)
+
+struct example_data
+{
+   Ecore_Evas  *ee;
+   Evas        *evas;
+   Evas_Object *bg, *scale;
+   double current_speed;
+};
+
+static struct example_data d;
+
+static Evas_Object * /* new rectangle to be put in the box */
+_new_rectangle_add(Evas *e)
+{
+   Efl_Canvas_Rectangle *rect = efl_add(EFL_CANVAS_RECTANGLE_CLASS, e);
+
+   efl_gfx_entity_size_set(rect, EINA_SIZE2D(10, 10));
+   efl_gfx_color_set(rect, 0, 255, 0, 255);
+   efl_gfx_entity_visible_set(rect, EINA_TRUE);
+
+   return rect;
+}
+
+/* use the following commands to interact with this example - 'h' is
+ * the key for help */
+static void
+_on_keydown(void *data EINA_UNUSED, const Efl_Event *ev)
+{
+   if (strcmp(efl_input_key_sym_get(ev->info) , "r") == 0)
+     {
+        Efl_Canvas_Animation *animation = efl_canvas_object_animation_get(d.scale);
+        double current_pos = efl_canvas_object_animation_progress_get(d.scale);
+        d.current_speed *= -1;
+        efl_canvas_object_animation_start(d.scale, animation, d.current_speed, 1.0 - current_pos);
+     }
+   if (strcmp(efl_input_key_sym_get(ev->info), "p") == 0)
+     {
+        efl_canvas_object_animation_pause_set(d.scale, !efl_canvas_object_animation_pause_get(d.scale));
+     }
+}
+
+static void
+_on_delete(Ecore_Evas *ee EINA_UNUSED)
+{
+   ecore_main_loop_quit();
+}
+
+static void /* adjust canvas' contents on resizes */
+_canvas_resize_cb(Ecore_Evas *ee)
+{
+   int w, h;
+
+   ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
+
+   efl_gfx_entity_geometry_set(d.bg, EINA_RECT(0, 0, w, h));
+}
+
+static void
+print_help(void)
+{
+   printf("evas-animation example\n Press r to reverse the animation of the red rect.\n Press p to pause the animation of the red rect.\n");
+}
+
+int
+main(void)
+{
+   if (!ecore_evas_init())
+     return EXIT_FAILURE;
+
+   /* this will give you a window with an Evas canvas under the first
+    * engine available */
+   d.ee = ecore_evas_new(NULL, 0, 0, WIDTH, HEIGHT, NULL);
+   if (!d.ee)
+     goto panic;
+
+   print_help();
+
+   ecore_evas_name_class_set(d.ee, "Evas Animation example", "Evas Animation Example");
+   ecore_evas_callback_delete_request_set(d.ee, _on_delete);
+   ecore_evas_callback_resize_set(d.ee, _canvas_resize_cb);
+   ecore_evas_show(d.ee);
+
+   d.evas = ecore_evas_get(d.ee);
+
+   d.bg = _new_rectangle_add(d.evas);
+   efl_gfx_color_set(d.bg, 255, 255, 255, 255);
+   efl_gfx_entity_visible_set(d.bg, EINA_TRUE);
+   efl_canvas_object_key_focus_set(d.bg, EINA_TRUE);
+   efl_event_callback_add(d.bg, EFL_EVENT_KEY_DOWN, _on_keydown, NULL);
+
+   _canvas_resize_cb(d.ee);
+
+   Evas_Object *scale_rect = _new_rectangle_add(d.evas);
+   efl_gfx_entity_geometry_set(scale_rect, EINA_RECT(50, 50, 50, 50));
+   efl_canvas_object_animation_start(scale_rect,
+    efl_new(EFL_CANVAS_ANIMATION_SCALE_CLASS,
+      efl_animation_scale_set(efl_added, EINA_VECTOR2(1.0, 1.0), EINA_VECTOR2(3.0, 3.0), scale_rect, EINA_VECTOR2(0.5, 0.5)),
+      efl_animation_start_delay_set(efl_added, 5.0),
+      efl_animation_duration_set(efl_added, 2.0),
+      efl_animation_repeat_count_set(efl_added, EFL_ANIMATION_REPEAT_INFINITE)
+    ),
+   1.0, 0.0);
+
+   Evas_Object *scale_rect2 = _new_rectangle_add(d.evas);
+   efl_gfx_entity_geometry_set(scale_rect2, EINA_RECT(50, 200, 50, 50));
+   efl_canvas_object_animation_start(scale_rect2,
+    efl_new(EFL_CANVAS_ANIMATION_SCALE_CLASS,
+      efl_animation_scale_set(efl_added, EINA_VECTOR2(1.0, 1.0), EINA_VECTOR2(3.0, 3.0), scale_rect2, EINA_VECTOR2(0.5, 0.5)),
+      efl_animation_duration_set(efl_added, 2.0),
+      efl_animation_repeat_count_set(efl_added, EFL_ANIMATION_REPEAT_INFINITE),
+      efl_animation_repeat_mode_set(efl_added, EFL_CANVAS_ANIMATION_REPEAT_MODE_REVERSE)
+    ),
+   1.0, 0.0);
+
+   Evas_Object *scale_rect3 = _new_rectangle_add(d.evas);
+   efl_gfx_entity_geometry_set(scale_rect3, EINA_RECT(50, 350, 50, 50));
+   efl_canvas_object_animation_start(scale_rect3,
+    efl_new(EFL_CANVAS_ANIMATION_SCALE_CLASS,
+      efl_animation_scale_set(efl_added, EINA_VECTOR2(1.0, 1.0), EINA_VECTOR2(3.0, 3.0), scale_rect3, EINA_VECTOR2(0.5, 0.5)),
+      efl_animation_duration_set(efl_added, 2.0),
+      efl_animation_repeat_count_set(efl_added, 3),
+      efl_animation_repeat_mode_set(efl_added, EFL_CANVAS_ANIMATION_REPEAT_MODE_REVERSE)
+    ),
+   1.0, 0.0);
+
+   Evas_Object *scale_rect4 = _new_rectangle_add(d.evas);
+   efl_gfx_entity_geometry_set(scale_rect4, EINA_RECT(200, 50, 50, 50));
+   efl_canvas_object_animation_start(scale_rect4,
+    efl_new(EFL_CANVAS_ANIMATION_SCALE_CLASS,
+      efl_animation_scale_set(efl_added, EINA_VECTOR2(1.0, 1.0), EINA_VECTOR2(3.0, 3.0), scale_rect4, EINA_VECTOR2(0.5, 0.5)),
+      efl_animation_duration_set(efl_added, 2.0),
+      efl_animation_final_state_keep_set(efl_added, EINA_TRUE)
+    ),
+   1.0, 0.0);
+
+
+   Evas_Object *scale_rect5 = d.scale = _new_rectangle_add(d.evas);
+   efl_gfx_color_set(scale_rect5, 255, 0, 0, 255);
+   efl_gfx_entity_geometry_set(scale_rect5, EINA_RECT(200, 200, 50, 50));
+   efl_canvas_object_animation_start(scale_rect5,
+    efl_new(EFL_CANVAS_ANIMATION_SCALE_CLASS,
+      efl_animation_scale_set(efl_added, EINA_VECTOR2(1.0, 1.0), EINA_VECTOR2(5.0, 5.0), scale_rect5, EINA_VECTOR2(0.5, 0.5)),
+      efl_animation_duration_set(efl_added, 5.0),
+      efl_animation_repeat_count_set(efl_added, EFL_ANIMATION_REPEAT_INFINITE)
+    ),
+   1.0, 0.0);
+   d.current_speed = 1.0;
+
+   ecore_main_loop_begin();
+   ecore_evas_shutdown();
+   return 0;
+
+panic:
+   fprintf(stderr, "error: Requires at least one Evas engine built and linked"
+                   " to ecore-evas for this example to run properly.\n");
+   return -2;
+}
+
index 659f9a5..93539e7 100644 (file)
@@ -50,6 +50,7 @@ examples = [
   'evas-vg-batman',
   'evas-vg-simple',
   'evas-vg-json',
+  'efl-canvas-animation',
 ]
 
 foreach example : examples
index 5a725cc..13ecde5 100644 (file)
@@ -223,7 +223,7 @@ struct _Efl_Canvas_Object_Animation_Event
 #include "gesture/efl_gesture_events.eo.h"
 
 #include "canvas/efl_canvas_object.eo.h"
-
+#include "canvas/efl_canvas_object_animation.eo.h"
 #include "canvas/efl_canvas_animation.eo.h"
 #include "canvas/efl_canvas_animation_alpha.eo.h"
 #include "canvas/efl_canvas_animation_rotate.eo.h"
index 785c496..dbee3db 100644 (file)
@@ -11,7 +11,7 @@ struct Efl.Event_Animator_Tick {
 
 abstract Efl.Canvas.Object extends Efl.Loop_Consumer implements Efl.Gfx.Entity, Efl.Gfx.Color, Efl.Gfx.Stack,
                             Efl.Input.Interface, Efl.Gfx.Hint,
-                            Efl.Gfx.Mapping, Efl.Canvas.Pointer, Efl.Gesture.Events
+                            Efl.Gfx.Mapping, Efl.Canvas.Pointer, Efl.Gesture.Events, Efl.Canvas.Object_Animation
 {
    [[Efl canvas object abstract class
 
diff --git a/src/lib/evas/canvas/efl_canvas_object_animation.c b/src/lib/evas/canvas/efl_canvas_object_animation.c
new file mode 100644 (file)
index 0000000..b86912d
--- /dev/null
@@ -0,0 +1,197 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "evas_common_private.h"
+#include "evas_private.h"
+#include "efl_canvas_object_animation.eo.h"
+#include <Ecore.h>
+
+#define MY_CLASS EFL_CANVAS_OBJECT_ANIMATION_MIXIN
+
+
+typedef struct
+{
+   Efl_Canvas_Animation *animation;
+   double speed;
+   double progress;
+   double run_start_time;
+   double start_pos;
+   int remaining_repeats;
+   Efl_Loop_Timer *timer;
+   Eina_Bool pause_state : 1;
+} Efl_Canvas_Object_Animation_Indirect_Data;
+
+typedef struct
+{
+   Efl_Canvas_Object_Animation_Indirect_Data *in;
+} Efl_Canvas_Object_Animation_Data;
+
+static void _end(Efl_Canvas_Object_Animation *obj, Efl_Canvas_Object_Animation_Data *pd);
+
+
+static void
+_animator_cb(void *data, const Efl_Event *ev EINA_UNUSED)
+{
+   Eo *obj = data;
+   Efl_Canvas_Object_Animation_Data *pd = efl_data_scope_get(obj, MY_CLASS);
+   double duration, elapsed_time, vector, current;
+
+   EINA_SAFETY_ON_NULL_RETURN(pd->in);
+   current = ecore_loop_time_get();
+   EINA_SAFETY_ON_FALSE_RETURN(pd->in->run_start_time <= current);
+
+   duration = efl_animation_duration_get(pd->in->animation) / pd->in->speed;
+   elapsed_time = current - pd->in->run_start_time;
+   vector = elapsed_time / duration;
+
+   /* When animation player starts, _animator_cb() is called immediately so
+    * both elapsed time and progress are 0.0.
+    * Since it is the beginning of the animation if progress is 0.0, the
+    * following codes for animation should be executed. */
+   if (pd->in->speed < 0.0)
+     vector += 1.0;
+   pd->in->progress = CLAMP(0.0, vector, 1.0);
+
+   /* The previously applied map effect should be reset before applying the
+    * current map effect. Otherwise, the incrementally added map effects
+    * increase numerical error. */
+   efl_gfx_mapping_reset(obj);
+   efl_animation_apply(pd->in->animation, pd->in->progress, obj);
+
+   efl_event_callback_call(obj, EFL_CANVAS_OBJECT_ANIMATION_EVENT_ANIMATION_PROGRESS_UPDATED, &pd->in->progress);
+
+   //Not end. Keep going.
+   if ((pd->in->speed < 0 && EINA_DBL_EQ(pd->in->progress, 0)) ||
+       (pd->in->speed > 0 && EINA_DBL_EQ(pd->in->progress, 1.0)))
+     {
+        //Repeat animation
+        if ((efl_animation_repeat_count_get(pd->in->animation) == EFL_ANIMATION_REPEAT_INFINITE) ||
+            (pd->in->remaining_repeats > 0))
+          {
+             pd->in->remaining_repeats--;
+
+             if (efl_animation_repeat_mode_get(pd->in->animation) == EFL_CANVAS_ANIMATION_REPEAT_MODE_REVERSE)
+               pd->in->speed *= -1;
+
+             pd->in->run_start_time = current;
+          }
+        else
+          {
+             efl_canvas_object_animation_stop(obj);
+          }
+     }
+}
+
+static void
+_end(Efl_Canvas_Object_Animation *obj, Efl_Canvas_Object_Animation_Data *pd)
+{
+   EINA_SAFETY_ON_NULL_RETURN(pd->in);
+   efl_event_callback_del(obj, EFL_CANVAS_OBJECT_EVENT_ANIMATOR_TICK, _animator_cb, obj);
+}
+
+static void
+_start(Efl_Canvas_Object_Animation *obj, Efl_Canvas_Object_Animation_Data *pd, double delay)
+{
+   EINA_SAFETY_ON_NULL_RETURN(pd->in);
+   pd->in->run_start_time = ecore_loop_time_get() - efl_animation_duration_get(pd->in->animation)*delay;
+   efl_event_callback_add(obj, EFL_CANVAS_OBJECT_EVENT_ANIMATOR_TICK, _animator_cb, obj);
+   _animator_cb(obj, NULL);
+}
+
+static Eina_Value
+_start_fcb(Eo *o, void *data EINA_UNUSED, const Eina_Value v)
+{
+   Efl_Canvas_Object_Animation_Data *pd = efl_data_scope_safe_get(o, MY_CLASS);
+   if (!pd->in) return v; //animation was stopped before anything started
+   _start(o, pd, pd->in->start_pos);
+   return v;
+}
+
+EOLIAN static Efl_Canvas_Animation*
+_efl_canvas_object_animation_animation_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Object_Animation_Data *pd)
+{
+   if (!pd->in) return NULL;
+   return pd->in->animation;
+}
+
+EOLIAN static double
+_efl_canvas_object_animation_animation_progress_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Object_Animation_Data *pd)
+{
+   if (pd->in && pd->in->animation)
+     return (pd->in->speed < 0) ? fabs(1.0 - pd->in->progress) : pd->in->progress;
+   else
+     return -1.0;
+}
+
+EOLIAN static void
+_efl_canvas_object_animation_animation_pause_set(Eo *obj, Efl_Canvas_Object_Animation_Data *pd, Eina_Bool pause)
+{
+   EINA_SAFETY_ON_NULL_RETURN(pd->in);
+
+   if (pd->in->pause_state == pause) return;
+
+   if (pause)
+     _end(obj, pd);
+   else
+     _start(obj, pd,(pd->in->speed < 0) ? 1.0 - pd->in->progress : pd->in->progress);
+   pd->in->pause_state = pause;
+}
+
+EOLIAN static Eina_Bool
+_efl_canvas_object_animation_animation_pause_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Object_Animation_Data *pd)
+{
+   if (!pd->in) return EINA_FALSE;
+
+   return pd->in->pause_state;
+}
+
+EOLIAN static void
+_efl_canvas_object_animation_animation_start(Eo *obj, Efl_Canvas_Object_Animation_Data *pd, Efl_Canvas_Animation *animation, double speed, double start_pos)
+{
+   Efl_Canvas_Object_Animation_Indirect_Data *in;
+   if (pd->in && pd->in->animation)
+     efl_canvas_object_animation_stop(obj);
+   EINA_SAFETY_ON_FALSE_RETURN(!pd->in);
+   in = pd->in = calloc(1, sizeof(Efl_Canvas_Object_Animation_Indirect_Data));
+
+   EINA_SAFETY_ON_NULL_RETURN(animation);
+   EINA_SAFETY_ON_FALSE_RETURN(start_pos >= 0.0 && start_pos <= 1.0);
+   EINA_SAFETY_ON_FALSE_RETURN(!EINA_DBL_EQ(speed, 0.0));
+   EINA_SAFETY_ON_FALSE_RETURN(efl_playable_seekable_get(animation));
+
+   in->pause_state = EINA_FALSE;
+   in->animation = efl_ref(animation);
+   in->remaining_repeats = efl_animation_repeat_count_get(animation); // -1 because one run is already going on
+   in->speed = speed;
+   in->start_pos = start_pos;
+   efl_event_callback_call(obj, EFL_CANVAS_OBJECT_ANIMATION_EVENT_ANIMATION_CHANGED, in->animation);
+
+   if (efl_animation_start_delay_get(animation) > 0.0)
+     {
+        Eina_Future *f = efl_loop_timeout(efl_loop_get(obj), efl_animation_start_delay_get(animation));
+
+        efl_future_then(obj, f, .success = _start_fcb);
+     }
+   else
+     _start(obj, pd, start_pos);
+}
+
+EOLIAN static void
+_efl_canvas_object_animation_animation_stop(Eo *obj, Efl_Canvas_Object_Animation_Data *pd)
+{
+   if (!pd->in) return;
+
+   if (!efl_animation_final_state_keep_get(pd->in->animation))
+     efl_gfx_mapping_reset(obj);
+   _end(obj, pd);
+   efl_unref(pd->in->animation);
+   pd->in->animation = NULL;
+
+   efl_event_callback_call(obj, EFL_CANVAS_OBJECT_ANIMATION_EVENT_ANIMATION_CHANGED, pd->in->animation);
+
+   free(pd->in);
+   pd->in = NULL;
+}
+
+#include "efl_canvas_object_animation.eo.c"
diff --git a/src/lib/evas/canvas/efl_canvas_object_animation.eo b/src/lib/evas/canvas/efl_canvas_object_animation.eo
new file mode 100644 (file)
index 0000000..49146a6
--- /dev/null
@@ -0,0 +1,60 @@
+mixin @beta Efl.Canvas.Object_Animation requires Efl.Object
+{
+  methods {
+    @property animation {
+      [[The animation that is currently played on the canvas object.
+
+        $null in case that there is no animation running.]]
+      get {
+
+      }
+      values {
+        animation : Efl.Canvas.Animation; [[The animation which is currently applied on this object.]]
+      }
+    }
+    @property animation_progress {
+      [[The current progress of the animation, between 0.0 and 1.0.
+
+        Even if the animation is going backwards (speed < 0.0). the progress will still go from 0.0 to 1.0.
+
+        If there is no animation going on, this will return -1.0.
+      ]]
+      get {
+
+      }
+      values {
+        progress : double; [[The progress the animation applying is currently in.]]
+      }
+    }
+    @property animation_pause {
+      [[Pause the animation
+
+        The animation will not be unset. When $pause is unset, the animation will be resumed at the same progress it has right now.
+      ]]
+      values {
+        pause : bool;
+      }
+    }
+    animation_start {
+      [[Start a new animation.
+
+        If there is a animation going on, this is stopped. The previous @.animation object will be replaced. The lifetime is adjusted accordingly.
+      ]]
+      params {
+        animation : Efl.Canvas.Animation @move; [[The animation to start. When not needed anymore, the reference that was passed is given up.]]
+        speed : double; [[The speed of the playback. `1.0` is normal playback. Negative values mean backward playback.]]
+        starting_progress : double; [[The progress to start, must be between 0.0 and 1.0.]]
+      }
+    }
+    animation_stop {
+      [[Stop the animation.
+
+        After this call, @.animation will return $null. The reference that was taken during @.animation_start will be given up on.
+      ]]
+    }
+  }
+  events {
+      animation,changed: Efl.Canvas.Animation; [[The animation object got changed.]]
+      animation_progress,updated : double; [[The animation progress got changed.]]
+  }
+}
index 1edaeca..c13d333 100644 (file)
@@ -56,6 +56,7 @@ pub_eo_files = [
   'efl_gfx_mapping.eo',
   'efl_canvas_event_grabber.eo',
   'efl_canvas_text.eo',
+  'efl_canvas_object_animation.eo',
 ]
 
 evas_canvas_eo_files = pub_eo_files
@@ -207,6 +208,7 @@ evas_src += files([
   'evas_canvas3d_primitive.c',
   'evas_canvas3d_node_callback.h',
   'evas_canvas3d_eet.c',
+  'efl_canvas_object_animation.c',
   ])
 
 evas_include_directories += include_directories('.')
index d2de52a..7024488 100644 (file)
@@ -5,10 +5,17 @@
 #include <stdio.h>
 
 #include <Evas.h>
+#define EFL_LOOP_PROTECTED //needed to set the loop time, we need that to simulate time passing for animation,tick
+#include <Ecore.h>
 
 #include "evas_suite.h"
 #include "evas_tests_helpers.h"
 
+static int called_changed;
+static Efl_Canvas_Animation *animation_changed_ev;
+static int called_running;
+static double animation_running_position;
+
 EFL_START_TEST(evas_object_various)
 {
    Evas *evas = EVAS_TEST_INIT_EVAS();
@@ -51,8 +58,159 @@ EFL_START_TEST(evas_object_freeze_events)
 }
 EFL_END_TEST
 
+EFL_START_TEST(evas_object_animation_simple)
+{
+   Evas *evas = EVAS_TEST_INIT_EVAS();
+   Evas_Object *obj = evas_object_rectangle_add(evas);
+   Efl_Canvas_Animation *animation = efl_add(EFL_CANVAS_ANIMATION_CLASS, evas);
+
+   ck_assert_ptr_eq(efl_canvas_object_animation_get(obj) , NULL);
+   ck_assert(EINA_DBL_EQ(efl_canvas_object_animation_progress_get(obj), -1.0));
+
+   efl_canvas_object_animation_start(obj, animation, 1.0, 0.0);
+   ck_assert_ptr_eq(efl_canvas_object_animation_get(obj) , animation);
+   ck_assert(EINA_DBL_EQ(efl_canvas_object_animation_progress_get(obj), 0.0));
+
+   efl_canvas_object_animation_stop(obj);
+   ck_assert_ptr_eq(efl_canvas_object_animation_get(obj) , NULL);
+   ck_assert(EINA_DBL_EQ(efl_canvas_object_animation_progress_get(obj), -1.0));
+
+   efl_canvas_object_animation_start(obj, animation, 1.0, 0.0);
+   efl_canvas_object_animation_stop(obj);
+
+   efl_canvas_object_animation_start(obj, animation, -1.0, 1.0);
+   efl_canvas_object_animation_stop(obj);
+   efl_canvas_object_animation_stop(obj);
+}
+EFL_END_TEST
+
+EFL_START_TEST(evas_object_animation_progress)
+{
+   Evas *evas = EVAS_TEST_INIT_EVAS();
+   Evas_Object *obj = evas_object_rectangle_add(evas);
+   Efl_Canvas_Animation *animation = efl_add(EFL_CANVAS_ANIMATION_CLASS, evas, efl_animation_duration_set(efl_added, 1.0));
+
+   efl_canvas_object_animation_start(obj, animation, 1.0, 0.0);
+   efl_loop_time_set(efl_main_loop_get(), efl_loop_time_get(efl_main_loop_get()) + 0.5);
+   efl_event_callback_call(obj, EFL_CANVAS_OBJECT_EVENT_ANIMATOR_TICK, NULL);
+   ck_assert(EINA_DBL_EQ(efl_canvas_object_animation_progress_get(obj), 0.5));
+   efl_canvas_object_animation_stop(obj);
+}
+EFL_END_TEST
+
+static inline void
+_simulate_time_passing(Eo *obj, double start, double jump)
+{
+   efl_loop_time_set(efl_main_loop_get(), start + jump);
+   efl_event_callback_call(obj, EFL_CANVAS_OBJECT_EVENT_ANIMATOR_TICK, NULL);
+}
+
+static inline void
+_simulate_assert_time_passing(Eo *obj, double start, double jump, double expected_position)
+{
+   _simulate_time_passing(obj, start, jump);
+   ck_assert_int_eq((efl_canvas_object_animation_progress_get(obj)-expected_position)*10000, 0);
+}
+
+EFL_START_TEST(evas_object_animation_pause)
+{
+   Evas *evas = EVAS_TEST_INIT_EVAS();
+   Evas_Object *obj = evas_object_rectangle_add(evas);
+   double start = efl_loop_time_get(efl_main_loop_get());
+   Efl_Canvas_Animation *animation = efl_add(EFL_CANVAS_ANIMATION_CLASS, evas, efl_animation_duration_set(efl_added, 1.0));
+
+   efl_canvas_object_animation_start(obj, animation, 1.0, 0.0);
+
+   _simulate_assert_time_passing(obj, start, 0.2, 0.2);
+   efl_canvas_object_animation_pause_set(obj, EINA_TRUE);
+
+   _simulate_assert_time_passing(obj, start, 0.5, 0.2);
+   efl_canvas_object_animation_pause_set(obj, EINA_FALSE);
+
+   _simulate_assert_time_passing(obj, start, 0.7, 0.4);
+   efl_canvas_object_animation_stop(obj);
+}
+EFL_END_TEST
+
+EFL_START_TEST(evas_object_animation_error)
+{
+   Evas *evas = EVAS_TEST_INIT_EVAS();
+   Evas_Object *obj = evas_object_rectangle_add(evas);
+   Efl_Canvas_Animation *animation = efl_add(EFL_CANVAS_ANIMATION_CLASS, evas);
+   EXPECT_ERROR_START;
+   efl_canvas_object_animation_start(obj, NULL, 1.0, 0.0);
+   EXPECT_ERROR_END;
+
+   EXPECT_ERROR_START;
+   efl_canvas_object_animation_start(obj, animation, 0.0, 0.0);
+   EXPECT_ERROR_END;
+   efl_canvas_object_animation_stop(obj);
+
+   EXPECT_ERROR_START;
+   efl_canvas_object_animation_start(obj, animation, 1.0, 2.0);
+   EXPECT_ERROR_END;
+   efl_canvas_object_animation_stop(obj);
+
+   EXPECT_ERROR_START;
+   efl_canvas_object_animation_start(obj, animation, 1.0, -1.0);
+   EXPECT_ERROR_END;
+   efl_canvas_object_animation_stop(obj);
+}
+EFL_END_TEST
+
+static void
+_anim_changed_cb(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
+{
+   animation_changed_ev = event->info;
+   called_changed ++;
+}
+
+static void
+_anim_running_cb(void *data EINA_UNUSED, const Efl_Event *event)
+{
+   animation_running_position = *((double*) event->info);
+   called_running ++;
+}
+
+EFL_CALLBACKS_ARRAY_DEFINE(animation_stats_cb,
+  {EFL_CANVAS_OBJECT_ANIMATION_EVENT_ANIMATION_CHANGED, _anim_changed_cb },
+  {EFL_CANVAS_OBJECT_ANIMATION_EVENT_ANIMATION_PROGRESS_UPDATED, _anim_running_cb },
+)
+
+EFL_START_TEST(evas_object_animation_events)
+{
+   Evas *evas = EVAS_TEST_INIT_EVAS();
+   Evas_Object *obj = evas_object_rectangle_add(evas);
+   double start = efl_loop_time_get(efl_main_loop_get());
+   Efl_Canvas_Animation *animation = efl_add(EFL_CANVAS_ANIMATION_CLASS, evas, efl_animation_duration_set(efl_added, 1.0));
+
+   efl_event_callback_array_add(obj, animation_stats_cb(), NULL);
+
+   efl_canvas_object_animation_start(obj, animation, 1.0, 0.0);
+
+   ck_assert_int_eq(called_changed, 1);
+   ck_assert_ptr_eq(animation_changed_ev, animation);
+   ck_assert_int_eq(called_running, 1);
+   ck_assert(EINA_DBL_EQ(animation_running_position, 0.0));
+
+   _simulate_time_passing(obj, start, 1.0);
+
+   ck_assert_int_eq(called_changed, 2);
+   ck_assert_ptr_eq(animation_changed_ev, NULL);
+   ck_assert_int_eq(called_running, 2);
+   ck_assert(EINA_DBL_EQ(animation_running_position, 1.0));
+   ck_assert_ptr_eq(efl_canvas_object_animation_get(obj), NULL);
+   ck_assert(EINA_DBL_EQ(efl_canvas_object_animation_progress_get(obj), -1.0));
+}
+EFL_END_TEST
+
 void evas_test_object(TCase *tc)
 {
    tcase_add_test(tc, evas_object_various);
    tcase_add_test(tc, evas_object_freeze_events);
+   tcase_add_test(tc, evas_object_animation_simple);
+   tcase_add_test(tc, evas_object_animation_progress);
+   tcase_add_test(tc, evas_object_animation_pause);
+   tcase_add_test(tc, evas_object_animation_error);
+   tcase_add_test(tc, evas_object_animation_events);
 }