ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

LVGL环形无限滚动实现方案:菜单、轮播与进度动画实战

LVGL环形无限滚动实现方案:菜单、轮播与进度动画实战 LVGL 里做“环形无限循环滚动”通常不是某一个控件直接开箱就能做出完整效果的事。它更接近一种“组合能力”动画驱动、位置重置、索引取余三者配合起来才能让列表、菜单、轮播图或滚轮在用户无感知的情况下循环往复地滚动。这篇文章会从 LVGL 的常用控件和动画 API 入手给出几种可落地的环形无限滚动实现方案包括滚轮选择器、旋转木马菜单、横向无缝轮播和环形进度动画。每个方案都配代码和验证思路方便直接在模拟器或开发板上跑通。这个需求在嵌入式 UI 里很常见智能家居控制面板上的环形菜单、仪器仪表里的环形进度圈、广告屏上的轮播 Banner、计时器里的滚轮选择。LVGL 本身是面向资源受限 MCU 的图形库所以这些效果不需要 GPU也不需要额外的图形引擎普通单片机加上一块屏幕就能跑。本文会覆盖三块内容第一环境怎么准备模拟器怎么搭第二几种“环形 无限循环 滚动”效果的实现思路和代码第三资源占用怎么观察、常见问题怎么排查。适合正在用 LVGL 8.x/9.x 做项目的开发者也适合刚接触 LVGL想在模拟器上快速验证交互效果的初学者。1. 核心能力速览能力项说明项目类型嵌入式 GUI 图形库 自定义滚动交互方案核心功能环形菜单、无限滚轮、无缝轮播、环形进度动画运行平台STM32、ESP32 等 MCU以及 PC 模拟器VS Code SDL2 / Visual Studio显存需求不依赖 GPU显存概念可忽略重点是 RAM 和 Flash 占用启动方式代码 CMake / IDE 构建官方提供模拟器工程模板主要 APIlv_anim、lv_roller、lv_arc、lv_obj_set_style_transform_*是否支持批量任务支持可批量更新列表项、批量启动动画、批量刷新 UI 数据是否支持接口提供 C API 和事件回调机制可对接外部数据源适合场景仪表盘、菜单导航、日期选择、轮播广告、动效演示这里要特别说明一点LVGL 的“资源占用”和 PC 图形库完全不同。它不关心你的显卡有多少显存更关心你的 MCU 有多少 RAM 和 Flash。LVGL 官方定位就是给资源受限设备用的典型项目经过裁剪后几十 KB 到几百 KB RAM 都能跑具体数值取决于控件数量和显示缓冲区配置所以后文会专门写怎么看内存占用。2. 适用场景与使用边界2.1 适合谁用嵌入式产品开发者需要在屏幕上实现简单但流畅的滚动交互。LVGL 学习新手想搞懂动画 API 和控件样式变换模拟器是最低成本的练习环境。UI / 交互设计验证人员在 PC 模拟器上快速验证环形菜单、轮播图的交互手感再交给开发移植到板子。开源硬件爱好者ESP32、STM32 开发板配合 TFT 屏就能实现一套完整的动效界面。2.2 能解决什么问题原生控件布局太单调需要“环形排列 旋转选中”的交互。lv_list只支持直线滚动想要无限循环的滚轮或轮播效果。需要在一个 UI 界面里展示周期变化的进度比如 0 到 360 度循环的仪表动画。需要新闻、广告、横幅文字自动跑马灯并且不能有明显的跳变。2.3 不适合什么场景需要复杂 3D 透视、粒子效果、大量着色器动效的场景LVGL 不适合。对帧率要求极高且动画元素极多的场景建议先评估 MCU 性能再决定。需要多语言混合排版、复杂富文本展示的场景LVGL 的文本能力偏基础。2.4 版权与合规提醒LVGL 本身是开源库使用前要看清楚项目对应的开源协议版本。自己做 UI 演示时建议使用自绘图标、系统字体或明确允许商用的字体和图片素材。如果产品要量产字体文件、图标素材、皮肤贴图的授权必须逐一确认。涉及用户数据的界面展示也要注意隐私设计比如输入框内容打码、日志不落盘明文。3. 环境准备与前置条件3.1 模拟器方案写 LVGL 效果最舒服的方式是先跑模拟器不需要反复烧录板子。官方和社区提供几个常用模拟器工程lvgl/lv_port_pc_vscodeVS Code SDL2 的 PC 模拟器跨平台适合代码调试。lvgl/lv_sim_visual_studioVisual Studio 工程Windows 下调试方便。lvgl/lv_sim_eclipse_sdlEclipse SDL2老牌模拟器方案。SquareLine Studio 或 EEZ Studio图形化设计工具能导出 LVGL 代码适合从界面布局开始做。如果选 VS Code SDL2 方案准备步骤大致如下# 克隆模拟器工程注意 lvgl 是 submodule需要递归拉取 git clone --recursive https://github.com/lvgl/lv_port_pc_vscode.git cd lv_port_pc_vscode code .然后在 VS Code 里安装 CMake 插件和对应编译工具链按F5启动调试。第一次编译时间会長一些因为要把 LVGL 源码和 SDL2 依赖一起编出来。3.2 开发板方案如果直接用 STM32 或 ESP32最基础的前置条件是一块能驱动屏幕的开发板屏幕分辨率例如 240x320、320x480 或更大。屏幕驱动 IC 的初始化代码通常是 ST7789、ILI9341、GC9A01 这类。LVGL 源码路径建议独立放在lvgl/目录不要和业务代码混在一起。触摸芯片驱动如果项目需要触摸交互比如 GT911、XPT2046。LVGL 移植到裸机或 RTOS 时至少要做两件事在lv_conf.h里打开LV_COLOR_DEPTH、LV_MEM_SIZE等配置。实现显示刷新回调disp_flush_cb以及可选的时间基准lv_tick_inc。/* lv_conf.h 中常见的配置项 */ #define LV_COLOR_DEPTH 16 #define LV_MEM_SIZE (32U * 1024U) /* 根据实际 RAM 调整 */ #define LV_DISP_DEF_REFR_PERIOD 30 /* 刷新周期单位 ms */这里不写死某种芯片的完整移植过程因为不同开发板和屏幕驱动差异很大。更稳妥的做法是先跑官方模拟器确认自己的效果逻辑再把lvgl源码和显示驱动接口替换成板子上的实现。3.3 版本差异注意LVGL 8.x 和 9.x 的 API 有差异。8.x 里很多函数直接以lv_组件_操作命名9.x 则继续保留大部分函数但内部架构做了调整部分样式的接口参数也变了。下面代码示例会以 LVGL 8.x 写法为主并标注哪些地方在 9.x 中需要注意。实际开发时以你工程里lvgl子模块的版本为准。4. 实现思路与核心代码环形无限循环滚动拆开看有三个技术点环形坐标计算中使用三角函数让控件围绕圆心分布。无限循环索引或角度通过取余折返或者动画到达终点后重置位置。滚动通过lv_anim驱动位置、角度、透明度等属性连续变化。下面给出四种可以跑通的具体方案按部件拆开说。4.1 方案一用lv_roller做无限循环滚轮lv_roller是 LVGL 自带的滚轮控件适合做日期选择、数字选择、菜单选项选择。设置成无限循环模式后滚动到末尾会自动回到开头直接实现“环形”索引。/* LVGL 8.x 示例 */ lv_obj_t * roller lv_roller_create(lv_scr_act()); lv_roller_set_options(roller, Item 0\n Item 1\n Item 2\n Item 3\n Item 4\n Item 5\n, LV_ROLLER_MODE_INFINITE); /* 无限循环模式 */ lv_roller_set_visible_row_count(roller, 3); lv_obj_set_width(roller, 160); lv_obj_center(roller);滚动效果是垂直的但选项在逻辑上首尾相连等价于“环形列表”。如果需要水平滚轮可以用lv_obj_set_style_transform_rotate把整个滚轮旋转 90 度或者自己用lv_obj和动画实现。LV_ROLLER_MODE_INFINITE在 LVGL 9.x 中依然存在只是部分样式接口改名比如选中项样式的设置方式略有变化。判断效果的标志是往下滚到最后一个选项后继续拖动是否会回到第一个选项并能持续滚动。如果滚动到头后卡住说明模式没有生效需要检查lv_roller_set_options的第三个参数是否为LV_ROLLER_MODE_INFINITE。4.2 方案二自定义旋转木马环形菜单这种效果在智能家居、车载 HMI 里很常见几个菜单图标围成一个圆环用户点击或滑动时整个环旋转当前高亮项停在顶部或中心。核心思路是把 N 个 item 按等分角度分布在圆环上。用一个全局角度变量current_angle控制整体位置。动画执行时修改角度然后重新计算每个 item 的坐标、缩放和透明度。角度不断累加取% 360保证坐标折返从而实现无限循环。#define ITEM_NUM 6 #define RADIUS 80 #define CENTER_X 160 #define CENTER_Y 120 static lv_obj_t * menu_items[ITEM_NUM]; static int current_angle 0; /* 围绕中心点布局子对象 */ static void menu_layout(void) { for (int i 0; i ITEM_NUM; i) { /* 每个 item 在圆环上的角度 全局角度 自身偏移 */ double rad (current_angle i * 360.0 / ITEM_NUM) * 3.14159265 / 180.0; lv_coord_t x CENTER_X RADIUS * cos(rad) - lv_obj_get_width(menu_items[i]) / 2; lv_coord_t y CENTER_Y RADIUS * sin(rad) - lv_obj_get_height(menu_items[i]) / 2; lv_obj_set_pos(menu_items[i], x, y); /* 用 cos 值模拟前后层次靠近正右侧的放大、变亮远离的缩小、变暗 */ int scale 180 60 * cos(rad); int opa 120 120 * cos(rad); if (scale 240) scale 240; if (scale 120) scale 120; if (opa 60) opa 60; lv_obj_set_style_transform_scale_x(menu_items[i], scale, 0); lv_obj_set_style_transform_scale_y(menu_items[i], scale, 0); /* 注意缩放变换中心默认是左上角要设置到控件中心 */ lv_obj_set_style_transform_pivot_x(menu_items[i], lv_obj_get_width(menu_items[i]) / 2, 0); lv_obj_set_style_transform_pivot_y(menu_items[i], lv_obj_get_height(menu_items[i]) / 2, 0); lv_obj_set_style_opa(menu_items[i], opa, 0); } }旋转动画用lv_anim驱动动画的变量直接使用current_angle的内存地址static void menu_anim_cb(void * var, int32_t v) { current_angle v % 360; menu_layout(); } static void menu_rotate(int target) { lv_anim_t a; lv_anim_init(a); lv_anim_set_var(a, current_angle); lv_anim_set_exec_cb(a, menu_anim_cb); lv_anim_set_values(a, current_angle, current_angle target); lv_anim_set_time(a, 400); lv_anim_set_path_cb(a, lv_anim_path_ease_out); lv_anim_start(a); }这样每次调用menu_rotate(60)环形菜单就转一格。target可以是正数或负数分别代表顺时针和逆时针。因为current_angle永远累加且布局时取余 360所以用户操作再多次也不会看到列表到头。LVGL 9.x 中lv_obj_set_style_transform_scale_x仍然可用但部分样式选择器参数从LV_STATE_DEFAULT调整为LV_PART_MAIN实际使用时按编译器提示调整即可。如果想加入触摸滑动可以在触摸事件回调里根据滑动距离计算目标角度。这里给一个简化思路static void menu_event_cb(lv_event_t * e) { lv_event_code_t code lv_event_get_code(e); if (code LV_EVENT_CLICKED) { /* 点击某个 item 时让它旋转到顶部 */ lv_obj_t * obj lv_event_get_target(e); for (int i 0; i ITEM_NUM; i) { if (obj menu_items[i]) { int step (i * 360 / ITEM_NUM); menu_rotate(-(step)); break; } } } }在实际项目里旋转角度最好按“当前选中索引”和“目标索引”的差值计算避免连续点击时角度跳变。4.3 方案三横向无缝轮播 / 跑马灯轮播 Banner 或跑马灯文字也是“无限循环滚动”的典型场景。实现思路比旋转木马简单让内容在水平方向移动当移动距离超过内容宽度时立刻重置到起始位置。这里为了不产生跳变需要让容器内容至少复制两份保证重置瞬间画面看起来连续。static lv_obj_t * banner; static lv_obj_t * label_1; static lv_obj_t * label_2; static int banner_width 320; static void marquee_anim_cb(void * var, int32_t v) { lv_obj_set_x(banner, v); if (v -banner_width) { lv_obj_set_x(banner, 0); } } void marquee_start(void) { lv_anim_t a; lv_anim_init(a); lv_anim_set_var(a, banner); lv_anim_set_exec_cb(a, marquee_anim_cb); lv_anim_set_values(a, 0, -banner_width); lv_anim_set_time(a, 3000); lv_anim_set_repeat_count(a, LV_ANIM_REPEAT_INFINITE); lv_anim_start(a); }如果做轮播图而不是跑马灯更方便的办法是只移动一个页面容器切到下一页时把页面位置瞬移回原点。例如三页轮播static void banner_anim_cb(void * var, int32_t v) { /* v 的范围是 0 ~ -page_count * page_width */ lv_obj_set_x(var, v); } static void banner_next(void) { static int page 0; page; lv_anim_t a; lv_anim_init(a); lv_anim_set_var(a, banner); lv_anim_set_exec_cb(a, banner_anim_cb); lv_anim_set_values(a, lv_obj_get_x(banner), -page * PAGE_WIDTH); lv_anim_set_time(a, 300); lv_anim_set_path_cb(a, lv_anim_path_ease_out); lv_anim_set_ready_cb(a, banner_ready_cb); /* 动画完成后做循环处理 */ lv_anim_start(a); } static void banner_ready_cb(lv_anim_t * a) { if (lv_obj_get_x(banner) -TOTAL_WIDTH PAGE_WIDTH) { lv_obj_set_x(banner, 0); } }“无限”的关键在于动画本身不受物理边界限制逻辑上通过“到达末尾后重置坐标”制造无缝观感。要保证视觉连续内容宽度必须刚好等于或大于视口宽度。4.4 方案四环形进度动画如果“环形滚动”指的是仪表盘上的环形进度圈用lv_arc加动画即可。这种效果在加载界面、倒计时、温湿度仪表里很常见。lv_obj_t * arc lv_arc_create(lv_scr_act()); lv_arc_set_range(arc, 0, 360); /* 设置 0~360 度范围 */ lv_arc_set_value(arc, 0); lv_obj_set_size(arc, 180, 180); lv_obj_center(arc); static void arc_anim_cb(void * var, int32_t v) { /* 到达 360 后回到 0形成环形循环 */ lv_arc_set_value(var, v % 360); } void arc_anim_start(void) { lv_anim_t a; lv_anim_init(a); lv_anim_set_var(a, arc); lv_anim_set_exec_cb(a, arc_anim_cb); lv_anim_set_values(a, 0, 360); lv_anim_set_time(a, 2000); lv_anim_set_repeat_count(a, LV_ANIM_REPEAT_INFINITE); lv_anim_start(a); }LVGL 9.x 中lv_arc依然存在lv_anim_set_repeat_count依然可用。这里的无限循环是“值域循环”适合表达周期性时间、角度、百分比等数据。5. 功能测试与效果验证不管用模拟器还是开发板功能验证都可以按下面顺序操作。5.1 测试滚轮选择器输入在模拟器里打开工程运行方案一代码。操作鼠标拖动或滚动滚轮观察最后一个选项和第一个选项是否能连续切换。预期滚轮可以一直向下滚动不会停在Item 5后卡住。判断标准滑动过程无断层、无白屏、选中项高亮正常。失败排查先确认LV_ROLLER_MODE_INFINITE是否设置成功再看lv_conf.h中动画刷新时间是否过慢。5.2 测试旋转木马菜单输入创建 6 个按钮或图片控件背景色不同。操作连续点击某个 item或调用menu_rotate传不同角度。预期每次旋转 60 度整体圆环平滑转动缩放和透明度随角度变化看起来有前后层次。判断标准转 20 次以上仍能正常布局不出现坐标漂移或突然跳回。失败排查最常见问题是transform_pivot_x/y没有设置到中心导致缩放时错位其次是cos计算使用整型导致精度不够建议保留double参与运算。5.3 测试横向无缝轮播输入准备 3 张宽度为 320 的占位色块连续动画播放。操作观察最后一页切换到第一页的瞬间。预期切换到第一页时画面没有闪烁或跳切。判断标准循环 10 次以上位置始终准确。失败排查检查重置坐标的阈值是否等于总内容宽度减去单页宽度如果阈值小于这个值画面上会看到一部分空白。5.4 测试环形进度动画输入单文件工程运行方案四代码。操作观察圆弧是否从 0 度平滑转完一圈。预期动画重复执行无残影。判断标准值域在 0~360 内循环lv_arc_set_value没有溢出异常。失败排查如果圆弧画不满一圈检查lv_arc_set_range和lv_arc_set_bg_angles的设置。6. 事件接口与批量更新LVGL 没有 HTTP API但它的 C API 和事件回调机制可以理解成“本地接口”。外部串口、传感器、MQTT 数据都可以通过事件或定时任务把数据塞进 UI。6.1 事件回调接口以旋转木马菜单为例用户点击 item 时通过事件回调获取目标索引static void ui_event_handler(lv_event_t * e) { lv_event_code_t code lv_event_get_code(e); lv_obj_t * obj lv_event_get_target(e); if (code LV_EVENT_CLICKED) { /* 根据 obj 匹配对应的菜单项执行旋转 */ for (int i 0; i ITEM_NUM; i) { if (menu_items[i] obj) { menu_rotate(i * 60); break; } } } else if (code LV_EVENT_RELEASED) { /* 松开时可以做回弹或复位操作 */ } }6.2 批量刷新列表数据比如从传感器读回 10 条数据需要批量更新到滚轮中。这时不要每读一条就重建一次滚轮应当缓存字符串后一次性设置。static char option_buffer[512]; void update_roller_data(lv_obj_t * roller, char ** options, int count) { memset(option_buffer, 0, sizeof(option_buffer)); for (int i 0; i count; i) { strcat(option_buffer, options[i]); if (i ! count - 1) { strcat(option_buffer, \n); } } lv_roller_set_options(roller, option_buffer, LV_ROLLER_MODE_INFINITE); }6.3 批量启动动画如果多个子对象需要同时执行入场动画可以循环启动动画注意每个动画的var必须指向不同对象。for (int i 0; i ITEM_NUM; i) { lv_anim_t a; lv_anim_init(a); lv_anim_set_var(a, menu_items[i]); lv_anim_set_exec_cb(a, item_scale_anim_cb); lv_anim_set_values(a, 0, 100); lv_anim_set_time(a, 300); lv_anim_set_delay(a, i * 50); /* 错峰启动 */ lv_anim_start(a); }既然是嵌入式环境批量任务要注意内存分配频率。动画对象lv_anim_t建议定义成局部变量后lv_anim_init或者用lv_anim_start返回的指针统一管理避免频繁创建销毁导致内存碎片。7. 资源占用与性能观察LVGL 的性能问题主要集中在两点内存占用和刷新带宽。7.1 内存占用LVGL 控件在被创建时会从lv_mem分配内存。lv_conf.h里的LV_MEM_SIZE决定总内存池大小。环形菜单如果创建几十个子对象就要给内存池留足空间。查看内存占用可以用内置函数lv_mem_monitor_t mon; lv_mem_monitor(mon); printf(used: %d, free: %d, frag: %d%%\n, mon.used, mon.free, mon.frag_pct);如果frag_pct一直升高说明频繁创建删除控件需要优化代码结构。7.2 动画性能动画越复杂每帧需要执行的坐标计算越多。旋转木马方案中每帧要循环计算所有 item 的cos和sin6 个 item 开销不大但如果环形菜单有 20 个以上 item可以考虑预计算角度表避免每帧调用数学函数。/* 预计算 0~359 度的 sin/cos 表角度转弧度 */ static int16_t sin_table[360]; static int16_t cos_table[360]; void init_trig_table(void) { for (int i 0; i 360; i) { double rad i * 3.14159265 / 180.0; sin_table[i] (int16_t)(sin(rad) * 1000); cos_table[i] (int16_t)(cos(rad) * 1000); } } /* 使用整数查表降低 MCU 浮点压力 */ double rad (current_angle i * 360 / ITEM_NUM) % 360; int x CENTER_X RADIUS * cos_table[rad] / 1000; int y CENTER_Y RADIUS * sin_table[rad] / 1000;7.3 刷新带宽屏幕分辨率越大刷新一帧需要的传输时间越长。如果动画出现卡顿优先检查显示缓冲区buf_size是否过小是否建议使用双缓冲。色深是 16bit 还是 24bit16bit 一般足够。是否有其他高开销任务和 LVGL 抢 CPU。LVGL 模拟器上出现的性能问题参考价值有限因为 PC 的 GPU 刷新速度远超 MCU。只有在开发板上验证到的帧率才代表真实效果。7.4 降低内存占用的通用建议减少动画同时运行数量动画结束记得lv_anim_del。图片素材尽量转成内置 C 数组或使用较小色深。不要频繁创建销毁相同的控件复用已创建的对象。大段静态文本考虑用 NorFlash 存储不要全部放进 RAM。LVGL 9.x 中可以选择关闭不使用的组件减小编译体积和内存占用。8. 常见问题与排查方法问题现象可能原因排查方式解决方案滚轮滚到底后不能继续未启用无限模式检查lv_roller_set_options第三个参数改成LV_ROLLER_MODE_INFINITE环形菜单旋转时图标位置错乱布局公式中角度未取余或current_angle过大打印current_angle和坐标布局前统一取% 360缩放时图标位置偏移变换中心未设置检查transform_pivot_x/y设置为控件的宽度和高度的一半switch 按下不变化LVGL 9.x 的 switch 状态需要事件回调手动维护检查按钮是否有LV_EVENT_VALUE_CHANGED在事件回调里切换LV_STATE_CHECKED或升级到更新版本轮播图跳变明显重置坐标的阈值不对打印lv_obj_get_x观察把阈值设为“总内容宽度 - 单页宽度”界面卡顿、掉帧动画任务过多或刷新缓冲区过小用lv_mem_monitor查看内存用帧率工具观察减少动画、增大缓冲区、降低分辨率模拟器启动后窗口黑屏SDL2 初始化失败或显示驱动回调没实现查看编译输出和运行日志重新安装 SDL2 依赖确认模拟器工程完整触摸滑动没有反应输入设备驱动未实现或未注册检查lv_indev_drv_register在模拟器中使用鼠标设备在开发板注册触摸驱动中文字体显示为方框字库未加载或字库文件不匹配确认字体回退设置生成并嵌入中文字体或使用 LVGL 自带的字体工具其中 switch 按下去没有状态变化是 LVGL 9.x 里一个容易踩的坑。旧版本里某些控件会自动处理点击状态9.x 中lv_switch通常需要你在事件回调里手动切换LV_STATE_CHECKED否则按下去视觉上不变化。代码参考static void switch_event_cb(lv_event_t * e) { lv_obj_t * sw lv_event_get_target(e); if (lv_obj_has_state(sw, LV_STATE_CHECKED)) { lv_obj_clear_state(sw, LV_STATE_CHECKED); } else { lv_obj_add_state(sw, LV_STATE_CHECKED); } }9. 最佳实践与使用建议9.1 先模拟器验证再上板调优环形菜单、轮播这种动效逻辑调试非常依赖可视化界面。先在lv_port_pc_vscode模拟器里跑通所有交互再移植到开发板。模拟器上验证不了的是性能和显存带宽但验证得了坐标计算、动画路径、事件状态。9.2 维护一份最小可运行工程不要每次新建工程都从零配置 LVGL。建议保存一个最小模拟器工程骨架包含固定版本的lvgl子模块。已经配置好的lv_conf.h。一个只创建单页面的ui_init入口。一个打印lv_mem_monitor的调试命令。后续新实验都在这个工程上改能大幅减少环境问题。9.3 坐标系和变换中心统一规划LVGL 的坐标系统默认以父对象左上角为原点。旋转木马方案依赖全局圆心坐标如果你的屏幕分辨率变了CENTER_X、CENTER_Y、RADIUS都要跟着调整。建议在一个头文件里集中定义这些布局参数而不是散落在代码里。9.4 动画回调里尽量少做耗时操作lv_anim的执行回调会在每一帧刷新周期内被调用。如果在回调里做文件读取、网络请求、大数组拷贝会直接拖垮动画帧率。耗时操作应该放在定时器或后台任务里完成后只更新结果值。9.5 数据刷新要节流批量更新列表数据时高频触发lv_roller_set_options会频繁重新创建选项造成内存碎片。比较稳妥的做法是数据到达后先缓存到缓冲区等数据稳定或定时刷新一次。9.6 UI 素材要合规菜单图标、背景图、字体文件、音效素材都要确认授权范围。开源字体、自绘素材、明确允许 MCU UI 使用的图标库都可以放心用。商业项目里的字体授权尤其要留意很多中文字体是不能直接打包进固件的。10. 总结与下一步LVGL 的环形无限循环滚动核心不是某一个控件而是动画系统、坐标计算和位置重置的组合。lv_roller可以直接给你滚轮式的循环效果旋转木马类菜单需要自己维护角度和布局轮播图则依赖动画结束后的坐标重置而lv_arc适合环形进度场景。先把这四种方案在模拟器上跑通再根据自己项目的交互需求选择或组合。最容易踩的坑有三个第一无限滚动忘记取余角度或索引越滚越大最终坐标异常第二变换缩放时忘记设置 pivot图标围绕左上角旋转而不是围绕中心第三LVGL 8.x 和 9.x 的 API 差异没核对导致编译通过但行为不对。下一步建议从“旋转木马菜单 点击旋转到顶部”这个组合开始实践它最能覆盖lv_anim、样式变换、事件回调、内存观察这几个常用技能点。跑通之后再做无缝轮播和批量数据刷新。每个效果都做好一个可复用的独立模块后面做多页面应用时可以直接拿去用。如果这篇文章里的示例代码和你的 LVGL 版本 API 不一致优先查你工程里lvgl子模块的文档和头文件。毕竟 8.x 和 9.x 的差异不小按版本对照调整才能少走弯路。
返回列表