Back to Lvgl

Animation (lv_anim)

docs/src/main-modules/animation.rst

9.5.016.3 KB
Original Source

.. _animation:

=================== Animation (lv_anim)

Animations allow you to define the way something should move or change over time, and let LVGL do the heavy lifting of making it happen. What makes it so powerful is that the thing being changed can be virtually anything in your system. It is very convenient to apply this to LVGL Widgets in your user interface (UI), to change their appearance, size or location over time. But because it is --- at its core --- a generic change-over-time manager, complete with a variety of optional event callbacks, its application can be wider than just to UI components.

For each Animation you create, it accomplishes the above by providing a generic method of varying a signed integer from a start value to an end value over a specified time period. It allows you to specify what object it applies to (the "variable"), which is available in the callback functions that are called as the Animation is playing through.

This variation over time can be linear (default), it can be on a path (curve) that you specify, and there is even a variety of commonly-used non-linear effects that can be specified.

The main callback called during an Animation (when it is playing) is called an animator function, which has the following prototype:

.. code-block:: c

void func(void *var , int32_t value);

This prototype makes it easy to use most of the LVGL set functions directly or via a trivial wrapper. It includes:

  • most of the widget properties
  • functions that set :ref:local style properties <style_local> directly on objects (needs a wrapper to set the selector)
  • set properties on :cpp:type:lv_style_t objects (e.g. :ref:shared styles <style_initialize>) (lv_obj_report_style_change needs to be called to notify the widgets having the style)
  • lv_style_set_<property_name>(&style, <value>)
  • lv_obj_set_<property_name>(widget, <value>)

Using lv_style_set_<property_name>(&shared_style, <value>), an animation on a single :cpp:type:lv_style_t object shared among several objects can simultaneously modify the appearance of all objects that use it. See :ref:styles for more details.

Examples of lv_obj_set_<property_name>(widget, <value>) are: :cpp:expr:lv_obj_set_x(widget, value) or :cpp:expr:lv_obj_set_width(widget, value).

This makes it very convenient to apply to the appearance (and other attributes) of UI components. You can also provide your own "set" functions, so the application of Animations is really limited only by your imagination.

The number of Animations that can be playing at the same time for a given object with a given animator callback is one (1). However, the number of Animations that can be playing at the same time is limited only by available RAM and CPU time for:

  • a given object with different animator callbacks; and
  • different objects.

Thus, you can have a Button's width being changed by one Animation while having its height being changed by another Animation.

.. _animations_create:

Creating an Animation


To create an Animation, start by creating an Animation template in an :cpp:type:lv_anim_t variable. It has to be initialized and configured with lv_anim_set_...() functions.

.. code-block:: c

/* INITIALIZE AN ANIMATION -----------------------/

static lv_anim_t anim_template; static lv_anim_t * running_anim;

lv_anim_init(&anim_template);

/* MANDATORY SETTINGS ------------------/

/* Set the "animator" function */ lv_anim_set_exec_cb(&anim_template, (lv_anim_exec_xcb_t) lv_obj_set_x);

/* Set target of the Animation */ lv_anim_set_var(&anim_template, widget);

/* Length of the Animation [ms] */ lv_anim_set_duration(&anim_template, duration_in_ms);

/* Set start and end values. E.g. 0, 150 */ lv_anim_set_values(&anim_template, start, end);

/* OPTIONAL SETTINGS ------------------/

/* Time to wait before starting the Animation [ms] */ lv_anim_set_delay(&anim_template, delay);

/* Set path (curve). Default is linear */ lv_anim_set_path_cb(&anim_template, lv_anim_path_ease_in);

/* Set anim_template callback to indicate when the Animation is completed. */ lv_anim_set_completed_cb(&anim_template, completed_cb);

/* Set anim_template callback to indicate when the Animation is deleted (idle). */ lv_anim_set_deleted_cb(&anim_template, deleted_cb);

/* Set anim_template callback to indicate when the Animation is started (after delay). */ lv_anim_set_start_cb(&anim_template, start_cb);

/* When ready, play the Animation backward with this duration. Default is 0 (disabled) [ms] */ lv_anim_set_reverse_duration(&anim_template, time);

/* Delay before reverse play. Default is 0 (disabled) [ms] */ lv_anim_set_reverse_delay(&anim_template, delay);

/* Number of repetitions. Default is 1. LV_ANIM_REPEAT_INFINITE for infinite repetition */ lv_anim_set_repeat_count(&anim_template, cnt);

/* Delay before repeat. Default is 0 (disabled) [ms] */ lv_anim_set_repeat_delay(&anim_template, delay);

/* Apply the start value immediately (default true); false: apply start value after * delay when the Animation actually starts. */ lv_anim_set_early_apply(&anim_template, true/false);

/* START THE ANIMATION ------------------/ running_anim = lv_anim_start(&anim_template); /* Start the Animation */

.. _animation_path:

Animation Path


You can control the Path (rate curve) of an Animation. The simplest case is linear, meaning the current value between start and end is changed at the same rate (i.e. with fixed steps) over the duration of the Animation. A Path is a function which calculates the next value to set based on the current state of the Animation. There are a number of built-in Paths that can be used:

  • :cpp:func:lv_anim_path_linear: linear animation (default)
  • :cpp:func:lv_anim_path_step: change in one step at the end
  • :cpp:func:lv_anim_path_ease_in: slow at the beginning
  • :cpp:func:lv_anim_path_ease_out: slow at the end
  • :cpp:func:lv_anim_path_ease_in_out: slow at the beginning and end
  • :cpp:func:lv_anim_path_overshoot: overshoot the end value
  • :cpp:func:lv_anim_path_bounce: bounce back a little from the end value (like hitting a wall)

Alternately, you can provide your own Path function.

:cpp:expr:lv_anim_init(&my_anim) sets the Path to :cpp:func:lv_anim_path_linear by default. If you want to use a different Path (including a custom Path function you provide), set it using :cpp:expr:lv_anim_set_path_cb(&anim_template, path_cb).

If you provide your own custom Path function, its prototype is:

.. code-block:: c

int32_t   calculate_value(lv_anim_t * anim);

.. _animation_speed_vs_time:

Speed vs Time


Normally, you set the Animation duration directly using :cpp:expr:lv_anim_set_duration(&anim_template, duration_in_ms). But in some cases the rate is known but the duration is not known. Given an Animation's start and end values, rate here means the number of units of change per second, i.e. how quickly (units per second) the Animation's value needs to change between the start and end value. For such cases there is a utility function :cpp:func:lv_anim_speed_to_time you can use to compute the Animation's duration, so you can set it like this:

.. code-block:: c

uint32_t  change_per_sec = 20;
uint32_t  duration_in_ms = lv_anim_speed_to_time(change_per_sec, 0, 100);
/* `duration_in_ms` will be 5000 */
lv_anim_set_duration(&anim_template, duration_in_ms);

.. _animation_direction:

Animating in Both Directions


Sometimes an Animation needs to play forward, and then play backwards, effectively reversing course, animating from the end value back to the start value again. To do this, pass a non-zero value to this function to set the duration for the reverse portion of the Animation: :cpp:expr:lv_anim_set_reverse_duration(&anim_template, duration_in_ms).

Optionally, you can also introduce a delay between the forward and backward directions using :cpp:expr:lv_anim_set_reverse_delay(&anim_template, delay_in_ms)

.. _animation_start:

Starting an Animation


After you have set up your :cpp:type:lv_anim_t object, it is important to realize that what you have set up is a "template" for a live, running Animation that has not been created yet. When you call :cpp:expr:lv_anim_start(&anim_template) passing the template you have set up, it uses your template to dynamically allocate an internal object that is a live, running Animation. This function returns a pointer to that object.

.. code-block:: c

static lv_anim_t   anim_template;
static lv_anim_t * running_anim;

/* Set up template... */
lv_anim_init(&anim_template);
/* ...and other set-up functions above. */

/* Later... */
running_anim = lv_anim_start(&anim_template);

.. note::

:cpp:expr:`lv_anim_start(&anim_template)` makes its own copy of the Animation
template, so if you do not need it later, its contents do not need to be
preserved after this call.

Once a live running Animation has been started, it runs until it has completed, or until it is deleted (see below), whichever comes first. An Animation has completed when:

  • its "value" has reached the designated end value;
  • if the Animation has a non-zero reverse duration value, then its value has run from the end value back to the start value again;
  • if a non-zero repeat count has been set, it has repeated the Animation that number of times.

Once the live, running Animation reaches completion, it is automatically deleted from the list of running Animations. This does not impact your Animation template.

.. note::

If :cpp:expr:`lv_anim_set_repeat_count(&anim_template, cnt)` has been called
passing :c:macro:`LV_ANIM_REPEAT_INFINITE`, the animation never reaches a state
of being "completed".  In this case, it must be deleted to terminate the
Animation.

.. _animation_delete:

Deleting Animations


You should delete an Animation using :cpp:expr:lv_anim_delete(var, func) if one of these two conditions exists:

  • the object (variable) being animated is deleted (and it is not a Widget) or
  • a running animation needs to be stopped before it is completed.

.. note::

If the object (variable) being deleted is a type of Widget, the housekeeping code
involved in deleting it also deletes any running animations that are connected
with it.  So :cpp:expr:`lv_anim_delete(var, func)` only needs to be called if the
object being deleted is *not* one of the Widgets.

If you kept a copy of the pointer returned by :cpp:func:lv_anim_start as running_anim, you can delete the running animation like this:

.. code-block:: c

lv_anim_delete(running_anim->var, running_anim->exec_cb);

In the event that the Animation completes after you have determined it needs to be deleted, and before the call to :cpp:func:lv_anim_delete is made, it does no harm to call it a second time --- no damage will occur.

This function returns a Boolean value indicating whether any live, running Animations were deleted.

.. _animation_pause:

Pausing Animations


If you kept a copy of the pointer returned by :cpp:func:lv_anim_start, you can pause the running animation using :cpp:expr:lv_anim_pause(animation) and then resume it using :cpp:expr:lv_anim_resume(animation).

:cpp:expr:lv_anim_pause_for(animation, milliseconds) is also available if you wish for the animation to resume automatically after.

.. _animations_timeline:

Timelines


You can create a series of related animations that are linked together using an Animation Timeline. A Timeline is a collection of multiple Animations which makes it easy to create complex composite Animations.

.. image:: /_static/images/anim-timeline.png

To create and use an Animation Timeline:

  • Create an Animation template but do not call :cpp:func:lv_anim_start on it.

  • Create an Animation Timeline object by calling :cpp:func:lv_anim_timeline_create.

  • Add Animation templates to the Timeline by calling :cpp:expr:lv_anim_timeline_add(timeline, start_time, &anim_template). start_time is the start time of the Animation on the Timeline. Note that start_time will override any value given to :cpp:expr:lv_anim_set_delay(&anim_template, delay).

  • Call :cpp:expr:lv_anim_timeline_start(timeline) to start the Animation Timeline.

.. note::

:cpp:expr:`lv_anim_timeline_add(timeline, start_time, &anim_template)` makes its
own copy of the contents of the Animation template, so if you do not need it
later, its contents do not need to be preserved after this call.

It supports forward and reverse play of the entire Animation group, using :cpp:expr:lv_anim_timeline_set_reverse(timeline, reverse). Note that if you want to play in reverse from the end of the Timeline, you need to call :cpp:expr:lv_anim_timeline_set_progress(timeline, LV_ANIM_TIMELINE_PROGRESS_MAX) after adding all Animations and before telling it to start playing.

Call :cpp:expr:lv_anim_timeline_pause(timeline) to pause the Animation Timeline. Note: this does not preserve its state. The only way to start it again is to call :cpp:expr:lv_anim_timeline_start(timeline), which starts the Timeline from the beginning or at the point set by :cpp:expr:lv_anim_timeline_set_progress(timeline, progress).

Call :cpp:expr:lv_anim_timeline_set_progress(timeline, progress) function to set the state of the Animation Timeline according to the progress value. progress is a value between 0 and 65535 (:c:macro:LV_ANIM_TIMELINE_PROGRESS_MAX) to indicate the proportion of the Timeline that has "played". Example: a progress value of :cpp:expr:LV_ANIM_TIMELINE_PROGRESS_MAX / 2 would set the Timeline play to its half-way point.

Call :cpp:expr:lv_anim_timeline_get_playtime(timeline) function to get the total duration (in milliseconds) of the entire Animation Timeline.

Call :cpp:expr:lv_anim_timeline_get_reverse(timeline) function to get whether the Animation Timeline is also played in reverse after its forward play completes.

Call :cpp:expr:lv_anim_timeline_delete(timeline) function to delete the Animation Timeline.

.. caution::

If you need to delete a Widget during Animation, be sure to delete the Animation
Timeline before deleting the Widget. Otherwise, the program may crash or behave
abnormally.

.. caution::

To prevent undefined behavior, the Animations subsystem does not allow more than
one Animation to execute concurrently when they modify the same target object
with the same *animator* callback.

If you have set up an Animation Timeline from 2 or more Animation Templates with
these characteristics, be aware that when the Animation is started for any of
them, the Animation subsystem looks for such cases and considers it a conflict
when:

- more than one are running concurrently, or
- more than one have their "early apply" behavior turned on (which it is by default).

When this is detected, all such Animations are deleted except for the one most
recently added.

To avoid this, if you want to use more than one Animation Template that updates
the same object using the same *animator* callback:

- ensure that all but the first one to execute in the Timeline have their
  "early apply" default behavior turned off before starting the Timeline, and

- ensure that they do not execute concurrently.

Example:

.. code-block:: c

    /* These 4 Animation Templates execute in an Animation Timeline in the numbered
     * sequence, and they modify the same object using the same callback. */
    lv_anim_t anim_template_1;
    lv_anim_t anim_template_2;
    lv_anim_t anim_template_3;
    lv_anim_t anim_template_4;

    /* Initialization code for all 4 of them here. */

    /* Ensure these steps are made before the Timeline is started. */
    lv_anim_set_early_apply(&anim_template_2, false);
    lv_anim_set_early_apply(&anim_template_3, false);
    lv_anim_set_early_apply(&anim_template_4, false);

    /* Start Timeline */
    lv_anim_timeline_start(timeline);

.. _animations_example:

Examples


.. include:: /examples/anim/index.rst

.. _animations_api:

API