How do I create a fill animation

Description

I have a need to create a custom animation. I want to create the effect of a glass being filled with a liquid in an animation. So I need to simply change the color of pixels inside the glass in an animation.

What MCU/Processor/Board and compiler are you using?

i.MXRT1050EVK – IAR v8.50

What LVGL version are you using?

v7.0.1

What do you want to achieve?

Fill animation

What have you tried so far?

  1. I tried creating lines at each Y point (bounded by the X coordinates I gave it) using the lv_line_create. That just resulted in a singled line that moved around the screen.

  2. I tried using the lv_draw_line to draw lines between the X bounds at different Y levels. That didn’t work either.

  3. I have tried changing the colors in the display buffers. This has been my most successful attempt as I can create a filled area of whatever shape I want. However, it still only seems to leave a single line at the end of the animation. The code that I have used for this is shown below. This one is the most perplexing to me.

Code to reproduce

#define LV_COLOR_BROWN  LV_COLOR_MAKE(0x86, 0x64, 0x42)

static void cafeBrewStart_cb(lv_obj_t * btn, lv_event_t event)
{
    uint32_t llY, ulY, lrY, urY, tmp32;
    uint32_t startY, endY;
    
    if(event == LV_EVENT_CLICKED) {
        
        /*Create an array for the points of the line*/
//        // Call brewing animation here
#if LV_USE_ANIMATION == 1
        // Get Y values of start and end points of the lines
        llY = demo_ui.CafeBrewScr_cupLeftSide->coords.y1;
        ulY = demo_ui.CafeBrewScr_cupLeftSide->coords.y2;
        
        if(llY < ulY) {
            tmp32 = ulY;
            ulY = llY;
            llY = tmp32;
        }
        
        lrY = demo_ui.CafeBrewScr_cupRightSide->coords.y1;
        urY = demo_ui.CafeBrewScr_cupRightSide->coords.y2;
        
        if(lrY < urY) {
            tmp32 = urY;
            urY = lrY;
            lrY = tmp32;
        }
        
        // Set start and end Y coords.
        if(llY > lrY) {
            startY = llY;
        } else {
            startY = lrY;
        }
        
        if(ulY > urY) {
            endY = ulY;
        } else {
            endY = urY;
        }
        
        // Setup the animation
        lv_anim_init(&brew_a);
                
        // Since I'm executing a custom function, I don't think it really 
        // matters what I animate.
        lv_anim_set_var(&brew_a, demo_ui.CafeHmScr);
                
        lv_anim_set_custom_exec_cb(&brew_a, (lv_anim_custom_exec_cb_t) animateCoffeeCupFill);
                
        lv_anim_set_time(&brew_a, 6000);
                
        lv_anim_set_values(&brew_a, startY, endY);
                
        lv_anim_set_start_cb(&brew_a, brew_animation_start_cb);
        
        lv_anim_set_ready_cb(&brew_a, brew_animation_end_cb);
                
        lv_anim_start(&brew_a); 
                
#endif
}

void animateCoffeeCupFill(lv_obj_t * obj, lv_coord_t Ynew)
{
    uint32_t tmpX1, tmpX2;
    lv_point_t leftpoint, rightpoint;
    lv_disp_t * lDisp;
    lv_coord_t xRes, yRes;
    lv_disp_buf_t * dispBuf;
    lv_color_t * mem1;
    lv_color_t * mem2;
    lv_color_t * mem3;
    
    lDisp = _lv_refr_get_disp_refreshing();
    dispBuf = lv_disp_get_buf(lDisp);
    mem1 = (lv_color_t *)dispBuf->buf_act;
    mem2 = (lv_color_t *)dispBuf->buf1;
    mem3 = (lv_color_t *)dispBuf->buf2;
    
    // Animate coffee cup
    // Get point one of the line (Left line)
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    tmpX1 = demo_ui.CafeBrewScr_cupLeftSide->coords.x1;
    if(tmpX1 < demo_ui.CafeBrewScr_cupLeftSide->coords.x2) {
        tmpX1 = demo_ui.CafeBrewScr_cupLeftSide->coords.x2;
    }
    
    leftpoint.x = tmpX1;
    leftpoint.y = Ynew;
    
    // Get point two of the line (Right line)
    tmpX2 = demo_ui.CafeBrewScr_cupRightSide->coords.x1;
    if(tmpX2 > demo_ui.CafeBrewScr_cupRightSide->coords.x2) {
        tmpX2 = demo_ui.CafeBrewScr_cupRightSide->coords.x2;
    }
    
    rightpoint.x = tmpX2;
    rightpoint.y = Ynew;
    
        for(tmpX1 = leftpoint.x; tmpX1 < rightpoint.x; tmpX1++) {
        mem1[(Ynew * DEMO_PANEL_WIDTH + tmpX1)] = LV_COLOR_BROWN;
        mem2[(Ynew * DEMO_PANEL_WIDTH + tmpX1)] = LV_COLOR_BROWN;
        mem3[(Ynew * DEMO_PANEL_WIDTH + tmpX1)] = LV_COLOR_BROWN;
    }
}

Screenshot and/or video

N/A at this time.

Manipulating LVGL buffers to draw things is very unstable and will cause rendering issues. I strongly suggest using the standard object APIs instead.

I assume your coffee cup is an image. You could make it into a transparent overlay and create an lv_obj behind it that was brown. You could then change the Y coordinate and move that up to create the illusion of “filling” the coffee cup.

Thanks embeddedt.

On that note, why did creating a line using the lv_line_create or lv_draw_line not work? The same animation method was used but instead of modifying the LVGL buffers in the animateCoffeeCupFill function I created a line.

Thanks,
Chris

lv_draw_line is also an internal API and should be avoided in favor of the objects API.

I assume the reason the lv_line_create line moved around the screen is because you changed the points array each time instead of creating one for each line.

If you’re going to use many lines, though, it would make just as much sense (and be more efficient) to use a solidly filled object and change its size and/or position.