27 KiB
UI 界面制作流程(图片 → HTML → LVGL)
用途:规范"参考图 → HTML 样机 → LVGL 代码 → 双端验证"的完整管线,规定每一步的动作、产物、放行条件(gate),保证过程可重复、可交接。 适用:所有 UI 新界面 / 界面大改;小改动走 §11 快速通道。 配套文档:
UI_设计架构.md(系统如何工作)、LVGL模拟器部署与使用说明.md(模拟器)、UI_中文字体生成与配置.md(字体生成)。 首个实例:界面 01 主界面(根页),样机mockups/01_主界面.html(本文所有示例数字均取自该试点)。
一、流程总览
参考图 ──▶ [P0 需求录入] ──▶ 界面台账 + 问题清单
│ gate: 界面定位/极性/阻塞问题已确认
▼
[P1 图片→HTML样机] ──▶ mockups/NN_界面名.html
│ ▲ │
│ └── 用户浏览器确认(循环至定稿)
│ gate: 样机定稿 + 字符集核对完成
▼
[P2 HTML→LVGL代码] ──▶ Drivers/BSP/UI/ 代码 + 字体重生成
│ gate: 代码与样机参数逐项一致 + 字体已重生成 + 内存预算通过
▼
[P3 模拟器验证] ──▶ 出图 → 与样机渲染图比对 → 不合依 HTML 改码
│ gate: 模拟器出图与样机渲染图同构(忽略抗锯齿/字形微差)
▼
[P4 MCU 验证] ──▶ @build + @test + 真机目检
│ gate: 0错0警 + test PASS + 目检通过
▼
[P5 归档] ──▶ 变更记录/台账更新
1.1 各阶段职责分工
| 阶段 | 用户做 | AI 做 | 产物 |
|---|---|---|---|
| P0 需求录入 | 提供图片(每批 1~3 屏);说明各屏作用与菜单位置;回答问题 | 图片分析、界面登记、问题清单 | 台账 + Q 清单 |
| P1 图片→HTML | 浏览器打开确认;指出问题或直接改 HTML | 度量核对、字符集核对、布局计算、样机制作与修改 | 定稿样机 |
| P2 HTML→代码 | (可选)抽查代码 | 逐参数翻译、字体重生成、内存预算 | 代码 + 字体 |
| P3 模拟器 | (可旁观) | 构建、离线出图、与样机渲染图比对、依 HTML 改码 | 比对结论 |
| P4 MCU | 真机目检(DTU-RemoteLCD) | @build / @test 执行、日志解析 | 日志 + 结论 |
| P5 归档 | — | 更新变更记录与台账 | 文档 |
二、设计约束(硬规则)
管线中一切设计必须满足以下约束;违反即需重做,不存在"真机上再凑合"的选项。
| # | 约束 | 值 | 来源 |
|---|---|---|---|
| C1 | 屏幕 | 160×160,1bpp 单色(LV_COLOR_FORMAT_I1),无物理屏(远端客户端显示) |
ui_port.h |
| C2 | 字体 | 唯一:ui_font_cn_12(宋体 12px 1bpp)。汉字 12px/字,ASCII 6px/字,行高 13px。需要其他字号 = 大工程(新字体 + 双端 lv_conf.h),默认禁止 |
ui_font_cn_12.c |
| C3 | 字符集 | 子集字体(当前 78 汉字 + ASCII 0x20-0x7E);缺字显示空白;新字必须重生成字体 | tools/font_gen/ |
| C4 | LVGL 内存池 | 40KB(LV_MEM_SIZE);任一时刻只放"当前导航路径 + 当前内容页" |
Lib/lv_conf.h |
| C5 | 文本 | 一律静态字符串(lv_label_set_text_static),禁止动态字符串/strdup |
架构文档 |
| C6 | 编码 | Drivers/BSP/UI/*、ui_task.c、Lib/lv_conf.h 为 UTF-8 带 BOM;改动只用 Edit 工具,禁止 PowerShell/cmd 整文件重写(U+FFFD 不可逆) |
AGENTS.md 铁律 |
| C7 | 代码共用 | ui_menu.c / 字体为 MCU 与模拟器共用源,禁止在 simulator/ 复制副本 |
架构文档 |
| C8 | 视觉禁区(1bpp) | 圆角 / 阴影 / 透明度 / 渐变 / 多色 / 抗锯齿 全部禁用;样机与代码都不得引入 | 物理限制 |
| C9 | 双端注册 | 新增 Drivers/BSP/UI/*.c 必须两处手动注册:Keil .uvprojx + simulator/CMakeLists.txt 的 UI_SOURCES 列表(均为显式列表,无通配扫描)。只注册一端 = 另一端静默缺文件 |
实测(2026-09-03) |
2.1 字体度量速查(2026-09-03 实测,字体变更时重测)
| 项 | 原始值 | = 设备像素 | 说明 |
|---|---|---|---|
| 汉字步进宽 | adv_w = 192(1/16 px) |
12 px/字 | 4 字标签 = 48px |
| ASCII 步进宽 | adv_w = 96 |
6 px/字 | "OK" = 12px,"V1.0" = 24px |
| 汉字墨迹盒 | box_w 9box_h 11 |
墨迹在盒内再内缩 1~2px | 字体自带侧边距,贴边文字仍有视觉留白 |
| 行高 | line_height = 13 |
13px | 布局按 13 算,不是 12 |
| 基线 | base_line = 2(自底部) |
— | 垂直定位参考 |
样机字体必须用 SimSun(设备字体的源字体),浏览器渲染宽度与设备度量一致,预览才"诚实"。
三、P0:需求录入与界面台账
3.1 用户输入
- 参考图(建议每批 1~3 屏,不必一次给全)
- 每图一句话:是什么界面、干什么用、挂在菜单哪个位置
- 对 AI 提出的问题的回答(能答则答,答不了的标"待定")
3.2 AI 动作:图片分析(检查单)
| 分析项 | 内容 |
|---|---|
| 总体结构 | 页头 / 主体(网格/列表/数值/曲线/表单)/ 页脚 / 状态栏 |
| 元素类型 | 网格、列表、数值、曲线、开关、进度条、图标(1bpp 小屏图标基本不可行 → 文本替代) |
| 文本内容 | 逐字抄录(字符集核对的输入;漏一个字 = 真机一处空白) |
| 比例换算 | 参考图比例 → 160×160 设备像素(参考屏分辨率通常大于 160,做比例适配而非像素照搬) |
| 风格 | 极性(白底黑字/黑底白字)、边框样式、选中样式 |
| 信息缺失 | 截图裁切、遮挡、模糊处 → 转成待确认问题 |
3.3 界面台账
- 编号
NN(01 = 试点界面),名称、作用、菜单位置、状态(待做/进行中/已完成) - 台账随 P5 归档更新
3.4 Gate(进入 P1)
- 界面作用与菜单位置已确认
- 阻塞性问题已回答;非阻塞项已标注默认值(如 "TBD")
试点实例:01 主界面。用户给一张根页截图:顶部梯形装饰 + 状态行"当前模式:无模式" + 7 行列表(装置信息/实时数据/参数定值/三遥设置/装置维护/通讯参数/记录查询),黑底白字(与现系统同极性)。AI 分析出:7 项与现有
g_menu_tab的映射,其中 3 项疑似重命名(参数定值/三遥设置/通讯参数)→ Q1;"三遥设置"对应现有"遥测量"还是新增"三遥"节点 → Q2;旧菜单"厂家设置/版本信息"未列入 7 项,入口在哪 → Q3;"当前模式"指什么、数据从哪来 → Q4。
四、P1:图片 → HTML 样机
4.1 字体度量核对(字体每次变更时重做)
- 读
Drivers/BSP/UI/ui_font_cn_12.c头部 Opts 注释 → 字体源(宋体)、尺寸(12)、bpp(1)、当前字符集 - 读
glyph_dsc[]表 →adv_w ÷ 16= px(核对 CJK=12 / ASCII=6) - 读
font_dsc→line_height=13、base_line=2 - 与 §2.1 速查不一致 → 全部样机参数重算
4.2 字符集核对
- 收集本屏全部文本中的去重汉字
- 对照现有字符集(字体
.c头部--symbols行为准;ui_cn_chars.txt为生成物,重生成后一致) - 输出:命中列表(现有)/ 新增列表(标红)→ 记入配套
_说明.md - 新增列表决定 P2 字体重生成的范围
试点实例:01 主界面 7 个菜单项共 22 个唯一汉字 → 命中 21(装置信息实时数据参数定值遥设维护通讯记录查询),新增 1(三);状态行"当前模式:无模式"新增 5(当前模式无);箭头 ▶(U+25B6)为新增符号。合计确定新增 7 个字形(若状态行用全角冒号":"则再多 1,共 8)。
4.3 布局计算
- 所有值为设备像素整数
- 列宽:
cell_w = floor((W − 2×边距 − 间隙×(n−1)) / n),1px 余量给最后一列 - 行高:
cell_h = floor((H − 2×边距 − 间隙×(m−1)) / m) - 必须给出验证等式(每项相加 = 屏幕尺寸)
- 文字宽度检查:
字数 × 12px(步进宽)vs 格内宽(格宽 − 2px 边框)。步进宽超出但墨迹宽(约 −1px/端)放得下即可;放不下 → 减字数 / 减列数 / 回 P0 问用户
试点实例(7 行列表):
160 = 顶边距2 + 顶装饰5 + 隙4 + 状态行13 + 隙5 + 列表框128 + 底边距3列表框144×128(1px 边框,x=8):内容高7×18 + 2(上下边框) = 128,7 行 × 18px 行文字 4字 = 步进 48px,行内宽 144(左留白 4px)→ 富余,左对齐成立推论:行通栏 144px,文字左对齐(左留白 4px)+ 字体自带侧边距 = 视觉留白;行尾 ▶ 右对齐(右留白 4px)。选中行整行反色(白底黑字)。
4.4 样机制作
文件:docs/UI 设计/mockups/NN_界面名.html(仅页面)+ 配套 NN_界面名_说明.md(规格)
HTML 样机(只渲染页面本身,无其他可见内容):
| 要求 | 规定 |
|---|---|
| 自包含 | 内联 CSS,无外部依赖,无 JS,双击即开(Chrome/Edge 最佳,需 SimSun) |
| 仅页面 | HTML 只渲染 160×160 页面;不出现标题/说明区/图例等任何非页面可见内容。规格、核对、问题等一律写入配套 _说明.md |
| 视图 | ① 1:1 实际大小(160×160,所见即真机)② 4x 放大(细节检查);选中/告警等状态若属设计一部分则直接画在页面内,不单列 |
| 4x 实现 | --u CSS 变量(1:1 取 1px,4x 取 4px),全部尺寸 calc(N*var(--u)),与 1:1 同源标记 |
| 字体 | font-family: SimSun, NSimSun, serif(与设备字体同源同宽) |
| 颜色 | 仅 #000 / #fff |
| 定位 | 绝对定位 + 精确 px(对 LVGL lv_obj_set_pos 翻译最忠实);确属流式布局才用 flex |
| 文字对齐 | 默认左对齐(见 §4.3 推论);垂直居中 |
| 1bpp 禁区 CSS | border-radius / box-shadow / opacity / 渐变 / transform / z-index 层叠 —— 出现即违规 |
配套 NN_界面名_说明.md(规格,顺序固定):
- 布局参数表 —— 每个 px 值都列出(P2 翻译的依据,必须与样机 CSS 完全一致)+ 验证等式
- 行/元素内容与状态
- 字符集核对结果 —— 命中 / 新增
- 待确认问题 —— 沿用 P0 的 Q 编号
- 映射建议(菜单类界面:条目 → 子树)
- LVGL 翻译要点 —— 本屏具体的翻译提示(控件选择、状态、内存注意点)
文件头注释(HTML 顶部,简短):样机规则 + 状态:制作中 / 已定稿 (YYYY-MM-DD) + 指向配套 _说明.md
4.5 原图校验循环(渲染 → 目视比对 → 修正)
目的:消除"AI 读图不准"的风险。样机完成后渲染成图,与原图并排目视比对,有差异就改,循环到基本一致。不做像素级对齐(字体字形本就不同,像素 diff 无意义)。
- 渲染样机成图:
python tools/ui_mock/render_mockup.py <mockup.html> <out.png>(无头 Chrome/Edge 截 1:1 视图,得 160×160 PNG) - AI 读两图比对:直接读
原图.png与渲染出的 PNG,目视逐项核对——装饰形状/位置、状态行位置与文案、列表框位置/大小、行高、选中行、箭头位置 - 有差异 → 改 HTML → 重新渲染 → 再比对,循环
- 基本一致:结构与原图无明显出入(允许字体字形微差)→ 交用户浏览器最终确认
- 用户指出问题,或直接修改 HTML 后告知(HTML 即设计文件,用户改 = 设计变更)
- 定稿:几何/文案/状态/参数与用户意图一致 → 文件头注释标
已定稿 (日期),此后改动须用户明确提出
4.6 Gate(进入 P2)
- 样机已定稿
- 原图校验通过:渲染图与原图目视比对基本一致(§4.5 循环收敛)
- 待确认问题全部有答案或显式默认值
- 字符集核对完成(新增字清单确定)
五、P2:HTML → LVGL 代码
5.1 翻译对照表(CSS → LVGL v9)
| 样机(HTML/CSS) | LVGL(设备端) | 备注 |
|---|---|---|
| 160×160 白底画布 | display(I1)+ 屏幕背景色 | 极性随全局决策(Q2 类问题) |
left/top 绝对定位 |
lv_obj_set_pos(o, x, y) |
整数 px |
width/height |
lv_obj_set_size(o, w, h) |
|
| flex row / row-wrap | lv_obj_set_flex_flow(o, LV_FLEX_FLOW_ROW[_WRAP]) |
|
gap |
lv_obj_set_style_pad_gap(o, g, 0) |
|
| 1px 边框 | lv_style_set_border_width(st,1) + border_color |
|
| 仅底边框 | lv_style_set_border_side(st, LV_BORDER_SIDE_BOTTOM, 1) |
v9 分边设置 |
| 背景色 | lv_style_set_bg_color + lv_style_set_bg_opa |
1bpp 仅纯黑/纯白 |
| 文本 | lv_label_create + lv_label_set_text_static |
静态字符串(C5) |
| 文字对齐 | 标签定位 / lv_label_set_long_mode |
默认左对齐 |
| 选中反色 | LV_STATE_CHECKED 样式(bg/文字色互换) |
条目对象自身带 CHECKED(防白底白字,架构 §5.5) |
display:none |
LV_OBJ_FLAG_HIDDEN |
页面/状态切换 |
| 圆角/阴影/透明/渐变 | 禁止翻译(C8) | 样机里就不该有 |
5.2 文件策略与双端注册
- 首选原地扩展
ui_menu.c(共用源,改一处两端生效) - 确需新文件(模块独立,如曲线页渲染器)时,两处注册(C9):
- Keil:
MDK-ARM/*.uvprojx手动添加 - 模拟器:
simulator/CMakeLists.txt的UI_SOURCES列表加一行
- Keil:
- 新页面类型(内容页/编辑页)扩展现有"容器持久、条目动态"架构,禁止另起第二套架构
- 字体文件
ui_font_cn_12.c两端均已注册 → 重生成是纯内容替换,无需注册
5.3 翻译纪律
- 逐参数翻译:代码中每个 px 值必须能在样机"布局参数表"中找到出处;禁止"看着办"
- 样式沿用现有
s_style_flat/s_style_selected模式(静态样式、一次性初始化、后加优先覆盖主题) - 焦点对象恒为页面容器;条目不入输入组(架构 §5.4)
- 容器永不删除(indev 释放事件 use-after-free,架构 §5.2);只建/毁条目
- 编码纪律(C6):Edit 工具精确替换;文件保持 UTF-8 BOM 不变
- 注释按代码规范 V1.0:文件头模块块 + 函数头块(功能/参数/返回/限定/说明)
5.4 字体重生成(固定顺序,不得跳过或调换)
1. 代码落库(新界面字符串已写入 ui_menu.c 等 UI 源码)
2. python tools/font_gen/extract_cn.py # 扫描 UI 源码字符串 → 仓库根 ui_cn_chars.txt(自动含新字)
3. python tools/font_gen/make_font.py # 宋体 12px 1bpp + ASCII + 字符集 → ui_font_cn_12.c(全量重生成 + 自动补回宋体缺失的 ▶ 箭头,幂等)
4. 生成物 diff 检查:新增字在位、旧字不变、Opts 行字符集与预期一致
- 一次性环境准备:
npm i -g lv_font_conv、pip install fonttools ui_cn_chars.txt(仓库根,受 git 跟踪)与ui_font_cn_12.c随代码一起提交- 双端
LV_FONT_DEFAULT均指向&ui_font_cn_12→ 重生成后两端自动生效 - P1 已知的新增字若含生僻字(宋体可能无此字形)→ 在写码前先手动验证字形,避免"代码写完才发现是空白"
- 已处理实例:▶(U+25B6)箭头——宋体无此字形,
lv_font_conv静默跳过 → 真机方框。make_font.py已内置merge_arrow_glyph()自动从 Segoe UI Symbol 补回(幂等),重生成字体后箭头不丢。换用自带 ▶ 的字体时可忽略
- 已处理实例:▶(U+25B6)箭头——宋体无此字形,
5.5 内存预算
- 估算:
对象数 × 单价(lv_obj≈ 150B、lv_label≈ 200B 量级,以实测为准) - 实测点:建页前后打
lv_mem_monitor池水位(沿用现有menu build start/done日志模式) - 红线:最坏路径(根页 + 最深下钻 + 当前内容页)< 40KB 且留 ≥25% 余量
- 超预算 → 回到 P1 减元素 / 改按需建毁策略;不得靠加大
LV_MEM_SIZE硬扛(RAM 总预算紧张)
5.6 Gate(进入 P3)
- 代码与样机参数逐项一致(自查对照表)
- 字体已重生成且 diff 检查通过
- 内存预算通过(估算 + 代码中埋好水位日志)
- 新文件(如有)双端注册完成
六、P3:模拟器验证
6.1 动作
cd simulator; cmake --build build --config Debug(改过CMakeLists.txt才需重新cmake -B)- 离线出图(模拟器页面渲染完成后):
.\build\Debug\lvgl_sim.exe --screenshot shot.bin→ 离屏渲染首帧,输出 I1(1bpp) 位图python i1_to_png.py shot.bin -o shot.png --scale 4→ 160×160 PNG(放大便于比对)
- 读两图比对:模拟器
shot.pngvs 样机渲染图mockups/render_NN.png,按 §6.2 检查单逐项核对 (样机渲染图由 P1 的tools/ui_mock/render_mockup.py产出,即本屏"输入图片") - 不合适 → 依 HTML 样机改 LVGL 代码(HTML 为设计基准)→ 重构建 → 重新出图 → 再比对,循环至一致
- 交互行为(导航/滚动/选中)在窗口模式
.\build\Debug\lvgl_sim.exe内按键验证(离线出图只覆盖首帧静态画面)
6.2 比对检查单
| 项 | 通过标准 |
|---|---|
| 几何 | 位置/尺寸/边框与 1:1 视图逐格一致(容差 1px) |
| 文本 | 内容完整、无空白块(空白 = 缺字 → 回 §5.4)、宽度/位置正确 |
| 状态 | 选中反色正确(极性正确:黑底白字↔白底黑字不反) |
| 导航 | ↑↓ 移动(环绕)、→/Enter 进入、←/Esc 返回;滚动条行为正常 |
| 日志 | menu build done: used=… 在预算内;无 ERROR/FAIL |
6.3 已知不阻塞差异(不修)
- 浏览器灰边抗锯齿 vs 1bpp 硬边
- 浏览器 TTF 渲染 vs 位图字体的字形微差(同源宋体,形状近似即可)
- 系统 DPI 缩放导致的窗口显示大小差异
6.4 Gate(进入 P4)
- 模拟器出图
shot.png与样机渲染图render_NN.png逐项同构(§6.2,忽略 6.3) - 导航行为符合键位语义表(架构 §3.4)
七、P4:MCU 验证
@build→ 0 错误 0 警告(AGENTS.md 强制;有警告看MDK-ARM/build_log.txt)@test(一键test/run_boot_test.ps1):- 采集窗口先于烧录武装(脚本
-FlashCmd分支已保证顺序) - Required:
UI port init: OK;UI menu init: OK+ 本界面定制标记(按屏增补,如页头标题字样) - Forbidden:
FAIL;ERROR;HardFault
- 采集窗口先于烧录武装(脚本
- 真机目检(用户执行,DTU-RemoteLCD @ TCP 7003):极性 / 边框 / 文字 / 选中态 / 滚动
- 串口日志:池水位、任务栈高水位(
uxTaskGetStackHighWaterMark,uiTask 预算 16384 words)
Gate(进入 P5)
- @build 0错0警
- @test PASS
- 真机目检通过(用户确认)
八、P5:归档
- 更新
docs/UI 设计/UI_设计架构.md:- 变更记录(日期 + 一行)
- 受影响章节:文件地图 / 关键参数 / 菜单架构 / 键位语义
- HTML 样机保留于
mockups/(定稿版 = 设计档案,后续改界面须先改样机再改代码) - 台账:该界面状态 → 已完成(日期)
- 提交检查:代码 + 字体(
ui_font_cn_12.c)+ui_cn_chars.txt+ 样机 HTML + 文档 一起进 git
九、批量生产策略
- 试点先行:01 主界面走完全流程,校准各阶段耗时、翻译损耗点、内存基线(试点已完成 P1,后续阶段数据回填本文档)
- 按界面族批量:
- 列表页(复用现有条目行架构)→ 数据显示页(数值行框架)→ 参数编辑页(编辑/确认交互)→ 曲线/状态页
- 每族先做族内第一屏产出可复用的页面框架,再批量铺同族界面
- 每批收尾:字体统一重生成(各屏字符集并集,一次生成)→ 全量回归(已完成界面重跑 @test)
- 字符集只增不减;字体文件每次全量重生成,禁止手工拼合
十、常见坑速查
| 坑 | 现象 | 对策 |
|---|---|---|
| PowerShell 重写含中文文件 | 整文件 U+FFFD 乱码(不可逆) | 只许 Edit 工具(C6) |
| 用了字体子集外的字 | 该字显示空白 | P1 字符集核对 + P2 字体重生成 |
| 字体缺字形(如宋体无 ▶) | 该字符显示成方框 □ | make_font.py 已内置 merge_arrow_glyph() 自动补回;其余缺字形同法(从有该字的字体提取位图合并,§5.4) |
| 新 .c 只注册了 Keil(或只改了 CMake) | 单端编译缺函数/静默缺失 | 双端注册(C9) |
模拟器主循环调 SDL_PollEvent |
键盘全部无响应 | 事件只由 LVGL SDL 驱动内部轮询(架构 §5.7) |
| 选中行"白底白字" | 选中文字不可见 | 条目行用 label 自身携带 CHECKED 态(架构 §5.5) |
| 按键处理里删除焦点容器 | use-after-free 崩溃 | 容器持久不删(架构 §5.2) |
| LVGL 池爆 40KB | 对象创建失败/画面残缺 | §5.5 预算 + 按需建毁 |
| 极性配反 | 黑屏/白屏/不可读 | 模拟器 mono 主题 dark_mode 参数 + MCU 端背景色一致 |
| 步进宽接近/超出格宽的标签居中 | 亚像素错位/不对称 | 左对齐 + 字体侧边距(§4.3) |
关闭了 LV_DRAW_SW_COMPLEX(省代码) |
lv_line 斜线不绘制(如主界面顶部"浅 V"两腿消失,只剩水平线) |
LV_DRAW_SW_COMPLEX 两端显式 =1;LV_DRAW_SW_CIRCLE_CACHE_SIZE 须非零(默认 4,否则零长数组 C2466)(架构 §关键约束 7) |
斜线用细 line_width(如 2px) |
1bpp 阈值吃掉斜线边缘,腿偏细、比设计稿细 | 斜线加粗(腿 3px),或按段拆 lv_line 分粗细(如"浅 V"两腿 3 / 水平线 2) |
| 样机用了花哨 CSS | P2 翻译不出来 | 1bpp 禁区(C8),P1 阶段就拦下 |
十一、小改动快速通道(简化流程)
| 改动类型 | 流程 |
|---|---|
| 文案修改 | 改样机 → 改代码 → 字符集核对(有新字则重生成字体)→ P3 比对 |
| 间距/尺寸调整 | 改样机参数表 → 改代码 → P3/P4 合并为一轮验证 |
| 纯交互调整(键位/滚动) | 改样机/配套说明 → 改代码 → P3 验证行为 |
| 字体重生成 | 任何引入新字的改动后强制执行 §5.4(最常见的漏项) |
附录 A:样机 HTML 模板骨架(仅页面)
<!--
界面 NN:<名称>
状态:制作中 / 已定稿 (YYYY-MM-DD)
规则:160×160 设备像素 | 1bpp 纯黑白 | SimSun 12px(汉字 12px/字,ASCII 6px/字,行高 13)
本文件仅渲染页面本身;规格见 NN_<名称>_说明.md
-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>界面 NN | <名称></title>
<style>
html, body { margin:0; }
body { padding:24px; background:#e8e8e8;
display:flex; gap:24px; align-items:flex-start; flex-wrap:wrap; }
/* 1 设备像素 = var(--u);1:1 取 1px,4x 取 4px,同源放大 */
.ui { --u:1px; position:relative;
width:calc(160*var(--u)); height:calc(160*var(--u));
background:#fff; color:#000; overflow:hidden;
font-family:SimSun,NSimSun,serif; }
.ui.z4 { --u:4px; }
/* 元素:绝对定位 + 精确 px,全部 calc(N*var(--u));
禁用 border-radius / box-shadow / opacity / 渐变 / transform */
.el { position:absolute; box-sizing:border-box; }
</style>
</head>
<body>
<!-- 仅页面,无标题/说明/图例。选中/告警等状态若属设计一部分则直接画在页面内。 -->
<div class="ui">…标记…</div> <!-- ① 1:1 -->
<div class="ui z4">…同标记…</div> <!-- ② 4x -->
</body>
</html>
附录 B:单界面生产检查单(复制使用)
界面 NN:<名称> 负责人:____ 开始:____
P0 [ ] 台账登记(编号/作用/菜单位置)
P1 [ ] 图片分析(结构/元素/文本逐字/比例/缺失)
[ ] 字体度量核对(与 §2.1 一致)
[ ] 字符集核对:__ 命中 / __ 新增
[ ] 布局计算(验证等式:____________)
[ ] 样机 HTML 产出(仅页面:1:1 / 4x)
[ ] 配套 NN_名称_说明.md 产出(布局/内容/字符集/问题/映射/LVGL 要点)
[ ] 原图校验:渲染成图 + 读两图目视比对,循环至基本一致(§4.5)
[ ] 用户浏览器确认定稿(日期:____)
P2 [ ] 代码翻译(与样机参数逐项一致)
[ ] 字体重生成(extract → make_font → diff 检查)
[ ] 新文件双端注册(如适用,C9)
[ ] 内存预算(实测水位:____KB / 40KB)
P3 [ ] 模拟器构建 0 错误
[ ] 离线出图:--screenshot shot.bin → i1_to_png.py → shot.png
[ ] shot.png 与样机渲染图(render_NN.png)逐项比对一致(几何/文本/状态);不一致依 HTML 改码再出图(§6.1)
[ ] 导航行为验证(窗口模式按键)
P4 [ ] @build 0 错 0 警
[ ] @test PASS(Required:____ / Forbidden:FAIL;ERROR;HardFault)
[ ] 真机目检通过
P5 [ ] UI_设计架构.md 变更记录更新
[ ] 台账状态 → 已完成(代码+字体+字符集+样机+文档 一并提交)
变更记录
| 日期 | 变更 |
|---|---|
| 2026-09-03 | 初始版本:基于 01 主界面试点(P0/P1 已执行)沉淀全流程规范 |
| 2026-09-03 | P3 增加"离线出图比对":模拟器 --screenshot 出图 + simulator/i1_to_png.py 转 PNG,与样机渲染图(render_NN.png)逐项比对,不合适则依 HTML 改 LVGL 代码再出图,循环至一致 |
| 2026-09-03 | 字体 ▶(U+25B6)箭头:宋体无此字形 → 从 Segoe UI Symbol 提取位图合并入 ui_font_cn_12.c;make_font.py 内置 merge_arrow_glyph() 幂等自动补回,重生成字体后箭头不丢 |
| 2026-09-03 | 顶部"开口浅 V"装饰两腿不渲染:根因 simulator/lv_conf.h 设 LV_DRAW_SW_COMPLEX 0(省代码)→ lv_line 斜段被跳过。改为两端显式 LV_DRAW_SW_COMPLEX 1 + LV_DRAW_SW_CIRCLE_CACHE_SIZE 4;三图(原图/HTML/截图)顶部装饰现已一致 |
| 2026-09-03 | 顶部"开口浅 V"两腿偏细(1bpp 斜线边缘被阈值吃掉):ui_menu.c 装饰拆 3 段 lv_line,两腿加粗 MENU_DECO_LEG_W=3、水平线 MENU_DECO_HORIZ_W=2(deco_line_apply 统一样式),三图腿粗现已一致(约 2~3px) |