Description
I’m trying to implement a custom animation but the result is a jerky animation.
I also attach the code used to generate the video animation and the lvgl configuration file.
I used masks and canvas to create the animation, is this the best solution?
What MCU/Processor/Board and compiler are you using?
I’m using a Virtual Machine (Virtual Box) with Ubuntu x64 and gcc
What LVGL version are you using?
I’m using LVGL v7.4.0
What do you want to achieve?
I want a smooth animation with no lags
What have you tried so far?
Code to reproduce
/**
* @file test_widget.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lvgl.h"
#include "lv_conf.h"
#include "lv_drv_conf.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
/*********************
* DEFINES
*********************/
#define BACKGROUND_WIDTH LV_HOR_RES_MAX
#define BACKGROUND_HEIGHT LV_VER_RES_MAX
#define ANIMATION_DURATION_MS 3000
/**********************
* TYPEDEFS
**********************/
typedef enum {NO_ACTION, TO_BORDER, TO_CENTER} anim_status_type_t;
typedef struct
{
int percent; //100 closed, 0 opened
anim_status_type_t status;
} anim_status_t;
/**********************
* VARIABLES
**********************/
static lv_obj_t* canvas;
static lv_obj_t* img_obj;
static lv_obj_t* white_string_obj;
static lv_obj_t* black_string_obj;
static lv_obj_t* external_mask;
static lv_obj_t* internal_mask;
static lv_objmask_mask_t* external_mask_t;
static lv_objmask_mask_t* internal_mask_t;
static lv_draw_mask_radius_param_t external_mask_par;
static lv_draw_mask_radius_param_t internal_mask_par;
static anim_status_t anim_status;
static lv_anim_t anim;
/**********************
* STATIC PROTOTYPES
**********************/
static void init_widget(lv_obj_t* parent);
static void moving_animation(lv_obj_t* obj, lv_anim_value_t value);
/**********************
* FUNCTIONS
**********************/
uint32_t custom_tick_get(void)
{
static uint64_t start_ms = 0;
if(start_ms == 0) {
struct timeval tv_start;
gettimeofday(&tv_start, NULL);
start_ms = (tv_start.tv_sec * 1000000 + tv_start.tv_usec) / 1000;
}
struct timeval tv_now;
gettimeofday(&tv_now, NULL);
uint64_t now_ms;
now_ms = (tv_now.tv_sec * 1000000 + tv_now.tv_usec) / 1000;
uint32_t time_ms = now_ms - start_ms;
return time_ms;
}
void moving_to_border()
{
if(100 == anim_status.percent || TO_CENTER == anim_status.status)
{
if(TO_CENTER == anim_status.status)
{
printf("Stop animation -> <-\n");
lv_anim_del(&anim, NULL);
}
anim_status.status = TO_BORDER;
printf("Start animation <- ->\n");
lv_anim_set_exec_cb(&anim, (lv_anim_exec_xcb_t)moving_animation);
lv_anim_set_values(&anim, anim_status.percent, 0);
lv_anim_set_time(&anim, ANIMATION_DURATION_MS*anim_status.percent/100);
lv_anim_start(&anim);
}
else
{
if(TO_BORDER == anim_status.status)
printf("Animation status is: |<- ->|\n");
else
printf("Animation status is: already |<- ->|\n");
}
}
void moving_to_center()
{
if(0 == anim_status.percent || TO_BORDER == anim_status.status)
{
if(TO_BORDER == anim_status.status)
{
printf("Stop animation <- ->\n");
lv_anim_del(&anim, NULL);
}
anim_status.status = TO_CENTER;
printf("Start animation -> <-\n");
lv_anim_set_exec_cb(&anim, (lv_anim_exec_xcb_t)moving_animation);
lv_anim_set_values(&anim, anim_status.percent, 100);
lv_anim_set_time(&anim, ANIMATION_DURATION_MS*(100-anim_status.percent)/100);
lv_anim_start(&anim);
}
else
{
if(TO_CENTER == anim_status.status)
printf("Animation status is: ->||<-\n");
else
printf("Animation status is: already ->||<-\n");
}
}
/**********************
* GLOBAL FUNCTIONS
**********************/
void test_widget(void)
{
static lv_color_t cbuf[LV_CANVAS_BUF_SIZE_TRUE_COLOR(BACKGROUND_WIDTH, BACKGROUND_HEIGHT)];
canvas = lv_canvas_create(lv_scr_act(), NULL);
lv_canvas_set_buffer(canvas, cbuf, BACKGROUND_WIDTH, BACKGROUND_HEIGHT, LV_IMG_CF_TRUE_COLOR);
lv_obj_align(canvas, NULL, LV_ALIGN_CENTER, 0, 0);
lv_canvas_fill_bg(canvas, LV_COLOR_BLACK, LV_OPA_COVER);
init_widget(canvas);
anim_status.percent = 100;
anim_status.status = NO_ACTION;
lv_anim_init(&anim);
}
void init_widget(lv_obj_t* parent)
{
lv_draw_rect_dsc_t rectangle;
lv_draw_rect_dsc_init(&rectangle);
rectangle.bg_color = LV_COLOR_WHITE;
int x = 540;
int y = 180;
int w = 0;
int h = 280;
lv_canvas_draw_rect(parent, x, y, 2*w, h, &rectangle);
static lv_style_t style1, style2;
lv_style_set_text_font(&style1, LV_STATE_DEFAULT, &montserrat_ultra_light_360);
lv_style_set_bg_opa(&style1, LV_STATE_DEFAULT, LV_OPA_TRANSP);
lv_style_set_text_color(&style1, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_style_set_text_opa(&style1, LV_STATE_DEFAULT, LV_OPA_COVER);
lv_style_copy(&style2, &style1);
lv_style_set_text_color(&style2, LV_STATE_DEFAULT, LV_COLOR_BLACK);
external_mask = lv_objmask_create(canvas, NULL);
lv_obj_set_pos(external_mask, 240, 100);
lv_obj_set_size(external_mask, 300, 280);
internal_mask = lv_objmask_create(canvas, NULL);
lv_obj_set_pos(internal_mask, 240, 100);
lv_obj_set_size(internal_mask, 300, 280);
white_string_obj = lv_label_create(internal_mask, NULL);
lv_obj_set_pos(white_string_obj, 25, -80);
lv_obj_set_size(white_string_obj, 300, 280);
lv_obj_reset_style_list(white_string_obj, LV_OBJ_PART_MAIN);
lv_obj_add_style(white_string_obj, LV_OBJ_PART_MAIN, &style1);
lv_label_set_align(white_string_obj, LV_LABEL_ALIGN_CENTER);
black_string_obj = lv_label_create(external_mask, NULL);
lv_obj_set_pos(black_string_obj, 25, -80);
lv_obj_set_size(black_string_obj, 300, 280);
lv_obj_reset_style_list(black_string_obj, LV_OBJ_PART_MAIN);
lv_obj_add_style(black_string_obj, LV_OBJ_PART_MAIN, &style2);
lv_label_set_align(black_string_obj, LV_LABEL_ALIGN_CENTER);
lv_label_set_text(white_string_obj, "A");
lv_label_set_text(black_string_obj, "A");
lv_area_t a;
a.x1 = 0;
a.y1 = 0;
a.x2 = 300;
a.y2 = 280;
lv_draw_mask_radius_init(&internal_mask_par, &a, 0, false);
internal_mask_t = lv_objmask_add_mask(internal_mask, &internal_mask_par);
lv_draw_mask_radius_init(&external_mask_par, &a, 0, true);
external_mask_t = lv_objmask_add_mask(external_mask, &external_mask_par);
}
void moving_animation(lv_obj_t* obj, lv_anim_value_t value)
{
lv_draw_rect_dsc_t rectangle;
lv_draw_rect_dsc_init(&rectangle);
static int x = 240;
static int y = 100;
static int w = 150;
static int h = 280;
double percent = value/100.0;
rectangle.bg_color = LV_COLOR_BLACK;
lv_canvas_draw_rect(canvas, x, y, 2*w, h, &rectangle);
int new_value;
if(percent < 0.5)
{
double percent2 = percent*2.0;
new_value = 2*(value)*150/100;
//left rectangle
rectangle.bg_color = LV_COLOR_WHITE;
lv_canvas_draw_rect(canvas, x, y, (int)(w*percent2), h, &rectangle);
//right rectangle
rectangle.bg_color = LV_COLOR_WHITE;
lv_canvas_draw_rect(canvas, (int)(x+w*(2.0-percent2)), y, (int)(w*percent2), h, &rectangle);
external_mask_par.cfg.outer = 1;
internal_mask_par.cfg.outer = 0;
}
else
{
double percent2 = (percent-0.5)*2.0;
new_value = 2*(value-50)*150/100 +1;
//left rectangle
rectangle.bg_color = LV_COLOR_WHITE;
lv_canvas_draw_rect(canvas, x+w*percent2+1, y, w*(1.0-percent2), h, &rectangle);
//right rectangle
rectangle.bg_color = LV_COLOR_WHITE;
lv_canvas_draw_rect(canvas, x+w, y, w*(1.0-percent2), h, &rectangle);
external_mask_par.cfg.outer = 0;
internal_mask_par.cfg.outer = 1;
}
if(anim_status.percent < value)
{
anim_status.status = TO_CENTER;
}
else if(anim_status.percent > value)
{
anim_status.status = TO_BORDER;
}
anim_status.percent = value;
if(anim_status.percent == 0 || anim_status.percent == 100)
{
printf("Animation status is: %s\n", (anim_status.status == TO_CENTER) ? "->||<-" : "|<- ->|");
anim_status.status = NO_ACTION;
}
external_mask_par.cfg.rect.x1 = new_value;
external_mask_par.cfg.rect.x2 = 300 - new_value;
internal_mask_par.cfg.rect.x1 = new_value;
internal_mask_par.cfg.rect.x2 = 300 - new_value;
lv_objmask_update_mask(external_mask, external_mask_t, &external_mask_par);
lv_objmask_update_mask(internal_mask, internal_mask_t, &internal_mask_par);
}
Screenshot and/or video
I have a video of the issue but I can’t attach it (new user).
Thanks for the support