Jerky custom animation

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

Hi,

You can upload the video to youtube as “not listed” and send the link here.

I’ve tried your code and noticed that you haven’t initialized the styles:

    static lv_style_t style1, style2;
    lv_style_init(&style1);   /*These were missing*/
    lv_style_init(&style2);

I haven’t examined your code deeply. It’d be great to see the video before that. :slight_smile: