mirror of
https://github.com/lvgl/lvgl.git
synced 2025-01-14 06:42:58 +08:00
feat(switch) add smooth animation when changing state (#2442)
* feat(switch) add smooth animation when changing state * refactor(switch) improve code quality for animation feature * refactor(switch) flatten animation structure into widget Co-authored-by: HX2003 <HX2003@users.noreply.github.com>
This commit is contained in:
parent
ea2545ae5d
commit
8ae894ebd4
@ -74,6 +74,7 @@ typedef struct {
|
||||
lv_style_t transition_delayed;
|
||||
lv_style_t transition_normal;
|
||||
lv_style_t anim;
|
||||
lv_style_t anim_fast;
|
||||
|
||||
/*Parts*/
|
||||
lv_style_t knob;
|
||||
@ -380,6 +381,9 @@ static void style_init(void)
|
||||
style_init_reset(&styles->anim);
|
||||
lv_style_set_anim_time(&styles->anim, 200);
|
||||
|
||||
style_init_reset(&styles->anim_fast);
|
||||
lv_style_set_anim_time(&styles->anim_fast, 120);
|
||||
|
||||
#if LV_USE_ARC
|
||||
style_init_reset(&styles->arc_indic);
|
||||
lv_style_set_arc_color(&styles->arc_indic, color_grey);
|
||||
@ -772,6 +776,7 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj)
|
||||
else if(lv_obj_check_type(obj, &lv_switch_class)) {
|
||||
lv_obj_add_style(obj, &styles->bg_color_grey, 0);
|
||||
lv_obj_add_style(obj, &styles->circle, 0);
|
||||
lv_obj_add_style(obj, &styles->anim_fast, 0);
|
||||
lv_obj_add_style(obj, &styles->disabled, LV_STATE_DISABLED);
|
||||
lv_obj_add_style(obj, &styles->outline_primary, LV_STATE_FOCUS_KEY);
|
||||
lv_obj_add_style(obj, &styles->bg_color_primary, LV_PART_INDICATOR | LV_STATE_CHECKED);
|
||||
@ -781,6 +786,9 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj)
|
||||
lv_obj_add_style(obj, &styles->bg_color_white, LV_PART_KNOB);
|
||||
lv_obj_add_style(obj, &styles->switch_knob, LV_PART_KNOB);
|
||||
lv_obj_add_style(obj, &styles->disabled, LV_PART_KNOB | LV_STATE_DISABLED);
|
||||
|
||||
lv_obj_add_style(obj, &styles->transition_normal, LV_PART_INDICATOR | LV_STATE_CHECKED);
|
||||
lv_obj_add_style(obj, &styles->transition_normal, LV_PART_INDICATOR);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -10,13 +10,9 @@
|
||||
|
||||
#if LV_USE_SWITCH != 0
|
||||
|
||||
/*Testing of dependencies*/
|
||||
#if LV_USE_SLIDER == 0
|
||||
#error "lv_sw: lv_slider is required. Enable it in lv_conf.h (LV_USE_SLIDER 1)"
|
||||
#endif
|
||||
|
||||
#include "../misc/lv_assert.h"
|
||||
#include "../misc/lv_math.h"
|
||||
#include "../misc/lv_anim.h"
|
||||
#include "../core/lv_indev.h"
|
||||
#include "../core/lv_disp.h"
|
||||
#include "lv_img.h"
|
||||
@ -26,6 +22,17 @@
|
||||
*********************/
|
||||
#define MY_CLASS &lv_switch_class
|
||||
|
||||
#define LV_SWITCH_IS_ANIMATING(sw) (((sw)->anim_state) != LV_SWITCH_ANIM_STATE_INV)
|
||||
|
||||
/** Switch animation start value. (Not the real value of the switch just indicates process animation)*/
|
||||
#define LV_SWITCH_ANIM_STATE_START 0
|
||||
|
||||
/** Switch animation end value. (Not the real value of the switch just indicates process animation)*/
|
||||
#define LV_SWITCH_ANIM_STATE_END 256
|
||||
|
||||
/** Mark no animation is in progress*/
|
||||
#define LV_SWITCH_ANIM_STATE_INV -1
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
@ -34,14 +41,19 @@
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
static void lv_switch_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
|
||||
static void lv_switch_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
|
||||
static void lv_switch_event(const lv_obj_class_t * class_p, lv_event_t * e);
|
||||
static void draw_main(lv_event_t * e);
|
||||
|
||||
static void lv_switch_anim_exec_cb(void * sw, int32_t value);
|
||||
static void lv_switch_trigger_anim(lv_obj_t * obj);
|
||||
static void lv_switch_anim_ready(lv_anim_t * a);
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
const lv_obj_class_t lv_switch_class = {
|
||||
.constructor_cb = lv_switch_constructor,
|
||||
.destructor_cb = lv_switch_destructor,
|
||||
.event_cb = lv_switch_event,
|
||||
.width_def = (4 * LV_DPI_DEF) / 10,
|
||||
.height_def = (4 * LV_DPI_DEF) / 17,
|
||||
@ -75,13 +87,24 @@ static void lv_switch_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj
|
||||
LV_UNUSED(class_p);
|
||||
LV_TRACE_OBJ_CREATE("begin");
|
||||
|
||||
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
|
||||
lv_obj_add_flag(obj, LV_OBJ_FLAG_CHECKABLE);
|
||||
lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
|
||||
lv_switch_t * sw = (lv_switch_t *)obj;
|
||||
|
||||
LV_TRACE_OBJ_CREATE("finished");
|
||||
sw->anim_state = LV_SWITCH_ANIM_STATE_INV;
|
||||
|
||||
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
|
||||
lv_obj_add_flag(obj, LV_OBJ_FLAG_CHECKABLE);
|
||||
lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
|
||||
|
||||
LV_TRACE_OBJ_CREATE("finished");
|
||||
}
|
||||
|
||||
static void lv_switch_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
|
||||
{
|
||||
LV_UNUSED(class_p);
|
||||
lv_switch_t * sw = (lv_switch_t *)obj;
|
||||
|
||||
lv_anim_del(sw, NULL);
|
||||
}
|
||||
|
||||
static void lv_switch_event(const lv_obj_class_t * class_p, lv_event_t * e)
|
||||
{
|
||||
@ -111,7 +134,8 @@ static void lv_switch_event(const lv_obj_class_t * class_p, lv_event_t * e)
|
||||
*s = LV_MAX(*s, knob_size);
|
||||
*s = LV_MAX(*s, lv_obj_calculate_ext_draw_size(obj, LV_PART_INDICATOR));
|
||||
}
|
||||
else if(code == LV_EVENT_CLICKED) {
|
||||
else if(code == LV_EVENT_VALUE_CHANGED) {
|
||||
lv_switch_trigger_anim(obj);
|
||||
lv_obj_invalidate(obj);
|
||||
}
|
||||
else if(code == LV_EVENT_DRAW_MAIN) {
|
||||
@ -122,6 +146,8 @@ static void lv_switch_event(const lv_obj_class_t * class_p, lv_event_t * e)
|
||||
static void draw_main(lv_event_t * e)
|
||||
{
|
||||
lv_obj_t * obj = lv_event_get_target(e);
|
||||
lv_switch_t * sw = (lv_switch_t *)obj;
|
||||
|
||||
const lv_area_t * clip_area = lv_event_get_param(e);
|
||||
lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
|
||||
|
||||
@ -131,38 +157,47 @@ static void draw_main(lv_event_t * e)
|
||||
lv_coord_t bg_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
|
||||
lv_coord_t bg_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
|
||||
|
||||
bool chk = lv_obj_get_state(obj) & LV_STATE_CHECKED;
|
||||
/*Draw the indicator in checked state*/
|
||||
if(chk) {
|
||||
/*Respect the background's padding*/
|
||||
lv_area_t indic_area;
|
||||
lv_area_copy(&indic_area, &obj->coords);
|
||||
indic_area.x1 += bg_left;
|
||||
indic_area.x2 -= bg_right;
|
||||
indic_area.y1 += bg_top;
|
||||
indic_area.y2 -= bg_bottom;
|
||||
/*Draw the indicator*/
|
||||
/*Respect the background's padding*/
|
||||
lv_area_t indic_area;
|
||||
lv_area_copy(&indic_area, &obj->coords);
|
||||
indic_area.x1 += bg_left;
|
||||
indic_area.x2 -= bg_right;
|
||||
indic_area.y1 += bg_top;
|
||||
indic_area.y2 -= bg_bottom;
|
||||
|
||||
lv_draw_rect_dsc_t draw_indic_dsc;
|
||||
lv_draw_rect_dsc_init(&draw_indic_dsc);
|
||||
lv_obj_init_draw_rect_dsc(obj, LV_PART_INDICATOR, &draw_indic_dsc);
|
||||
lv_draw_rect(&indic_area, clip_area, &draw_indic_dsc);
|
||||
}
|
||||
lv_draw_rect_dsc_t draw_indic_dsc;
|
||||
lv_draw_rect_dsc_init(&draw_indic_dsc);
|
||||
lv_obj_init_draw_rect_dsc(obj, LV_PART_INDICATOR, &draw_indic_dsc);
|
||||
lv_draw_rect(&indic_area, clip_area, &draw_indic_dsc);
|
||||
|
||||
/*Draw the knob*/
|
||||
lv_coord_t objh = lv_obj_get_height(obj);
|
||||
lv_coord_t knob_size = objh;
|
||||
lv_area_t knob_area;
|
||||
|
||||
/*Left*/
|
||||
if((base_dir != LV_BASE_DIR_RTL && !chk) || (base_dir == LV_BASE_DIR_RTL && chk)) {
|
||||
knob_area.x1 = obj->coords.x1 + bg_left;
|
||||
knob_area.x2 = knob_area.x1 + knob_size;
|
||||
lv_coord_t anim_length = obj->coords.x2 - bg_right - obj->coords.x1 - bg_left - knob_size;
|
||||
|
||||
lv_coord_t anim_value_x;
|
||||
|
||||
bool chk = lv_obj_get_state(obj) & LV_STATE_CHECKED;
|
||||
|
||||
if(LV_SWITCH_IS_ANIMATING(sw)) {
|
||||
/* Use the animation's coordinate */
|
||||
anim_value_x = (anim_length * sw->anim_state) / LV_SWITCH_ANIM_STATE_END;
|
||||
}
|
||||
else {
|
||||
knob_area.x2 = obj->coords.x2 - bg_right;
|
||||
knob_area.x1 = knob_area.x2 - knob_size;
|
||||
/* Use LV_STATE_CHECKED to decide the coordinate */
|
||||
anim_value_x = chk ? anim_length : 0;
|
||||
}
|
||||
|
||||
if(base_dir == LV_BASE_DIR_RTL) {
|
||||
anim_value_x = anim_length - anim_value_x;
|
||||
}
|
||||
|
||||
knob_area.x1 = obj->coords.x1 + bg_left + anim_value_x;
|
||||
knob_area.x2 = knob_area.x1 + knob_size;
|
||||
|
||||
knob_area.y1 = obj->coords.y1 + bg_top;
|
||||
knob_area.y2 = obj->coords.y2 - bg_bottom;
|
||||
|
||||
@ -182,7 +217,66 @@ static void draw_main(lv_event_t * e)
|
||||
lv_obj_init_draw_rect_dsc(obj, LV_PART_KNOB, &knob_rect_dsc);
|
||||
|
||||
lv_draw_rect(&knob_area, clip_area, &knob_rect_dsc);
|
||||
|
||||
}
|
||||
|
||||
static void lv_switch_anim_exec_cb(void * var, int32_t value)
|
||||
{
|
||||
lv_switch_t * sw = var;
|
||||
sw->anim_state = value;
|
||||
lv_obj_invalidate((lv_obj_t *)sw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the switch's animation state to "no animation in progress".
|
||||
*/
|
||||
static void lv_switch_anim_ready(lv_anim_t * a)
|
||||
{
|
||||
lv_switch_t * sw = a->var;
|
||||
sw->anim_state = LV_SWITCH_ANIM_STATE_INV;
|
||||
lv_obj_invalidate((lv_obj_t *)sw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an animation for the switch knob. if the anim_time style property is greater than 0
|
||||
* @param obj the switch to animate
|
||||
*/
|
||||
static void lv_switch_trigger_anim(lv_obj_t * obj)
|
||||
{
|
||||
LV_ASSERT_OBJ(obj, MY_CLASS);
|
||||
lv_switch_t * sw = (lv_switch_t *)obj;
|
||||
|
||||
uint32_t anim_dur_full = lv_obj_get_style_anim_time(obj, LV_PART_MAIN);
|
||||
|
||||
if(anim_dur_full>0) {
|
||||
bool chk = lv_obj_get_state(obj) & LV_STATE_CHECKED;
|
||||
int32_t anim_start;
|
||||
int32_t anim_end;
|
||||
/*No animation in progress -> simply set the values*/
|
||||
if(sw->anim_state == LV_SWITCH_ANIM_STATE_INV) {
|
||||
anim_start = chk ? LV_SWITCH_ANIM_STATE_START : LV_SWITCH_ANIM_STATE_END;
|
||||
anim_end = chk ? LV_SWITCH_ANIM_STATE_END : LV_SWITCH_ANIM_STATE_START;
|
||||
}
|
||||
/*Animation in progress. Start from the animation end value*/
|
||||
else {
|
||||
anim_start = sw->anim_state;
|
||||
anim_end = chk ? LV_SWITCH_ANIM_STATE_END : LV_SWITCH_ANIM_STATE_START;
|
||||
}
|
||||
/*Calculate actual animation duration*/
|
||||
uint32_t anim_dur = (anim_dur_full * LV_ABS(anim_start - anim_end))/LV_SWITCH_ANIM_STATE_END;
|
||||
|
||||
/*Stop the previous animation if it exists*/
|
||||
lv_anim_del(sw, NULL);
|
||||
|
||||
lv_anim_t a;
|
||||
lv_anim_init(&a);
|
||||
lv_anim_set_var(&a, sw);
|
||||
lv_anim_set_exec_cb(&a, lv_switch_anim_exec_cb);
|
||||
lv_anim_set_values(&a, anim_start, anim_end);
|
||||
lv_anim_set_ready_cb(&a, lv_switch_anim_ready);
|
||||
lv_anim_set_time(&a, anim_dur);
|
||||
lv_anim_start(&a);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -17,11 +17,6 @@ extern "C" {
|
||||
|
||||
#if LV_USE_SWITCH != 0
|
||||
|
||||
/*Testing of dependencies*/
|
||||
#if LV_USE_SLIDER == 0
|
||||
#error "lv_switch: lv_slider is required. Enable it in lv_conf.h (LV_USE_SLIDER 1)"
|
||||
#endif
|
||||
|
||||
#include "../core/lv_obj.h"
|
||||
|
||||
/*********************
|
||||
@ -34,6 +29,7 @@ extern "C" {
|
||||
|
||||
typedef struct {
|
||||
lv_obj_t obj;
|
||||
int32_t anim_state;
|
||||
}lv_switch_t;
|
||||
|
||||
extern const lv_obj_class_t lv_switch_class;
|
||||
|
Loading…
x
Reference in New Issue
Block a user