multanim控件¶
1. 使用场景¶
场景切换过程中,同一种动画可能反复用到,比如APP切换、TLV切换、表盘切换等。为提到代码重复利用率,方便使用,本控件将常用的动画进行了封装,应用程序只需调用统一的接口,即可实现相应的动画。封装的动画主要有缩放动画、3D翻转动画、轴向压缩动画,mask渐隐动画等
2. 功能介绍¶
2.1 动画类型¶
enum
{
LV_MULTANIM_NONE, //无效动画
LV_MULTANIM_ZOOM, //缩放动画
LV_MULTANIM_3D, //3D翻转动画
LV_MULTANIM_SWITCH, //反向翻转并带一定缩放
LV_MULTANIM_TURN, //半页翻转动画
LV_MULTANIM_SCALE, //轴向压缩动画
LV_MULTANIM_FADE, //mask渐隐动画
LV_MULTANIM_OPEN, //中间向两侧打开的mask动画
LV_MULTANIM_OPEN, //预留,暂不支持
LV_MULTANIM_ROLL, //预留,暂不支持
LV_MULTANIM_DRAG, //预留,暂不支持
LV_MULTANIM_BOOK, //翻书动画,仅58X支持
LV_MULTANIM_SHUTTLE, //飞梭动画,仅58X支持
LV_MULTANIM_SHUTTER, //百叶窗动画,仅58X支持
LV_MULTANIM_MAX, //无效
};
typedef uint16_t lv_multanim_type;
//目前已封装的动画类型包含以上几种,
2.2 接口说明¶
接口函数 |
功能说明 |
参数说明 |
返回值说明 |
|---|---|---|---|
lv_multanim_create(lv_obj_t *parent) |
创建一个multanim动画实例 |
parent:父对象指针,新创建的multanim实例将作为该对象的子对象 |
成功返回创建的multanim对象指针,失败返回NULL |
lv_multanim_set_type(lv_obj_t *multanim, lv_multanim_type type) |
设置动画类型(需在启动动画前设置) |
multanim:multanim实例指针;type:动画类型(如LV_MULTANIM_ZOOM、LV_MULTANIM_FADE等) |
返回设置前的动画类型 |
lv_multanim_set_dir(lv_obj_t *multanim, lv_multanim_dir dir) |
设置动画方向 |
multanim:multanim实例指针;dir:动画方向(如LV_MULTANIM_LEFT、LV_MULTANIM_TOP等,支持组合) |
无 |
lv_multanim_set_major_img(lv_obj_t *multanim, lv_obj_t *major_img) |
设置动画的主图像(必须设置) |
multanim:multanim实例指针;major_img:主图像对象指针 |
无 |
lv_multanim_set_minor_img(lv_obj_t *multanim, lv_obj_t *minor_img) |
设置动画的次要图像 |
multanim:multanim实例指针;minor_img:次要图像对象指针 |
无 |
lv_multanim_set_range(lv_obj_t *multanim, int32_t range) |
设置动画的范围 |
multanim:multanim实例指针;range:动画范围值 |
无 |
lv_multanim_set_viewpoint(lv_obj_t *multanim, lv_point_t *start_v, lv_point_t *end_v) |
设置动画的视角起止点 |
multanim:multanim实例指针;start_v:动画起始视角点;end_v:动画结束视角点 |
无 |
lv_multanim_set_zoom(lv_obj_t *multanim, lv_coord_t start_zoom, lv_coord_t zoom_end) |
设置动画的缩放起止值 |
multanim:multanim实例指针;start_zoom:起始缩放值;zoom_end:结束缩放值 |
无 |
lv_multanim_set_process(lv_obj_t *multanim, int32_t process) |
设置动画进度(核心动画控制接口) |
multanim:multanim实例指针;process:动画进度值[0,1024] |
无 |
lv_multanim_set_mask(lv_obj_t *multanim, const lv_img_dsc_t *mask_l, const lv_img_dsc_t *mask_r) |
为蒙版类动画设置左右蒙版 |
multanim:multanim实例指针;mask_l:左侧渐隐蒙版;mask_r:右侧渐隐蒙版 |
无 |
lv_multanim_get_proc(lv_obj_t *multanim) |
获取当前动画进度 |
multanim:multanim实例指针 |
返回当前动画进度值 |
lv_multanim_get_major_img(lv_obj_t *multanim) |
获取动画的主图像对象 |
multanim:multanim实例指针 |
返回主图像对象指针 |
lv_multanim_get_minor_img(lv_obj_t *multanim) |
获取动画的次要图像对象 |
multanim:multanim实例指针 |
返回次要图像对象指针 |
lv_multanim_create_mask(const void *src) |
根据指定路径读取蒙版的图像描述符 |
src:蒙版文件/资源路径 |
返回蒙版的lv_img_dsc_t类型指针 |
lv_multanim_free_mask(lv_img_dsc_t *dsc) |
释放由lv_multanim_create_mask创建的蒙版描述符 |
dsc:需要释放的蒙版描述符指针 |
无 |
lv_multanim_get_type(lv_obj_t *multanim) |
获取当前设置的动画类型 |
multanim:multanim实例指针 |
返回当前动画类型 |
3. 案例详解¶
3.1 使用流程¶
动画使用步骤基本流程:
//1.创建multanim实例
lv_obj_t *multanim = lv_multanim_create(parent);
//2.设置动画类型
lv_multanim_set_type(multanim, anim_type);
//3.设置主、次图片
lv_multanim_set_major_img(multanim, major_img);
lv_multanim_set_minor_img(multanim, minor_img);
//4.设置进度值
lv_multanim_set_process(multanim, proc);
3.2 切换动画¶
3D切换动画的实现使用了该控件,流程如下
static void lv_turn3d_anim_progress(lv_baseanim_t *baseanim, lv_obj_t *anim_obj, int32_t progress)
{
if (LV_BASEANIM_EXIT_TYPE == lv_baseanim_get_type(baseanim))
{
if (NULL == switch_multanim)
{
//1.创建multanim实例
switch_multanim = lv_multanim_create(anim_obj);
//2.设置动画类型
lv_multanim_set_type(switch_multanim, LV_MULTANIM_3D);
//3.设置主图片
lv_multanim_set_major_img(switch_multanim, anim_obj);
lv_multanim_set_zoom(switch_multanim, APP_TRANS_ANIM_ZOOM_NONE + 4, APP_TRANS_ANIM_ZOOM_NONE * 0.8f);
lv_obj_add_event_cb(switch_multanim, lv_switch_multanim_delete, LV_EVENT_DELETE, NULL);
}
lv_baseanim_para_t *para = lv_baseanim_get_para(baseanim);
if (para->flag)
lv_multanim_set_process(switch_multanim, progress);//4.设置进度值
else
lv_multanim_set_process(switch_multanim, -progress);//4.设置进度值
}
else if (switch_multanim)
{
//3.设置次图片
lv_multanim_set_minor_img(switch_multanim, anim_obj);
}
}
BUILTIN_ANIMATION(turn3Danim, LV_SWITCHANIM_TURN_3D, lv_turn3d_anim_progress);
效果展示
3.3 平铺动画¶
/**
* @brief TLV组件切换动画的核心实现函数
* @param multlist 多列表容器对象(TLV的载体)
* @param item 当前需要执行动画的列表项
* @param offset 当前项的偏移量(水平方向,正值向右,负值向左)
* @note 该函数是LVGL框架下的动画回调,负责根据TLV状态、偏移量和配置的动画类型,
* 动态管理快照对象、切换动画类型、配置动画参数并更新元素位置
*/
static void tlv_switch_anim(lv_obj_t *multlist, lv_multlist_item_t *item, int32_t offset)
{
// 1. 基础参数初始化
int32_t offset_abs = LV_ABS(offset); // 偏移量的绝对值(消除方向影响)
lv_coord_t hor_res = lv_disp_get_hor_res(NULL); // 获取屏幕水平分辨率(用于动画范围计算)
lv_coord_t ver_res = lv_disp_get_ver_res(NULL); // 获取屏幕垂直分辨率(预留参数,当前未使用)
// 2. 暂停状态(TLV_STATE_PAUSE)的处理逻辑:仅保留屏幕内的快照,超出则销毁
if (TLV_STATE_PAUSE == p_tlv->state)
{
/* 暂停状态下,当元素偏移超出屏幕中心位置时,删除快照释放资源 */
if (offset_abs > hor_res && item->snapshot)
{
lv_obj_del(item->snapshot); // 销毁快照对象
item->snapshot = NULL; // 置空指针,避免野指针
}
/* 暂停状态下,元素在屏幕范围内且无快照时,创建快照并显示 */
if (offset_abs < hor_res && NULL == item->snapshot)
{
lv_img_dsc_t *dsc = p_tlv->img_dsc; // 获取TLV默认的图片数据源
// 获取当前多列表的中心元素(用于避免快照图片重复)
lv_multlist_item_t *cent = lv_multlist_get_center_item(multlist);
// 如果中心元素存在且有快照,切换到下一张图片(避免同图重复显示)
if (cent && cent->snapshot)
dsc = (dsc == lv_img_get_src(cent->snapshot)) ? (dsc + 1) : dsc;
tlv_item_take_snapshot(item, dsc); // 为当前项创建快照(基于指定图片数据源)
}
// 如果快照存在,取消隐藏标志(确保快照可见)
if (item->snapshot)
lv_obj_clear_flag(item->snapshot, LV_OBJ_FLAG_HIDDEN);
}
// 3. 恢复状态(TLV_STATE_RESUME)的处理逻辑:销毁所有快照,显示原始元素
if (TLV_STATE_RESUME == p_tlv->state && item->snapshot)
{
lv_obj_del(item->snapshot); // 销毁快照对象
item->snapshot = NULL; // 置空指针
// 取消原始元素的隐藏标志,恢复正常显示
lv_obj_clear_flag(item->element, LV_OBJ_FLAG_HIDDEN);
}
// 4. 快照存在时,执行动画类型配置和参数更新(核心动画逻辑)
if (item->snapshot)
{
/* 步骤1:根据TLV配置的动画过渡类型,映射到LVGL多动画类型 */
uint8_t anim_type = TLV_ANIM_TYPE_FIRST; // 动画类型标记(区分不同动画逻辑分支)
uint32_t target_anim = LV_MULTANIM_ZOOM; // 默认动画类型:缩放
switch (p_tlv->anim_cfg.trans)
{
case TLV_ANIM_TRANS_ZOOM: // 缩放过渡
target_anim = LV_MULTANIM_ZOOM;
break;
case TLV_ANIM_TRANS_FADE: // 渐变过渡
target_anim = LV_MULTANIM_FADE;
anim_type = TLV_ANIM_TYPE_SECOND; // 渐变使用第二种动画逻辑
break;
case TLV_ANIM_TRANS_TURN_3D_HOR: // 3D水平旋转过渡
target_anim = LV_MULTANIM_3D;
break;
case TLV_ANIM_TRANS_TURN_Y: // Y轴旋转过渡(映射为缩放动画)
target_anim = LV_MULTANIM_SCALE;
break;
case TLV_ANIM_TRANS_DRAG: // 拖拽旋转过渡
target_anim = LV_MULTANIM_TURN;
break;
default: // 未知类型,使用默认缩放
break;
}
/* 步骤2:初始化多动画对象(multanim),仅首次创建 */
if (NULL == p_tlv->multanim)
{
// 创建多动画对象,绑定到TLV的多列表容器
p_tlv->multanim = lv_multanim_create(p_tlv->multlist);
// 添加标志位:
// - LV_OBJ_FLAG_PRESS_LOCK:按压锁定(避免重复触发)
// - LV_OBJ_FLAG_EVENT_BUBBLE:事件冒泡(向上传递事件)
// - LV_OBJ_FLAG_CLICKABLE:可点击(响应交互)
lv_obj_add_flag(p_tlv->multanim, LV_OBJ_FLAG_PRESS_LOCK | LV_OBJ_FLAG_EVENT_BUBBLE | LV_OBJ_FLAG_CLICKABLE);
}
/* 步骤3:动画类型切换时,重置动画参数(遮罩、缩放等) */
// 先设置动画类型,若返回值与目标类型不一致,说明需要切换参数
if (target_anim != lv_multanim_set_type(p_tlv->multanim, target_anim))
{
/* 子步骤3.1:遮罩资源管理(渐变/展开动画需要遮罩) */
if (LV_MULTANIM_FADE == target_anim || LV_MULTANIM_OPEN == target_anim)
{
// 懒加载左遮罩:为空时从资源池加载并创建
if (NULL == p_tlv->mask_l)
p_tlv->mask_l = lv_multanim_create_mask(APP_GET_IMG_FROM_APP(trans_anim, img_mask_left));
// 懒加载右遮罩:为空时从资源池加载并创建
if (NULL == p_tlv->mask_r)
p_tlv->mask_r = lv_multanim_create_mask(APP_GET_IMG_FROM_APP(trans_anim, img_mask_right));
// 将左右遮罩绑定到多动画对象
lv_multanim_set_mask(p_tlv->multanim, p_tlv->mask_l, p_tlv->mask_r);
}
else
{
// 非渐变/展开动画:释放无用的遮罩资源
// 获取默认遮罩资源(用于对比是否需要释放)
lv_img_dsc_t *mask_l = (lv_img_dsc_t *)APP_GET_IMG_FROM_APP(trans_anim, img_mask_left);
lv_img_dsc_t *mask_r = (lv_img_dsc_t *)APP_GET_IMG_FROM_APP(trans_anim, img_mask_right);
// 左遮罩存在且与默认资源不一致 → 释放
if (p_tlv->mask_l && mask_l != p_tlv->mask_l)
lv_multanim_free_mask(p_tlv->mask_l);
// 右遮罩存在且与默认资源不一致 → 释放
if (p_tlv->mask_r && mask_r != p_tlv->mask_r)
lv_multanim_free_mask(p_tlv->mask_r);
// 置空遮罩指针
p_tlv->mask_l = NULL;
p_tlv->mask_r = NULL;
}
/* 子步骤3.2:缩放参数配置(不同动画类型对应不同缩放值) */
if (LV_MULTANIM_ZOOM == target_anim)
{
// 普通缩放:默认缩放值 → 半倍缩放(APP_TRANS_ANIM_ZOOM_NONE为基础缩放值)
lv_multanim_set_zoom(p_tlv->multanim, APP_TRANS_ANIM_ZOOM_NONE, APP_TRANS_ANIM_ZOOM_NONE >> 1);
}
else if (LV_MULTANIM_3D == target_anim)
{
// 3D旋转:基础缩放+2 → 基础缩放*0.7(营造3D透视效果)
lv_multanim_set_zoom(p_tlv->multanim, APP_TRANS_ANIM_ZOOM_NONE + 2, APP_TRANS_ANIM_ZOOM_NONE * 0.7f);
}
else if (LV_MULTANIM_SCALE == target_anim)
{
// Y轴旋转:固定缩放值(基础缩放+2),保持大小不变
lv_multanim_set_zoom(p_tlv->multanim, APP_TRANS_ANIM_ZOOM_NONE + 2, APP_TRANS_ANIM_ZOOM_NONE + 2);
}
else
{
// 其他动画:使用默认缩放值(无缩放变化)
lv_multanim_set_zoom(p_tlv->multanim, APP_TRANS_ANIM_ZOOM_NONE, APP_TRANS_ANIM_ZOOM_NONE);
}
}
/* 步骤4:动画位置与进度控制(区分两种动画类型) */
// 非缩放动画:将快照居中显示(缩放动画由参数控制位置)
if (LV_MULTANIM_ZOOM != target_anim)
lv_obj_center(item->snapshot);
// 计算动画进度:偏移量映射到0~1024范围(LVGL动画进度常用1024为满值)
int32_t proc = (offset << 10) / hor_res;
// 动画类型1(默认:缩放/3D/旋转等)
if (anim_type == TLV_ANIM_TYPE_FIRST)
{
// 偏移量绝对值 ≤ 屏幕3/4宽度:执行核心动画
if (offset_abs <= (hor_res * 3 / 4))
{
// 偏移量绝对值 ≤ 屏幕1/4宽度:设置为主动画图片,恢复默认缩放
if (offset_abs <= (hor_res >> 2))
{
if (LV_MULTANIM_ZOOM != target_anim)
lv_img_set_zoom(item->snapshot, APP_TRANS_ANIM_ZOOM_NONE);
// 将当前快照设为动画的主图片(核心显示层)
lv_multanim_set_major_img(p_tlv->multanim, item->snapshot);
}
// 如果当前快照是次动画图片:调整进度值(修正方向)
if (item->snapshot == lv_multanim_get_minor_img(p_tlv->multanim))
proc = proc > 0 ? (proc - 1024) : (proc + 1024);
// 设置动画进度(驱动动画执行)
lv_multanim_set_process(p_tlv->multanim, proc);
}
else
{
// 偏移量超出屏幕3/4宽度:将快照移至背景层,设为次动画图片
lv_obj_move_background(item->snapshot);
lv_multanim_set_minor_img(p_tlv->multanim, item->snapshot);
}
}
// 动画类型2(渐变动画)
else
{
// 偏移量绝对值 ≤ 屏幕1/2宽度:居中显示,设为主动画图片并更新进度
if (offset_abs <= (hor_res >> 1))
{
lv_obj_center(item->snapshot);
lv_multanim_set_major_img(p_tlv->multanim, item->snapshot);
lv_multanim_set_process(p_tlv->multanim, proc);
}
else
{
// 偏移量超出屏幕1/2宽度:移至背景层,设为次动画图片
lv_obj_move_background(item->snapshot);
lv_multanim_set_minor_img(p_tlv->multanim, item->snapshot);
}
}
}
// 5. 更新原始元素位置(无论快照是否存在,保证元素跟随偏移量移动)
if (item->element)
{
// 将原始元素居中对齐,水平偏移量为当前offset(垂直无偏移)
lv_obj_align(item->element, LV_ALIGN_CENTER, offset, 0);
}
}
效果展示
4. 注意事项¶
该控件仅针对动画本身做了按进度的功能封装,实际使用时需结合具体业务场景做适配处理。以 APP 切换动画为例,可在注册的切换回调中,调用该控件接口完成初始化与进度调控。