feat(xml): add color percentage support to animations (#9209)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Gabor Kiss-Vamosi
2025-11-14 07:39:30 +01:00
committed by GitHub
parent 3d3bcd5dd3
commit 5472a77c6e
5 changed files with 118 additions and 74 deletions
@@ -61,7 +61,7 @@ that you can reference later.
Inside a ``<timeline>``, you add ``<animation>``\ s to describe each step.
Supported properties of ``<animation>`` are:
- ``prop``: Style property to animate. All integer style properties are supported (colors are not).
- ``prop``: Style property to animate. All integer, percentage and color style properties are supported.
- ``selector``: Style selector, e.g. ``knob|pressed``. Default: ``main|default``.
- ``target``: Name of the UI element to animate. ``self`` refers to the root element of the Component (the ``<view>``).
- ``start``: Start value (integer only).
-2
View File
@@ -23,8 +23,6 @@ void lv_example_xml_2(void)
lv_obj_t * obj = (lv_obj_t *) lv_xml_create(lv_screen_active(), "view", NULL);
lv_obj_set_pos(obj, 10, 10);
lv_xml_component_unregister("my_button");
const char * slider_attrs[] = {
"x", "200",
"y", "-15",
+38 -11
View File
@@ -1,15 +1,42 @@
<!-- A simple button that defines a show up animation for itself -->
<component>
<consts>
<px name="size" value="100"/>
<color name="orange" value="0xffa020"/>
</consts>
<api>
<prop name="label_text" type="string" default="Click me" />
</api>
<api>
<prop name="btn_text" type="string"/>
</api>
<styles>
<style
name="style_base"
bg_opa="100%"
bg_color="0x000044"
width="content"
height="content"
radius="10"
pad_hor="30"
pad_ver="20"
text_color="0xfff"
/>
<view extends="lv_button" width="#size">
<my_h3 text="$btn_text" align="center" color="#orange"/>
</view>
<style name="style_pressed" recolor="0xfff" recolor_opa="20%" />
</styles>
<animations>
<!-- Fade in and move up
This animation can be played later in an event by a parent component or screen -->
<timeline name="show_up">
<animation target="self" prop="opa" start="0" end="255" duration="200" early_apply="true" />
<animation target="self" prop="bg_color" start="0xff0000" end="0x00ff00" duration="2000" early_apply="true" />
<animation target="self" prop="translate_y" start="20" end="0" duration="200" early_apply="true" />
<animation target="self" prop="width" start="0%" end="50%" duration="200" early_apply="true" />
</timeline>
</animations>
<view extends="lv_button">
<remove_style_all />
<style name="style_base" />
<style name="style_pressed" selector="pressed" />
<lv_label text="$label_text" align="center" />
<play_timeline_event target="self" timeline="show_up" />
</view>
</component>
+6 -21
View File
@@ -9,26 +9,11 @@
<style name="btn_pr_style" bg_opa="255"/>
</styles>
<view extends="lv_obj" name="main" width="280" height="content" style_bg_color="#light_blue"
style_layout="grid" style_grid_column_dsc_array="100 fr(1)" style_grid_row_dsc_array="50 200"
>
<lv_obj style_grid_cell_column_pos="0" style_grid_cell_column_span="1" style_grid_cell_x_align="stretch"
style_grid_cell_row_pos="0" style_grid_cell_row_span="1" style_grid_cell_y_align="stretch">
<lv_label text="0/0"/>
</lv_obj>
<lv_obj style_grid_cell_column_pos="0" style_grid_cell_column_span="1" style_grid_cell_x_align="stretch"
style_grid_cell_row_pos="1" style_grid_cell_row_span="1" style_grid_cell_y_align="stretch">
<lv_label text="0/1"/>
</lv_obj>
<lv_obj style_grid_cell_column_pos="1" style_grid_cell_column_span="1" style_grid_cell_x_align="stretch"
style_grid_cell_row_pos="0" style_grid_cell_row_span="1" style_grid_cell_y_align="stretch">
<lv_label text="1/0"/>
</lv_obj>
<lv_obj style_grid_cell_column_pos="1" style_grid_cell_column_span="1" style_grid_cell_x_align="stretch"
style_grid_cell_row_pos="1" style_grid_cell_row_span="1" style_grid_cell_y_align="stretch">
<lv_label text="1/1"/>
</lv_obj>
<view width="280" height="content"
flex_flow="column">
<my_button/>
<my_button/>
<my_button/>
<my_button/>
</view>
</component>
+73 -39
View File
@@ -32,9 +32,19 @@
**********************/
typedef enum {
STYLE_PROP_TYPE_INT,
STYLE_PROP_TYPE_OPA,
STYLE_PROP_TYPE_COLOR,
STYLE_PROP_TYPE_UNKNOWN
} style_prop_anim_type_t;
typedef struct {
lv_style_selector_t selector;
lv_style_prop_t prop;
style_prop_anim_type_t prop_type;
lv_color_t color_start;
lv_color_t color_end;
} anim_data_t;
/**********************
* STATIC PROTOTYPES
**********************/
@@ -46,8 +56,7 @@ static void process_image_element(lv_xml_parser_state_t * state, const char * ty
static void process_prop_element(lv_xml_parser_state_t * state, const char ** attrs);
static char * extract_view_content(const char * xml_definition);
static style_prop_anim_type_t style_prop_anim_get_type(lv_style_prop_t prop);
static int32_t anim_value_to_int(lv_style_prop_t prop_type, const char * value_str);
static void int_anim_exec_cb(lv_anim_t * a, int32_t v);
static void anim_exec_cb(lv_anim_t * a, int32_t v);
/**********************
* STATIC VARIABLES
@@ -318,6 +327,7 @@ lv_result_t lv_xml_component_unregister(const char * name)
lv_xml_anim_timeline_child_t * child;
LV_LL_READ(&timeline->anims_ll, child) {
if(child->is_anim) {
lv_free(child->data.anim.user_data); /*It was anim_data_t*/
lv_free(child->data.anim.var); /*It was the name of the target object*/
}
else {
@@ -592,10 +602,6 @@ static void process_animation_element(lv_xml_parser_state_t * state, const char
lv_style_selector_t selector = lv_xml_style_selector_text_to_enum(selector_str);
int32_t start = anim_value_to_int(prop_type, start_str);
int32_t end = anim_value_to_int(prop_type, end_str);
if(target_str[0] == '#') target_str = lv_xml_get_const(&state->scope, &target_str[1]);
if(prop_str[0] == '#') prop_str = lv_xml_get_const(&state->scope, &prop_str[1]);
if(start_str[0] == '#') start_str = lv_xml_get_const(&state->scope, &start_str[1]);
@@ -615,20 +621,41 @@ static void process_animation_element(lv_xml_parser_state_t * state, const char
return;
}
uint32_t selector_and_prop = ((prop & 0xff) << 24) | selector;
lv_xml_anim_timeline_child_t * child = lv_ll_ins_tail(&at->anims_ll);
child->is_anim = true;
lv_anim_t * a = &child->data.anim;
anim_data_t * anim_data = lv_malloc(sizeof(anim_data_t));
anim_data->selector = selector;
anim_data->prop = prop;
anim_data->prop_type = prop_type;
lv_anim_init(a);
if(prop_type == STYLE_PROP_TYPE_INT) {
int32_t start = lv_xml_to_size(start_str);
int32_t end = lv_xml_to_size(end_str);
lv_anim_set_values(a, start, end);
lv_anim_set_custom_exec_cb(a, anim_exec_cb);
}
else if(prop_type == STYLE_PROP_TYPE_OPA) {
int32_t start = lv_xml_to_opa(start_str);
int32_t end = lv_xml_to_opa(end_str);
lv_anim_set_values(a, start, end);
lv_anim_set_custom_exec_cb(a, anim_exec_cb);
}
else if(prop_type == STYLE_PROP_TYPE_COLOR) {
anim_data->color_start = lv_xml_to_color(start_str);
anim_data->color_end = lv_xml_to_color(end_str);
lv_anim_set_values(a, 0, 255);
lv_anim_set_custom_exec_cb(a, anim_exec_cb);
}
lv_anim_set_var(a, lv_strdup(target_str));
lv_anim_set_values(a, start, end);
lv_anim_set_custom_exec_cb(a, int_anim_exec_cb);
lv_anim_set_duration(a, lv_xml_atoi(duration_str));
lv_anim_set_delay(a, lv_xml_atoi(delay_str));
lv_anim_set_early_apply(a, lv_xml_to_bool(early_apply_str));
lv_anim_set_user_data(a, (void *)((uintptr_t)selector_and_prop));
lv_anim_set_user_data(a, anim_data);
}
static void process_include_timeline_element(lv_xml_parser_state_t * state, const char ** attrs)
@@ -989,34 +1016,22 @@ static style_prop_anim_type_t style_prop_anim_get_type(lv_style_prop_t prop)
case LV_STYLE_MARGIN_RIGHT:
case LV_STYLE_MARGIN_TOP:
case LV_STYLE_MARGIN_BOTTOM:
case LV_STYLE_BG_OPA:
case LV_STYLE_BG_MAIN_STOP:
case LV_STYLE_BG_GRAD_STOP:
case LV_STYLE_BG_IMAGE_RECOLOR_OPA:
case LV_STYLE_BORDER_WIDTH:
case LV_STYLE_BORDER_OPA:
case LV_STYLE_OUTLINE_WIDTH:
case LV_STYLE_OUTLINE_OPA:
case LV_STYLE_OUTLINE_PAD:
case LV_STYLE_SHADOW_WIDTH:
case LV_STYLE_SHADOW_OFFSET_X:
case LV_STYLE_SHADOW_OFFSET_Y:
case LV_STYLE_SHADOW_SPREAD:
case LV_STYLE_SHADOW_OPA:
case LV_STYLE_TEXT_OPA:
case LV_STYLE_TEXT_LETTER_SPACE:
case LV_STYLE_TEXT_LINE_SPACE:
case LV_STYLE_IMAGE_OPA:
case LV_STYLE_IMAGE_RECOLOR_OPA:
case LV_STYLE_LINE_OPA:
case LV_STYLE_LINE_WIDTH:
case LV_STYLE_LINE_DASH_WIDTH:
case LV_STYLE_LINE_DASH_GAP:
case LV_STYLE_ARC_OPA:
case LV_STYLE_ARC_WIDTH:
case LV_STYLE_OPA:
case LV_STYLE_OPA_LAYERED:
case LV_STYLE_COLOR_FILTER_OPA:
case LV_STYLE_TRANSFORM_WIDTH:
case LV_STYLE_TRANSFORM_HEIGHT:
case LV_STYLE_TRANSLATE_X:
@@ -1027,33 +1042,52 @@ static style_prop_anim_type_t style_prop_anim_get_type(lv_style_prop_t prop)
case LV_STYLE_TRANSFORM_ROTATION:
case LV_STYLE_TRANSFORM_PIVOT_X:
case LV_STYLE_TRANSFORM_PIVOT_Y:
case LV_STYLE_RECOLOR_OPA:
return STYLE_PROP_TYPE_INT;
case LV_STYLE_ARC_OPA:
case LV_STYLE_OPA:
case LV_STYLE_OPA_LAYERED:
case LV_STYLE_BG_OPA:
case LV_STYLE_BORDER_OPA:
case LV_STYLE_OUTLINE_OPA:
case LV_STYLE_SHADOW_OPA:
case LV_STYLE_TEXT_OPA:
case LV_STYLE_LINE_OPA:
case LV_STYLE_IMAGE_OPA:
case LV_STYLE_IMAGE_RECOLOR_OPA:
case LV_STYLE_RECOLOR_OPA:
case LV_STYLE_COLOR_FILTER_OPA:
return STYLE_PROP_TYPE_OPA;
case LV_STYLE_ARC_COLOR:
case LV_STYLE_BG_COLOR:
case LV_STYLE_BG_GRAD_COLOR:
case LV_STYLE_BORDER_COLOR:
case LV_STYLE_LINE_COLOR:
case LV_STYLE_OUTLINE_COLOR:
case LV_STYLE_SHADOW_COLOR:
case LV_STYLE_TEXT_COLOR:
return STYLE_PROP_TYPE_COLOR;
default:
return STYLE_PROP_TYPE_UNKNOWN;
}
}
static int32_t anim_value_to_int(lv_style_prop_t prop_type, const char * value_str)
static void anim_exec_cb(lv_anim_t * a, int32_t v)
{
if(prop_type == STYLE_PROP_TYPE_INT) {
return lv_xml_atoi(value_str);
}
return 0;
}
static void int_anim_exec_cb(lv_anim_t * a, int32_t v)
{
uint32_t data = (lv_uintptr_t)lv_anim_get_user_data(a);
lv_style_prop_t prop = data >> 24;
lv_style_selector_t selector = data & 0x00ffffff;
anim_data_t * anim_data = lv_anim_get_user_data(a);
lv_style_value_t style_value;
style_value.num = v;
lv_obj_set_local_style_prop(a->var, prop, style_value, selector);
if(anim_data->prop_type == STYLE_PROP_TYPE_INT || anim_data->prop_type == STYLE_PROP_TYPE_OPA) {
style_value.num = v;
}
else if(anim_data->prop_type == STYLE_PROP_TYPE_COLOR) {
style_value.color = lv_color_mix(anim_data->color_end, anim_data->color_start, v);
}
lv_obj_set_local_style_prop(a->var, anim_data->prop, style_value, anim_data->selector);
}