动画在现代用户界面设计中扮演着至关重要的角色,它们不仅可以增加应用程序的视觉吸引力,还可以提高用户体验。LVGL 9带来了令人兴奋的新特性和改进,其中动画功能的增强是引人瞩目的。在本文中,我们将深入探讨LVGL 9中的动画功能,探索其新特性以及对用户界面设计的影响。
您可以使用动画来自动地在起始值和结束值之间改变变量的值。动画会通过周期性地调用具有相应值参数的“animator”函数来实现。
这些“animator”函数具有以下原型:
void func(void * var, lv_anim_var_t value);
该原型与LVGL中大多数属性设置函数兼容。例如 lv_obj_set_x(obj, value)
或 lv_obj_set_width(obj, value)
。
要创建动画,需要初始化lv_anim_t并使用 lv_anim_set_…() 函数配置一个 lv_anim_t 变量。
使用lv_anim_init
进行lv_anim_t
类型的初始化
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_exec_cb
设置动画的回调函数
lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t) lv_obj_set_x);
我们可以直接把设置属性的函数强制类型转换成lv_anim_exec_xcb_t就可以直接当作回调函数了
使用下面这个函数设置要对哪个对象进行动画操作
lv_anim_set_var(&a, obj);
使用下面的函数设置动画的使用:
lv_anim_set_duration(&a, duration);
单位为ms
使用下面这个函数设置动画的起始值
lv_anim_set_values(&a, start, end);
使用下面这个函数可以开启动画:
lv_anim_start(&a);
lv_obj_t *obj = lv_obj_create(lv_scr_act());
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t) lv_obj_set_x);
lv_anim_set_var(&a, obj);
/*Length of the animation [ms]*/
lv_anim_set_duration(&a, 1000);
/*Set start and end values. E.g. 0, 150*/
lv_anim_set_values(&a, 0, 200);
lv_anim_start(&a);
这样就可以使用动画了
lv_anim_set_delay(&a, delay);
设置动画开始前的延迟时间,以毫秒为单位。即动画将在延迟时间之后才会启动。
lv_anim_set_path(&a, lv_anim_path_ease_in);
设置动画的路径或曲线。默认是线性 (lv_anim_path_linear),可以选择其他曲线,比如 lv_anim_path_ease_in 表示加速曲线。
lv_anim_set_completed_cb(&a, completed_cb);
设置一个回调函数,指示动画完成时要调用的函数。completed_cb 是用户定义的回调函数,用于执行动画完成后的特定操作。
lv_anim_set_deleted_cb(&a, deleted_cb);
设置一个回调函数,指示动画被删除(空闲)时要调用的函数。deleted_cb 是用户定义的回调函数,用于执行删除动画时的特定操作。
lv_anim_set_start_cb(&a, start_cb);
设置一个回调函数,指示动画启动(在延迟之后)时要调用的函数。start_cb 是用户定义的回调函数,用于执行动画启动时的特定操作。
lv_anim_set_playback_duration(&a, time);
在动画完成后,以指定的持续时间反向播放动画。默认是 0,表示禁用回放。1就是回放
lv_anim_set_playback_delay(&a, delay);
设置回放动画开始前的延迟时间,以毫秒为单位。
lv_anim_set_repeat_count(&a, cnt);
设置动画的重复次数。默认为 1,可以设置为 LV_ANIM_REPEAT_INFINITE 表示无限次重复。
lv_anim_set_repeat_delay(&a, delay);
设置每次重复开始前的延迟时间,以毫秒为单位。
lv_anim_set_early_apply(&a, true/false);
控制是否立即应用动画的起始值。如果设置为 true(默认),动画启动后立即应用起始值;如果设置为 false,则在动画真正启动时应用起始值(在延迟之后)。
lvgl包含下面这些动画曲线:
lv_anim_path_linear():
线性动画
这是最简单的动画路径,表示动画在起始值和结束值之间以恒定的步长改变当前值。
lv_anim_path_step():
一步变化
在动画结束时,直接从起始值变换到结束值,没有过渡。
lv_anim_path_ease_in():
起始缓慢
在动画的开始部分,变化速度较慢,逐渐加速到结束。
lv_anim_path_ease_out():
结束缓慢
在动画的结束部分,变化速度较慢,逐渐减速到结束。
lv_anim_path_ease_in_out():
起始和结束缓慢
在动画的开始和结束部分,变化速度较慢,中间部分变化速度较快。
lv_anim_path_overshoot():
超过结束值
动画在结束时会超过结束值一段距离,然后再回到结束值。
lv_anim_path_bounce():
反弹
动画在达到结束值后会稍微反弹一段距离,类似于撞到墙壁后的反弹效果。
这些路径函数允许您根据动画的需求选择合适的动画效果,使动画看起来更加生动和自然。
示例
lv_anim_path_ease_in(&a);
您可以使用 lv_anim_delete(var, func) 函数来删除动画,只需提供被动画化的变量及其动画函数即可。
比如:lv_anim_delete(obj,(lv_anim_exec_xcb_t) lv_obj_set_x);
时间轴是多个动画的集合,可以轻松创建复杂的复合动画。
首先,创建一个动画元素,但不要调用 lv_anim_start()。
其次,通过调用 lv_anim_timeline_create()
创建一个动画时间轴对象。
第三,通过调用 lv_anim_timeline_add(at, start_time, &a)
将动画元素添加到动画时间轴中。start_time 是动画在时间轴上的开始时间。请注意,start_time 将覆盖延迟的值。
最后,调用 lv_anim_timeline_start(at)
来启动动画时间轴。
它支持整个动画组的正向和反向播放,使用 lv_anim_timeline_set_reverse(at, reverse)。请注意,如果您想从时间轴的末尾开始反向播放,则需要在添加所有动画并开始播放之前调用 lv_anim_timeline_set_progress(at, LV_ANIM_TIMELINE_PROGRESS_MAX)
。
调用 lv_anim_timeline_stop(at)
来停止动画时间轴。
调用 lv_anim_timeline_set_progress(at, progress)
函数来设置对象的状态,以对应时间轴的进度。
调用 lv_anim_timeline_get_playtime(at)
函数来获取整个动画时间轴的总持续时间。
调用 lv_anim_timeline_get_reverse(at)
函数来获取是否反转动画时间轴。
调用 lv_anim_timeline_delete(at)
函数来删除动画时间轴。注意:如果您需要在动画过程中删除对象,请务必在删除对象之前删除动画时间轴。否则,程序可能会崩溃或行为异常。
示例代码:
static const int32_t obj_width = 90;
static const int32_t obj_height = 70;
static void set_width(lv_anim_t * var, int32_t v)
{
lv_obj_set_width(var->var, v);
}
static void set_height(lv_anim_t * var, int32_t v)
{
lv_obj_set_height(var->var, v);
}
static void set_slider_value(lv_anim_t * var, int32_t v)
{
lv_slider_set_value(var->var, v, LV_ANIM_OFF);
}
static void btn_start_event_handler(lv_event_t * e)
{
lv_obj_t * btn = lv_event_get_current_target_obj(e);
lv_anim_timeline_t * anim_timeline = lv_event_get_user_data(e);
bool reverse = lv_obj_has_state(btn, LV_STATE_CHECKED);
lv_anim_timeline_set_reverse(anim_timeline, reverse);
lv_anim_timeline_start(anim_timeline);
}
static void btn_pause_event_handler(lv_event_t * e)
{
lv_anim_timeline_t * anim_timeline = lv_event_get_user_data(e);
lv_anim_timeline_pause(anim_timeline);
}
static void slider_prg_event_handler(lv_event_t * e)
{
lv_obj_t * slider = lv_event_get_current_target_obj(e);
lv_anim_timeline_t * anim_timeline = lv_event_get_user_data(e);
int32_t progress = lv_slider_get_value(slider);
lv_anim_timeline_set_progress(anim_timeline, progress);
}
/**
* Create an animation timeline
*/
void lv_example_anim_timeline_1(void)
{
/* Create anim timeline */
lv_anim_timeline_t * anim_timeline = lv_anim_timeline_create();
lv_obj_t * par = lv_screen_active();
lv_obj_set_flex_flow(par, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(par, LV_FLEX_ALIGN_SPACE_AROUND, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
/* create btn_start */
lv_obj_t * btn_start = lv_button_create(par);
lv_obj_add_event_cb(btn_start, btn_start_event_handler, LV_EVENT_VALUE_CHANGED, anim_timeline);
lv_obj_add_flag(btn_start, LV_OBJ_FLAG_IGNORE_LAYOUT);
lv_obj_add_flag(btn_start, LV_OBJ_FLAG_CHECKABLE);
lv_obj_align(btn_start, LV_ALIGN_TOP_MID, -100, 20);
lv_obj_t * label_start = lv_label_create(btn_start);
lv_label_set_text(label_start, "Start");
lv_obj_center(label_start);
/* create btn_pause */
lv_obj_t * btn_pause = lv_button_create(par);
lv_obj_add_event_cb(btn_pause, btn_pause_event_handler, LV_EVENT_CLICKED, anim_timeline);
lv_obj_add_flag(btn_pause, LV_OBJ_FLAG_IGNORE_LAYOUT);
lv_obj_align(btn_pause, LV_ALIGN_TOP_MID, 100, 20);
lv_obj_t * label_pause = lv_label_create(btn_pause);
lv_label_set_text(label_pause, "Pause");
lv_obj_center(label_pause);
/* create slider_prg */
lv_obj_t * slider_prg = lv_slider_create(par);
lv_obj_add_event_cb(slider_prg, slider_prg_event_handler, LV_EVENT_VALUE_CHANGED, anim_timeline);
lv_obj_add_flag(slider_prg, LV_OBJ_FLAG_IGNORE_LAYOUT);
lv_obj_align(slider_prg, LV_ALIGN_BOTTOM_MID, 0, -20);
lv_slider_set_range(slider_prg, 0, LV_ANIM_TIMELINE_PROGRESS_MAX);
/* create 3 objects */
lv_obj_t * obj1 = lv_obj_create(par);
lv_obj_set_size(obj1, obj_width, obj_height);
lv_obj_set_scrollbar_mode(obj1, LV_SCROLLBAR_MODE_OFF);
lv_obj_t * obj2 = lv_obj_create(par);
lv_obj_set_size(obj2, obj_width, obj_height);
lv_obj_set_scrollbar_mode(obj2, LV_SCROLLBAR_MODE_OFF);
lv_obj_t * obj3 = lv_obj_create(par);
lv_obj_set_size(obj3, obj_width, obj_height);
lv_obj_set_scrollbar_mode(obj3, LV_SCROLLBAR_MODE_OFF);
/* anim-slider */
lv_anim_t a_slider;
lv_anim_init(&a_slider);
lv_anim_set_var(&a_slider, slider_prg);
lv_anim_set_values(&a_slider, 0, LV_ANIM_TIMELINE_PROGRESS_MAX);
lv_anim_set_custom_exec_cb(&a_slider, set_slider_value);
lv_anim_set_path_cb(&a_slider, lv_anim_path_linear);
lv_anim_set_duration(&a_slider, 700);
/* anim-obj1 */
lv_anim_t a1;
lv_anim_init(&a1);
lv_anim_set_var(&a1, obj1);
lv_anim_set_values(&a1, 0, obj_width);
lv_anim_set_custom_exec_cb(&a1, set_width);
lv_anim_set_path_cb(&a1, lv_anim_path_overshoot);
lv_anim_set_duration(&a1, 300);
lv_anim_t a2;
lv_anim_init(&a2);
lv_anim_set_var(&a2, obj1);
lv_anim_set_values(&a2, 0, obj_height);
lv_anim_set_custom_exec_cb(&a2, set_height);
lv_anim_set_path_cb(&a2, lv_anim_path_ease_out);
lv_anim_set_duration(&a2, 300);
/* anim-obj2 */
lv_anim_t a3;
lv_anim_init(&a3);
lv_anim_set_var(&a3, obj2);
lv_anim_set_values(&a3, 0, obj_width);
lv_anim_set_custom_exec_cb(&a3, set_width);
lv_anim_set_path_cb(&a3, lv_anim_path_overshoot);
lv_anim_set_duration(&a3, 300);
lv_anim_t a4;
lv_anim_init(&a4);
lv_anim_set_var(&a4, obj2);
lv_anim_set_values(&a4, 0, obj_height);
lv_anim_set_custom_exec_cb(&a4, set_height);
lv_anim_set_path_cb(&a4, lv_anim_path_ease_out);
lv_anim_set_duration(&a4, 300);
/* anim-obj3 */
lv_anim_t a5;
lv_anim_init(&a5);
lv_anim_set_var(&a5, obj3);
lv_anim_set_values(&a5, 0, obj_width);
lv_anim_set_custom_exec_cb(&a5, set_width);
lv_anim_set_path_cb(&a5, lv_anim_path_overshoot);
lv_anim_set_duration(&a5, 300);
lv_anim_t a6;
lv_anim_init(&a6);
lv_anim_set_var(&a6, obj3);
lv_anim_set_values(&a6, 0, obj_height);
lv_anim_set_custom_exec_cb(&a6, set_height);
lv_anim_set_path_cb(&a6, lv_anim_path_ease_out);
lv_anim_set_duration(&a6, 300);
/* add animations to timeline */
lv_anim_timeline_add(anim_timeline, 0, &a_slider);
lv_anim_timeline_add(anim_timeline, 0, &a1);
lv_anim_timeline_add(anim_timeline, 0, &a2);
lv_anim_timeline_add(anim_timeline, 200, &a3);
lv_anim_timeline_add(anim_timeline, 200, &a4);
lv_anim_timeline_add(anim_timeline, 400, &a5);
lv_anim_timeline_add(anim_timeline, 400, &a6);
lv_anim_timeline_set_progress(anim_timeline, LV_ANIM_TIMELINE_PROGRESS_MAX);
}
LVGL 9的动画功能为用户界面设计带来了许多令人振奋的新机会。通过引入更多的动画效果和特性,LVGL 9使得开发人员能够创建更加生动、交互性更强的用户界面。从简单的过渡效果到复杂的动态元素,LVGL 9的动画功能为开发人员提供了丰富的工具和资源,以创造出令人惊叹的用户体验。随着LVGL 9的持续发展和改进,我们可以期待看到更多创新的动画效果和功能,为未来的用户界面设计开辟新的可能性。