【LVGL】滑动切换页面的界面优化实践
这个 Demo 基于 LVGL 实现滑动切换主页界面完成了背景美化、图标高亮展示、动态标题说明和按钮按压反馈等交互效果。#include lvgl/lvgl.h #define NUM_ICONS 5 #define CENTER_ZOOM 420 #define SIDE_ZOOM 150 #define FAR_ZOOM 96 #define CENTER_OPA 255 #define SIDE_OPA 102 #define FAR_OPA 48 #define ICON_WIDTH 120 #define ICON_HEIGHT 120 #define CENTER_SIZE 196 #define SIDE_SIZE 84 #define FAR_SIZE 58 #define DRAG_STEP_X 160 #define ITEM_SPACING 220 #define VISIBLE_COUNT 3 #define HOME_BG_COLOR_HEX 0x123B22 #define INFO_TEXT_COLOR_HEX 0xF6D32D #define ICON_BG_COLOR_HEX 0x2A1240 static lv_obj_t * container; static lv_obj_t * icons[NUM_ICONS]; static lv_obj_t * icon_symbols_obj[NUM_ICONS]; static lv_obj_t * icon_info_label; static lv_style_t style_icon; static lv_style_t style_icon_pressed; static lv_style_t style_icon_release_transition; static float scroll_offset 0.0f; static int pressed_x 0; static float pressed_offset 0; static bool is_dragging false; static int current_center_idx -1; static const lv_color_t icon_colors[NUM_ICONS] { LV_COLOR_MAKE(0x87, 0xF5, 0xC2), LV_COLOR_MAKE(0xFF, 0xDE, 0x73), LV_COLOR_MAKE(0xFA, 0x4F, 0x0A), LV_COLOR_MAKE(0xF7, 0x22, 0x22), LV_COLOR_MAKE(0xB7, 0x9D, 0xFF), }; static const char * icon_symbols[NUM_ICONS] { LV_SYMBOL_WIFI, LV_SYMBOL_BELL, LV_SYMBOL_BATTERY_FULL, LV_SYMBOL_AUDIO, LV_SYMBOL_BLUETOOTH, }; static const char * icon_names[NUM_ICONS] { WiFi, Bell, Battery, Audio, Bluetooth, }; static float clampf(float value, float min_value, float max_value) { if (value min_value) return min_value; if (value max_value) return max_value; return value; } static float absf(float value) { return value 0.0f ? -value : value; } static float lerpf(float start, float end, float t) { return start (end - start) * t; } static float smoothstepf(float t) { t clampf(t, 0.0f, 1.0f); return t * t * (3.0f - 2.0f * t); } static lv_color_t darken_icon_filter(const lv_color_filter_dsc_t * dsc, lv_color_t color, lv_opa_t opa) { LV_UNUSED(dsc); return lv_color_darken(color, opa); } static int get_center_icon_index(void) { int idx (int)(scroll_offset 0.5f); if (idx 0) idx 0; if (idx NUM_ICONS) idx NUM_ICONS - 1; return idx; } static void update_icon_info_label(void) { int idx; if (icon_info_label NULL) return; idx get_center_icon_index(); if (idx current_center_idx) return; current_center_idx idx; lv_label_set_text(icon_info_label, icon_names[idx]); } static void update_stack_order(const float * scores) { bool used[NUM_ICONS] { false }; for (int order 0; order NUM_ICONS; order) { int pick -1; float best_score -1000000.0f; for (int i 0; i NUM_ICONS; i) { if (used[i]) continue; if (pick 0 || scores[i] best_score) { pick i; best_score scores[i]; } } used[pick] true; lv_obj_move_foreground(icons[pick]); } } static void recalc_layout(void) { lv_disp_t * disp lv_disp_get_default(); lv_coord_t cx disp ? lv_disp_get_hor_res(disp) / 2 : 400; lv_coord_t cy disp ? lv_disp_get_ver_res(disp) / 2 : 240; float stack_scores[NUM_ICONS]; bool visible[NUM_ICONS] { false }; for (int i 0; i NUM_ICONS; i) { stack_scores[i] -1000000.0f; } for (int shown 0; shown VISIBLE_COUNT; shown) { int pick -1; float best_dist 1000000.0f; for (int i 0; i NUM_ICONS; i) { if (visible[i]) continue; float dist absf((float)i - scroll_offset); if (pick 0 || dist best_dist) { pick i; best_dist dist; } } if (pick 0) visible[pick] true; } for (int i 0; i NUM_ICONS; i) { if (!visible[i]) { lv_obj_add_flag(icons[i], LV_OBJ_FLAG_HIDDEN); continue; } float dist (float)i - scroll_offset; float abs_dist absf(dist); float near_t clampf(abs_dist, 0.0f, 1.0f); float far_t clampf(abs_dist - 1.0f, 0.0f, 1.0f); float size lerpf((float)CENTER_SIZE, (float)SIDE_SIZE, near_t); float zoom lerpf((float)CENTER_ZOOM, (float)SIDE_ZOOM, near_t); float opa lerpf((float)CENTER_OPA, (float)SIDE_OPA, near_t); if (abs_dist 1.0f) { size lerpf((float)SIDE_SIZE, (float)FAR_SIZE, far_t); zoom lerpf((float)SIDE_ZOOM, (float)FAR_ZOOM, far_t); opa lerpf((float)SIDE_OPA, (float)FAR_OPA, far_t); } lv_coord_t icon_size (lv_coord_t)size; lv_coord_t x (lv_coord_t)(cx dist * ITEM_SPACING - icon_size / 2); lv_coord_t y cy - icon_size / 2; lv_opa_t obj_opa (lv_opa_t)opa; lv_opa_t shadow_opa obj_opa 60 ? (lv_opa_t)(obj_opa - 60) : 0; lv_obj_clear_flag(icons[i], LV_OBJ_FLAG_HIDDEN); lv_obj_set_size(icons[i], icon_size, icon_size); lv_obj_set_pos(icons[i], x, y); lv_obj_set_style_bg_opa(icons[i], obj_opa, 0); lv_obj_set_style_shadow_opa(icons[i], shadow_opa, 0); lv_obj_set_style_transform_zoom(icon_symbols_obj[i], (lv_coord_t)zoom, 0); lv_obj_set_style_text_opa(icon_symbols_obj[i], obj_opa, 0); lv_obj_update_layout(icon_symbols_obj[i]); lv_obj_set_style_transform_pivot_x(icon_symbols_obj[i], lv_obj_get_width(icon_symbols_obj[i]) / 2, 0); lv_obj_set_style_transform_pivot_y(icon_symbols_obj[i], lv_obj_get_height(icon_symbols_obj[i]) / 2, 0); lv_obj_center(icon_symbols_obj[i]); stack_scores[i] size; } update_stack_order(stack_scores); if (icon_info_label ! NULL) lv_obj_move_foreground(icon_info_label); update_icon_info_label(); } static void scroll_anim_cb(void * var, int32_t value) { scroll_offset (float)value / 100.0f; recalc_layout(); } static void start_drag(lv_event_t * e) { lv_indev_t * indev lv_event_get_indev(e); lv_point_t point; if (indev NULL) indev lv_indev_get_act(); if (indev NULL) return; lv_anim_del(scroll_offset, scroll_anim_cb); lv_indev_get_point(indev, point); pressed_x point.x; pressed_offset scroll_offset; is_dragging true; } static void update_drag(lv_event_t * e) { lv_indev_t * indev lv_event_get_indev(e); lv_point_t point; if (indev NULL) indev lv_indev_get_act(); if (indev NULL) return; if (!is_dragging) { start_drag(e); } lv_indev_get_point(indev, point); int dx point.x - pressed_x; float delta dx / (float)DRAG_STEP_X; scroll_offset pressed_offset - delta; if (scroll_offset 0) scroll_offset 0; if (scroll_offset NUM_ICONS - 1) scroll_offset NUM_ICONS - 1; recalc_layout(); } static void snap_to_center(int target_idx) { if (target_idx 0) target_idx 0; if (target_idx NUM_ICONS) target_idx NUM_ICONS - 1; lv_anim_t anim; lv_anim_init(anim); lv_anim_set_var(anim, scroll_offset); lv_anim_set_values(anim, (int)(scroll_offset * 100), target_idx * 100); lv_anim_set_time(anim, 300); lv_anim_set_exec_cb(anim, scroll_anim_cb); lv_anim_set_path_cb(anim, lv_anim_path_ease_out); lv_anim_start(anim); } static void event_handler(lv_event_t * e) { lv_event_code_t code lv_event_get_code(e); if (code LV_EVENT_PRESSED) { start_drag(e); } else if (code LV_EVENT_PRESSING) { update_drag(e); } else if (code LV_EVENT_RELEASED || code LV_EVENT_PRESS_LOST) { if (!is_dragging) return; is_dragging false; snap_to_center((int)(scroll_offset 0.5f)); } } void demo_carousel(void) { static lv_style_prop_t press_transition_props[] { LV_STYLE_TRANSLATE_Y, LV_STYLE_SHADOW_WIDTH, LV_STYLE_SHADOW_OFS_Y, LV_STYLE_SHADOW_OPA, 0 }; static lv_style_transition_dsc_t transition_dsc_def; static lv_style_transition_dsc_t transition_dsc_pr; static lv_color_filter_dsc_t color_filter; lv_style_init(style_icon); lv_style_set_bg_color(style_icon, lv_color_hex(ICON_BG_COLOR_HEX)); lv_style_set_bg_opa(style_icon, 255); lv_style_set_radius(style_icon, LV_RADIUS_CIRCLE); lv_style_set_border_width(style_icon, 0); lv_style_set_shadow_width(style_icon, 4); lv_style_set_shadow_color(style_icon, lv_color_black()); lv_style_set_shadow_opa(style_icon, 30); lv_style_set_shadow_ofs_y(style_icon, 2); lv_style_set_pad_all(style_icon, 10); lv_style_transition_dsc_init(transition_dsc_def, press_transition_props, lv_anim_path_ease_out, 140, 40, NULL); lv_style_transition_dsc_init(transition_dsc_pr, press_transition_props, lv_anim_path_ease_in_out, 90, 0, NULL); lv_color_filter_dsc_init(color_filter, darken_icon_filter); lv_style_init(style_icon_release_transition); lv_style_set_transition(style_icon_release_transition, transition_dsc_def); lv_style_init(style_icon_pressed); lv_style_set_translate_y(style_icon_pressed, 4); lv_style_set_shadow_width(style_icon_pressed, 1); lv_style_set_shadow_ofs_y(style_icon_pressed, 0); lv_style_set_shadow_opa(style_icon_pressed, 12); lv_style_set_color_filter_dsc(style_icon_pressed, color_filter); lv_style_set_color_filter_opa(style_icon_pressed, LV_OPA_20); lv_style_set_transition(style_icon_pressed, transition_dsc_pr); lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(HOME_BG_COLOR_HEX), 0); lv_obj_set_style_bg_opa(lv_scr_act(), LV_OPA_COVER, 0); container lv_obj_create(lv_scr_act()); lv_obj_set_size(container, lv_obj_get_width(lv_scr_act()), lv_obj_get_height(lv_scr_act())); lv_obj_center(container); lv_obj_clear_flag(container, LV_OBJ_FLAG_SCROLLABLE); lv_obj_add_flag(container, LV_OBJ_FLAG_OVERFLOW_VISIBLE); lv_obj_set_style_bg_color(container, lv_color_hex(HOME_BG_COLOR_HEX), 0); lv_obj_set_style_bg_opa(container, LV_OPA_COVER, 0); lv_obj_set_style_border_width(container, 0, 0); lv_obj_set_style_radius(container, 0, 0); lv_obj_set_style_pad_all(container, 0, 0); lv_obj_add_event_cb(container, event_handler, LV_EVENT_ALL, NULL); icon_info_label lv_label_create(container); lv_obj_set_style_text_color(icon_info_label, lv_color_hex(INFO_TEXT_COLOR_HEX), 0); lv_obj_set_style_text_font(icon_info_label, lv_font_montserrat_34, 0); lv_label_set_text(icon_info_label, ); lv_obj_align(icon_info_label, LV_ALIGN_TOP_RIGHT, -24, 20); for (int i 0; i NUM_ICONS; i) { icons[i] lv_obj_create(container); lv_obj_set_size(icons[i], ICON_WIDTH, ICON_HEIGHT); lv_obj_add_style(icons[i], style_icon, 0); lv_obj_add_style(icons[i], style_icon_release_transition, 0); lv_obj_add_style(icons[i], style_icon_pressed, LV_STATE_PRESSED); lv_obj_clear_flag(icons[i], LV_OBJ_FLAG_SCROLLABLE); lv_obj_add_flag(icons[i], LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_EVENT_BUBBLE | LV_OBJ_FLAG_PRESS_LOCK | LV_OBJ_FLAG_OVERFLOW_VISIBLE); icon_symbols_obj[i] lv_label_create(icons[i]); lv_label_set_text(icon_symbols_obj[i], icon_symbols[i]); lv_obj_set_style_text_color(icon_symbols_obj[i], icon_colors[i], 0); lv_obj_set_style_text_font(icon_symbols_obj[i], lv_font_montserrat_48, 0); lv_obj_update_layout(icon_symbols_obj[i]); lv_obj_center(icon_symbols_obj[i]); } scroll_offset 2.0f; recalc_layout(); }

相关新闻

分布式事务解决方案:从2PC到Saga模式

分布式事务解决方案:从2PC到Saga模式

引言 在微服务架构中,业务操作往往涉及多个服务的协作。当这些操作需要保持数据一致性时,分布式事务成为必须面对的挑战。从传统的两阶段提交(2PC)到现代化的Saga模式,分布式事务解决方案在不断演进。 本文将系统梳理…

2026/7/5 1:16:27阅读更多 →
历史人物总记混?不妨试试线索推理小游戏

历史人物总记混?不妨试试线索推理小游戏

如果你家孩子刚学完秦汉历史,能把秦始皇、刘邦、项羽、汉武帝的基本事迹说出来,但一到具体细节——比如“谁是‘罢黜百家,独尊儒术’的推行者”就容易混淆。或者你作为历史爱好者,自己也会把隋炀帝和唐太宗的政策记错,…

2026/7/5 1:16:27阅读更多 →
BI 看板指标卡:一个数字旁边应该放什么上下文

BI 看板指标卡:一个数字旁边应该放什么上下文

BI 看板指标卡:一个数字旁边应该放什么上下文 一、指标卡不是数字展柜 BI 看板里最常见的组件是指标卡:GMV、订单数、转化率、活跃用户、客单价。很多看板把指标卡做得很大,却只放一个数字和一个环比箭头。问题是,读者看到数字后仍…

2026/7/5 1:11:27阅读更多 →
生成式 UI Schema:先约束状态,再生成页面

生成式 UI Schema:先约束状态,再生成页面

生成式 UI Schema:先约束状态,再生成页面 一、没有 Schema 的生成很容易散 生成式 UI 如果只给模型一段自然语言需求,它可能生成看起来不错的页面,但状态、事件、字段和权限都不稳定。页面一多,组件协议就会混乱&#…

2026/7/5 2:21:31阅读更多 →
Kafka 消费者再均衡:别让扩容变成抖动源

Kafka 消费者再均衡:别让扩容变成抖动源

Kafka 消费者再均衡:别让扩容变成抖动源 一、再均衡是常见稳定性问题 Kafka 消费组在成员加入、退出、超时或分区变化时会触发再均衡。再均衡本身是正常机制,但在高频扩缩容、消费者处理过慢、心跳配置不合理的情况下,它会变成吞吐抖动和延迟…

2026/7/5 2:21:31阅读更多 →
Stduio Pro 8最新版VR/R2R下载一键安装完整版StduioPro 8下载安装教程支持Win/Mac Stduio Pro 8.1.0 双系统安装最新版下载Stduio one 8

Stduio Pro 8最新版VR/R2R下载一键安装完整版StduioPro 8下载安装教程支持Win/Mac Stduio Pro 8.1.0 双系统安装最新版下载Stduio one 8

Win/Mac Stduio Pro 8/7/6 最新中文完整版​ Stduio one 8/7/6 下载链接:Win系统 https://www.dygdu.com/soft/one.htmlMac 系统 https://www.dygdu.com/soft/mone.html“Studio Pro 8”是一个相对宽泛的名称,可能对应不同领域的软件产品,但…

2026/7/5 2:21:31阅读更多 →
C 语言 enum 的用法

C 语言 enum 的用法

一、enum 是什么enum 是枚举类型,用来定义一组有限、固定、具名字的整型常量。本质:枚举成员都是 int 常量,只是用文字替代数字,可读性远优于纯数字。语法基础:enum 枚举名 {枚举常量1,枚举常量2,枚举常量3 };二、基础…

2026/7/5 2:21:31阅读更多 →
全友家居(邳州旗舰店)vs 大博金沙发厂多维度对比测评——品牌专卖店与源头工厂谁更值得买?

全友家居(邳州旗舰店)vs 大博金沙发厂多维度对比测评——品牌专卖店与源头工厂谁更值得买?

摘要 核心结论:在2026年邳州家具消费市场中,大博金沙发厂在全屋配套场景下的综合性价比(三室两厅配齐全屋家具总价约1.6万-2万元,较品牌专卖店低40%-60%)和交付效率(成品现货当天提货)上显著优…

2026/7/5 2:21:31阅读更多 →
终极岛屿规划指南:用Happy Island Designer轻松创建你的动物森友会梦想岛

终极岛屿规划指南:用Happy Island Designer轻松创建你的动物森友会梦想岛

终极岛屿规划指南:用Happy Island Designer轻松创建你的动物森友会梦想岛 【免费下载链接】HappyIslandDesigner "Happy Island Designer (Alpha)",是一个在线工具,它允许用户设计和定制自己的岛屿。这个工具是受游戏《动物森友会》…

2026/7/5 2:16:31阅读更多 →
从GitHub安全案例解析常见漏洞与防护实践

从GitHub安全案例解析常见漏洞与防护实践

1. 项目概述:从GitHub Trending看安全实战 最近在GitHub Trending上看到一个项目,叫 skills4/skills ,它因为一些安全漏洞案例被大家讨论。这其实是一个挺典型的场景:一个旨在展示或教授某种技能的仓库,本身却成了安…

2026/7/5 0:01:08阅读更多 →
MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

# MLT 2026启示:因果推理与概率建模驱动下一代LLM应用## 一、背景与挑战:从“黑箱预测”到“可信推理”2026年6月,第7届机器学习与趋势国际会议(MLT 2026)将在悉尼召开。会议议程中,“因果与可解释机器学习…

2026/7/5 0:01:08阅读更多 →
通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

1. 项目概述与漏洞背景最近在梳理一些历史OA系统的安全风险时,通达OA v11.6版本中的一个老漏洞又进入了我的视线。这个漏洞位于/general/bi_design/appcenter/report_bi.func.php文件中,是一个典型的SQL注入点。虽然这个漏洞的利用方式看起来并不复杂&am…

2026/7/5 0:01:08阅读更多 →
从GitHub安全案例解析常见漏洞与防护实践

从GitHub安全案例解析常见漏洞与防护实践

1. 项目概述:从GitHub Trending看安全实战 最近在GitHub Trending上看到一个项目,叫 skills4/skills ,它因为一些安全漏洞案例被大家讨论。这其实是一个挺典型的场景:一个旨在展示或教授某种技能的仓库,本身却成了安…

2026/7/5 0:01:08阅读更多 →
MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

# MLT 2026启示:因果推理与概率建模驱动下一代LLM应用## 一、背景与挑战:从“黑箱预测”到“可信推理”2026年6月,第7届机器学习与趋势国际会议(MLT 2026)将在悉尼召开。会议议程中,“因果与可解释机器学习…

2026/7/5 0:01:08阅读更多 →
通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

1. 项目概述与漏洞背景最近在梳理一些历史OA系统的安全风险时,通达OA v11.6版本中的一个老漏洞又进入了我的视线。这个漏洞位于/general/bi_design/appcenter/report_bi.func.php文件中,是一个典型的SQL注入点。虽然这个漏洞的利用方式看起来并不复杂&am…

2026/7/5 0:01:08阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/5 1:30:27阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/4 2:33:55阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/4 2:33:55阅读更多 →