txtlite控件

1. 使用场景

txtlite 是基于 LVGL + VGLite 硬件加速实现的高性能文本/图形渲染控件,核心面向嵌入式UI场景,尤其适用于以下需求:

  • 需要3D变换效果(如旋转、缩放、位移)的文本展示场景;

  • 需实现丰富动画效果(飞入、跳跃、环形运动、缩放等)的UI交互场景;

2. 接口介绍

接口函数

功能说明

参数说明

lv_obj_t *lv_txtlite_create(lv_obj_t *parent)

创建txtlite自定义控件实例,基于LVGL图像对象扩展,是使用控件的基础入口

parent:父LVGL对象指针(如屏幕、容器)

void lv_txtlite_init(lv_obj_t *txtlite, lv_coord_t w, lv_coord_t h, lv_img_cf_t cf, uint32_t flag)

初始化控件核心渲染参数,创建控件后必须立即调用

txtlite:已创建的控件实例指针;w:控件宽度(LVGL坐标单位);h:控件高度(LVGL坐标单位);cf:帧缓冲颜色格式(需匹配VGLite硬件支持格式);flag:初始化标志位(BIT0:异步渲染;BIT1:硬件加速;BIT2:双缓冲;BIT3:抗锯齿)

void lv_txtlite_refr(lv_obj_t *txtlite)

强制刷新控件渲染内容,手动触发全量重绘(文本、多边形、动画)

txtlite:已初始化的控件实例指针

void lv_txtlite_set_bg_color(lv_obj_t *txtlite, vg_lite_color_t color)

设置控件渲染区域的纯色背景色

txtlite:已初始化的控件实例指针;color:VGLite格式背景色(包含r/g/b/a分量)

lv_txtlite_item_t *lv_txtlite_add_face_polygon(lv_obj_t *txtlite, const vg_lite_vertex_t *point, uint16_t cnt, uint32_t color, void *uset_data)

添加多边形面到控件,基于VGLite硬件加速渲染填充多边形

txtlite:已初始化的控件实例指针;point:多边形顶点数组(x/y/z坐标);cnt:顶点数量(≥3且≤CUBE_POINT_MAX);color:32位RGBA填充色;uset_data:用户自定义数据指针

void lv_txtlite_add_txt(lv_obj_t *txtlite, const char *txt, lv_align_t align, lv_coord_t ofs_x, lv_coord_t ofs_y)

添加文本项到控件,结合LVGL文本渲染和VGLite加速

txtlite:已初始化的控件实例指针;txt:待显示的以’\0’结尾的文本串(支持ASCII/UTF-8);align:文本相对控件的对齐方式;ofs_x:水平偏移量;ofs_y:垂直偏移量

void lv_txtlite_set_rot(lv_obj_t *txtlite, vg_lite_matrix_4x4_t *rot)

设置控件整体3D旋转矩阵,实现3D旋转和透视效果

txtlite:已初始化的控件实例指针;rot:4x4旋转/变换矩阵指针

void lv_txtlite_get_rot(lv_obj_t *txtlite, vg_lite_matrix_4x4_t *rot)

获取控件当前3D旋转矩阵,读取旋转状态

txtlite:已初始化的控件实例指针;rot:存储矩阵的输出指针(需预分配)

void lv_txtlite_set_zoom(lv_obj_t *txtlite, float scale)

设置控件整体缩放因子,覆盖现有缩放动画(直至重启动画)

txtlite:已初始化的控件实例指针;scale:缩放因子(≥0.1,1.0为原始尺寸,负数忽略)

float lv_txtlite_get_zoom(lv_obj_t *txtlite)

获取控件当前缩放因子(含静态设置/动画动态值)

txtlite:已初始化的控件实例指针

void lv_txtlite_reset_pos(lv_obj_t *txtlite, uint32_t flag)

重置控件位置/变换状态到默认值

txtlite:已初始化的控件实例指针;flag:重置标志位(BIT0:位置;BIT1:旋转矩阵;BIT2:缩放;BIT3:全部)

void lv_txtlite_save_pos(lv_obj_t *txtlite, uint32_t flag)

保存控件当前位置/变换状态到内部缓冲区

txtlite:已初始化的控件实例指针;flag:保存标志位(BIT0:位置;BIT1:旋转矩阵;BIT2:缩放;BIT3:全部)

lv_txtlite_fly_in_para_t *lv_txtlite_add_fly_in_anim(lv_obj_t *txtlite, uint32_t start_time, uint32_t dur_time, uint32_t flag)

添加飞入动画,控件从起始位置飞至当前位置

txtlite:已初始化的控件实例指针;start_time:动画启动延迟(ms);dur_time:动画时长(ms,≥10);flag:动画标志位(轴/循环/缓动等)

lv_txtlite_move_para_t *lv_txtlite_add_move_anim(lv_obj_t *txtlite, uint32_t start_time, uint32_t dur_time, uint32_t flag)

添加线性移动动画,控件匀速从当前位置移至目标位置

txtlite:已初始化的控件实例指针;start_time:动画启动延迟(ms);dur_time:动画时长(ms,≥10);flag:动画标志位(轴/循环/乒乓模式等)

lv_txtlite_jump_para_t *lv_txtlite_add_jump_anim(lv_obj_t *txtlite, uint32_t start_time, uint32_t dur_time, uint32_t flag)

添加跳跃动画,控件沿抛物线轨迹弹跳

txtlite:已初始化的控件实例指针;start_time:动画启动延迟(ms);dur_time:动画时长(ms,≥50);flag:动画标志位(2D/3D/循环/重力模拟等)

lv_txtlite_zoom_para_t *lv_txtlite_add_zoom_anim(lv_obj_t *txtlite, uint32_t start_time, uint32_t dur_time, uint32_t flag)

添加缩放动画,控件从当前尺寸缩放到目标尺寸

txtlite:已初始化的控件实例指针;start_time:动画启动延迟(ms);dur_time:动画时长(ms,≥10);flag:动画标志位(均匀/非均匀缩放/循环/缓动等)

lv_txtlite_ring_para_t *lv_txtlite_add_ring_anim(lv_obj_t *txtlite, uint32_t start_time, uint32_t dur_time, uint32_t flag)

添加环形动画,为控件添加环形运动效果

txtlite:已初始化的控件实例指针;start_time:动画启动延迟(ms);dur_time:动画时长(ms);flag:动画控制标志位

void lv_txtlite_start_anim(lv_obj_t *txtlite)

启动所有已添加的动画,按添加顺序执行

txtlite:已初始化的控件实例指针

void lv_txtlite_clear_all_anim(lv_obj_t *txtlite)

停止并清除所有动画,保留控件当前状态

txtlite:已初始化的控件实例指针

3. 使用案例

3.1 环形转动歌词动效


#define VG_TXTLITE_MOVE  LV_SCENE_STYLE_CUSTOMER
#define VG_TXTLITE_RING  (LV_SCENE_STYLE_CUSTOMER + 1)

void txtlite_add_lyric(lv_obj_t *txtlite, const char *str)
{
    if (!str || !str[0]) return;
    int32_t child_cnt = lv_obj_get_child_cnt(txtlite);
    while (child_cnt > 1)
    {
        lv_obj_t *child = lv_obj_get_child(txtlite, 0);
        lv_obj_del(child);
        child_cnt--;
    }

    float target_z = -5 * LV_HOR_RES_MAX / LV_VER_RES_MAX;
    float target_x = target_z * 1.5f;

    for (uint32_t i = 0; i < child_cnt; i++)
    {
        lv_obj_t *child = lv_obj_get_child(txtlite, i);
        lv_txtlite_clear_all_anim(child);
        if (VG_TXTLITE_MOVE == music_get_nvm()->lyric_style)
            lv_txtlite_reset_pos(child, 0x2);

        vg_lite_matrix_4x4_t rot;
        lv_txtlite_get_rot(child, &rot);

        float z = target_z * (child_cnt - i) - rot.m[2][3];
        float x = target_x * (child_cnt - i) - rot.m[0][3];
        lv_txtlite_move_para_t *p_para = NULL;
        p_para = lv_txtlite_add_move_anim(child, 0, 1000, 0);
        p_para->offset.x = x;
        p_para->offset.y = 1;
        p_para->offset.z = z;
        p_para->x_deg = 0;
        p_para->y_deg = 0;
        p_para->z_deg = 0;
        lv_txtlite_start_anim(child);
    }

    lv_obj_t *sub = lv_txtlite_create(txtlite);
    const lv_font_t *font = lv_obj_get_style_text_font(sub, 0);
    if (music_lyric->font_cnt)
    {
        char *font_name = (char *)music_lyric->font_names[0];
#if LV_HOR_RES_MAX < 600
        uint16_t font_size = music_lyric->font_size_arr[LV_SCENE_FONT_BIG];
#else
        uint16_t font_size = music_lyric->font_size_arr[LV_SCENE_FONT_HUG];
#endif
        font = lvsf_get_font_by_name(font_name, font_size);
        lv_obj_set_style_text_font(sub, font, 0);
    }

    if (VG_TXTLITE_MOVE == music_get_nvm()->lyric_style)
    {
        uint32_t str_len = strlen(str);
        char *str_buf = app_calloc(1, str_len + 16);
        RT_ASSERT(str_buf);
        uint32_t src_offset =  0;
        uint32_t dst_offset =  0;
        uint32_t line_cnt = 0;
        for (uint32_t i = 0; i < 15 && str[src_offset]; i++)
        {
            uint32_t len = _lv_txt_get_next_line(&str[src_offset], font, 0, LV_HOR_RES_MAX * 3 / 4, NULL, 0);
            rt_memcpy(str_buf + dst_offset, &str[src_offset], len);
            dst_offset += len;
            str_buf[dst_offset] = 0;
            dst_offset++;
            src_offset += len;
            line_cnt++;
        }

        lv_coord_t pos_y = ((line_cnt - 1) * font->line_height) >> 1;
        dst_offset = 0;
        for (uint32_t i = 0; i < line_cnt; i++)
        {
            lv_txtlite_add_txt(sub, &str_buf[dst_offset], LV_ALIGN_CENTER, 0, pos_y);
            dst_offset += strlen(&str_buf[dst_offset]) + 1;
            pos_y -= font->line_height;
        }
        app_free(str_buf);

        vg_lite_matrix_4x4_t rot;
        vg_lite_identity_4x4(&rot);
        lv_coord_t angle_z = rand() % 30 - 15;
        angle_z = angle_z > 0 ? (angle_z + 15) : (angle_z - 15);
        vg_lite_rotate_z(angle_z, &rot);
        lv_coord_t angle_x = rand() % 60 - 30;
        angle_x = angle_x > 0 ? (angle_x + 20) : (angle_x - 20);
        vg_lite_rotate_x(angle_x, &rot);
        vg_lite_rotate_y(rand() % 20 - 10, &rot);
        lv_txtlite_set_rot(sub, &rot);

        lv_txtlite_fly_in_para_t *p_para = NULL;
        p_para = lv_txtlite_add_fly_in_anim(sub, 0, 1000, 0);
        p_para->offset.x = 2.f;
        p_para->offset.y = rand() % 300 / 100 - 1.5f;
        p_para->offset.z = 0;

        lv_txtlite_add_zoom_anim(sub, 1000, 3000, LV_TXTLITE_ANIM_LOOP);
        lv_txtlite_start_anim(sub);

    }
    else if (VG_TXTLITE_RING == music_get_nvm()->lyric_style)
    {
        lv_txtlite_add_txt(sub, str, LV_ALIGN_CENTER, 0, FONT_NORMAL);
        lv_txtlite_ring_para_t *p_para = NULL;
        p_para = lv_txtlite_add_ring_anim(sub, 0, 1000, LV_TXTLITE_ANIM_LOOP);
        p_para->angle_v = 1.f;
        p_para->norm.x = 0.1f + rand() % 90 / (float)100;
        p_para->norm.y = rand() % 100 / (float)100;
        p_para->norm.z = rand() % 100 / (float)100;
        lv_txtlite_start_anim(sub);
    }

    lv_txtlite_start_anim(txtlite);

}


lv_obj_t *music_create_txtlite(lv_obj_t *parent)
{
	//同步刷新
	lv_obj_set_render_async(lv_scr_act(), 0);
	
	lv_obj_t *img_bg = lv_img_create(parent);
	lv_img_set_src(img_bg, APP_GET_IMG(img_txt_bg));
	lv_coord_t w = lv_obj_get_self_width(img_bg);
	if (w && w != LV_HOR_RES_MAX)
		lv_img_set_zoom(img_bg, LV_HOR_RES_MAX * LV_IMG_ZOOM_NONE / w);
	lv_obj_center(img_bg);

	lv_obj_t *txtlite = lv_txtlite_create(parent);
	lv_txtlite_init(txtlite, LV_HOR_RES_MAX, LV_VER_RES_MAX, LV_IMG_CF_ALPHA_8BIT, 0);
	lv_obj_update_layout(txtlite);
	lv_obj_center(txtlite);
	lv_ext_set_local_font(txtlite, FONT_HUGE, LV_COLOR_RED);
	lv_obj_set_style_img_recolor_opa(txtlite, LV_OPA_100, 0);
	lv_obj_set_style_img_recolor(txtlite, LV_COLOR_RED, 0);

	music_lyric->txtlite = txtlite;
	//、、、、
}

3.2 案例效果展示

fishy

4. 注意事项

4.1 初始化相关

  1. 必须在 lv_txtlite_create 后立即调用 lv_txtlite_init,否则控件无法正常渲染;

  2. 多次调用 lv_txtlite_init 会覆盖原有参数并重置控件状态,需避免重复调用。

  3. 文本渲染默认使用LVGL默认字体,可通过LVGL字体接口(lv_font_set_default)修改;

  4. 旋转矩阵需符合VGLite右手坐标系规范(默认Y轴向下),非法矩阵(如奇异矩阵)会导致渲染异常;

  5. 清空动画(lv_txtlite_clear_all_anim)仅停止动画,不会重置控件位置/缩放状态。