# 间距测量与 rpx 换算 ## 核心换算公式 ``` 小程序 viewport 宽 = 750rpx = 430px RPX_FACTOR = 750 / 430 = 1.7442 精确公式:rpx = ceil(H5_px × 1.7442 / 2) × 2 简化心算:rpx ≈ H5_px × 2 × 0.875 取偶(6px、14px 处有 2rpx 偏差,以精确公式为准) 圆角例外:border-radius = px × 2(不乘系数),数值更整洁 ``` --- ## 结构化审计方法论(三阶段) 本方法论适用于两个场景: - **迁移前**:对 H5 页面做详细审计,产出映射表,指导 MP 开发 - **迁移后**:基于映射表做双端对比,逐元素验证偏差 ### 阶段 0:结构拆解(自顶向下) 目标:将页面拆解为树状元素清单,建立 H5↔MP 映射表。 #### 0.1 拆解流程 1. 读取 H5 源码(`docs/h5_ui/pages/.html`),识别页面首层容器 2. 对每个首层容器,递归拆解子元素,直到到达叶子节点 3. 叶子节点定义:**单一样式属性可描述**的最小视觉单元 - 文字 span(font-size + color + weight 即可描述) - 图标 image / SVG - 纯色/渐变 view(background 即可描述) - 分隔线(border 即可描述) 4. 为每个节点记录:层级深度、CSS 选择器、Tailwind 类名、角色描述 #### 0.2 H5 审计映射表(迁移前必做) 拆解完成后,产出结构化映射表。此表是后续所有工作的基准: ```markdown ## H5 审计映射表 | # | 层级 | 元素描述 | H5 选择器 | Tailwind 类名 | 理论 rpx 值 | MP 选择器 | 备注 | |---|------|---------|-----------|--------------|------------|-----------|------| | 1 | L0 | 页面根容器 | .page | px-4 bg-gray-1 | padding: 0 14rpx | .page | — | | 2 | L1 | Banner 区域 | .banner-bg | h-40 rounded-2xl | height: 140rpx, radius: 32rpx | .banner-bg | 渐变需简化 | | 3 | L1 | 统计卡片 | .summary-card | p-4 rounded-xl | padding: 28rpx, radius: 24rpx | .summary-card | — | | 4 | L2 | 卡片标题 | .summary-card h3 | text-base font-semibold | font-size: 28rpx, weight: 600 | .card-title | — | | 5 | L2 | 金额数值 | .amount | text-2xl font-semibold | font-size: 42rpx, weight: 600 | .amount | — | | ... | ... | ... | ... | ... | ... | ... | ... | ``` 列说明: - **层级**:L0=页面根, L1=首层区块, L2=区块内组件, L3=组件内元素... - **理论 rpx 值**:从 Tailwind 类名查速查表换算,不是实测值 - **MP 选择器**:迁移前留空,迁移后填入对应的 WXML 选择器 - **备注**:CSS 风险特性、不可消除差异、特殊处理方案 #### 0.3 结构完整性校验 映射表同时用于校验 MP 端结构完整性: | 校验项 | 方法 | 判定 | |--------|------|------| | 元素缺失 | H5 映射表中有、MP 选择器为空或不存在 | P0 | | 元素多余 | MP 中存在映射表未列出的视觉元素 | 检查是否为调试元素,否则标注 | | 顺序错误 | MP 元素 DOM 顺序与映射表不一致 | P0 | | 嵌套层级不一致 | MP 的父子关系与 H5 不同 | P1(可能影响间距计算) | 校验时机: - **迁移前**:映射表产出后,MP 选择器列全部留空,无需校验 - **迁移后**:填入 MP 选择器,逐行校验存在性和顺序 - **对比审计时**:先跑结构完整性校验,通过后再进入测量阶段 ### 阶段 1:逐级测量(自底向上) 目标:从叶子节点开始,逐级向上测量每个元素的尺寸和间距。 #### 1.1 测量顺序 ``` 叶子节点(L3/L4)→ 组件容器(L2)→ 区块容器(L1)→ 页面根(L0) ``` 自底向上的原因:内层元素的实测尺寸决定了外层容器的实际高度。先测内层,才能判断外层的 padding/gap 是否正确。 #### 1.2 每个元素的标准测量维度 | 维度 | 测量方法 | 适用节点 | |------|---------|---------| | width / height | `getBoundingClientRect()` | 所有 | | padding(四方向) | `getComputedStyle().paddingTop/Right/Bottom/Left` | 容器节点 | | margin(四方向) | `getComputedStyle().marginTop/Right/Bottom/Left` | 所有 | | font-size | `getComputedStyle().fontSize` | 文字节点 | | line-height | `getComputedStyle().lineHeight` | 文字节点 | | border-radius | `getComputedStyle().borderRadius` | 有圆角的节点 | | 与父容器的 offset | `child.rect.top - parent.rect.top - parent.paddingTop` | 所有子节点 | | 与相邻兄弟的 gap | `next.rect.top - current.rect.bottom` | 同级相邻节点 | #### 1.3 H5 端测量(Playwright MCP / measure_gaps.py) 方式 A:Playwright MCP `browser_evaluate` ```javascript // 测量单个元素的完整属性 async (selector) => { const el = document.querySelector(selector); if (!el) return { error: 'not found' }; const rect = el.getBoundingClientRect(); const cs = getComputedStyle(el); return { selector, width: rect.width, height: rect.height, top: rect.top, left: rect.left, paddingTop: parseFloat(cs.paddingTop), paddingRight: parseFloat(cs.paddingRight), paddingBottom: parseFloat(cs.paddingBottom), paddingLeft: parseFloat(cs.paddingLeft), marginTop: parseFloat(cs.marginTop), marginBottom: parseFloat(cs.marginBottom), fontSize: parseFloat(cs.fontSize), lineHeight: cs.lineHeight === 'normal' ? 'normal' : parseFloat(cs.lineHeight), fontWeight: cs.fontWeight, borderRadius: cs.borderRadius, color: cs.color, backgroundColor: cs.backgroundColor }; } ``` > Playwright MCP 不支持 `file://` 协议。需先起本地 HTTP 服务: > `python -m http.server 8765 --directory docs/h5_ui/pages` > 然后用 `browser_navigate("http://localhost:8765/.html")` 访问。 方式 B:`scripts/ops/measure_gaps.py`(批量测量) ```bash uv run python scripts/ops/measure_gaps.py --selectors "" "" ... ``` #### 1.4 MP 端测量(SelectorQuery) ```javascript // 微信开发者工具 MCP evaluate_script async (selector) => { return new Promise(resolve => { const query = wx.createSelectorQuery(); query.select(selector).boundingClientRect(rect => { query.select(selector).fields({ computedStyle: ['paddingTop','paddingRight','paddingBottom','paddingLeft', 'marginTop','marginBottom','fontSize','lineHeight', 'fontWeight','borderRadius','color','backgroundColor'] }, style => { resolve({ rect, style }); }).exec(); }).exec(); }); } ``` #### 1.5 测量结果记录格式 每个元素的测量结果追加到映射表: ```markdown | # | 元素 | 属性 | H5 实测(px) | 理论 rpx | MP 实测(rpx) | 偏差 | 级别 | |---|------|------|------------|---------|-------------|------|------| | 4 | 卡片标题 | font-size | 16px | 28rpx | 28rpx | 0 | ✅ | | 4 | 卡片标题 | line-height | 24px | 42rpx | 36rpx | -6rpx | P3 | | 4 | 卡片标题 | font-weight | 600 | 600 | 400 | — | P6 | | 3 | 统计卡片 | padding-top | 16px | 28rpx | 30rpx | +2rpx | ✅ | | 3→4 | 卡片→标题 gap | margin-top | 0px | 0rpx | 0rpx | 0 | ✅ | ``` ### 阶段 2:偏差审计 目标:基于测量结果,逐属性判定偏差级别,输出结构化审计报告。 #### 2.1 偏差判定标准 | rpx 偏差 | 级别 | 说明 | |----------|------|------| | 0 | ✅ 通过 | 完全匹配 | | ≤ 2rpx | ✅ 通过 | rpx 取偶导致的不可避免误差 | | > 2rpx 且 ≤ 4rpx | ⚠️ 警告(P4-P7) | 像素级精调可修复 | | > 4rpx | ❌ 不通过(P0-P3) | 必须修正 | 特殊属性判定: - **font-weight**:不用 rpx 偏差,直接比对数值(400/500/600/700),不一致即 P6 - **color**:不用 rpx 偏差,比对 hex 值,不在标准色表中即 P5 - **border-radius**:使用 `px × 2` 公式(不乘 1.7442),偏差 > 2rpx 即 P4 - **结构缺失/顺序错误**:直接 P0,不进入测量 #### 2.2 审计报告模板 ```markdown # 结构化审计报告 ## 基本信息 - 页面: - 审计日期:YYYY-MM-DD - H5 元素总数:N - MP 元素总数:M - 结构完整性:✅ 通过 / ❌ 缺失 K 个元素 ## 结构完整性校验 | # | H5 元素 | MP 状态 | 问题 | |---|---------|---------|------| | 7 | .env-arrow svg | ❌ 缺失 | MP 不支持 inline SVG,需导出 | ## 偏差汇总 | 级别 | 数量 | 占比 | |------|------|------| | ✅ 通过 | XX | XX% | | ⚠️ P4-P7 | XX | XX% | | ❌ P0-P3 | XX | XX% | ## 逐元素偏差明细 (按映射表 # 排序,仅列出有偏差的属性) | # | 元素 | 属性 | H5 值 | 理论 rpx | MP 值 | 偏差 | 级别 | 修正建议 | |---|------|------|-------|---------|-------|------|------|---------| | ... | ... | ... | ... | ... | ... | ... | ... | ... | ## 不可消除差异(不计入偏差) - 字体渲染差异(Noto Sans SC vs 系统字体) - 行内元素高度系统性偏小 ~7% - 抗锯齿差异 ``` #### 2.3 迁移前审计 vs 迁移后对比 | 维度 | 迁移前审计 | 迁移后对比 | |------|-----------|-----------| | 输入 | H5 源码 | H5 源码 + MP 源码 | | 映射表 MP 列 | 留空 | 填入实际选择器 | | 测量端 | 仅 H5 | H5 + MP 双端 | | 产出 | 映射表 + 理论 rpx 值 | 偏差审计报告 | | 用途 | 指导 WXSS 编写 | 验证迁移质量 | 迁移前审计产出的映射表直接作为迁移后对比的输入——MP 开发完成后,填入 MP 选择器列,跑结构完整性校验 + 逐级测量,即可得到偏差报告。 --- ## Tailwind → WXSS 完整速查表 ### 字号与行高 | Tailwind | H5 px | rpx | 说明 | |----------|-------|-----|------| | text-xs | 12px / 16px | 22rpx / 28rpx | 字号 / 行高 | | text-sm | 14px / 20px | 26rpx / 36rpx | 字号 / 行高 | | text-base | 16px / 24px | 28rpx / 42rpx | 字号 / 行高 | | text-lg | 18px / 28px | 32rpx / 50rpx | 字号 / 行高 | | text-xl | 20px / 28px | 36rpx / 50rpx | 字号 / 行高 | | text-2xl | 24px / 32px | 42rpx / 56rpx | 字号 / 行高 | ⚠️ Tailwind `text-*` 同时设置 font-size 和 line-height。MP 迁移时必须同时迁移两个属性,遗漏 line-height 会导致行高塌陷。 ### 间距 | Tailwind | H5 px | rpx | |----------|-------|-----| | p-1 / gap-1 | 4px | 8rpx | | p-1.5 / gap-1.5 | 6px | 12rpx | | p-2 / gap-2 | 8px | 14rpx | | p-3 / gap-3 / space-y-3 | 12px | 22rpx | | p-4 / gap-4 | 16px | 28rpx | | p-5 / gap-5 | 20px | 36rpx | | p-6 / gap-6 | 24px | 42rpx | | p-8 / gap-8 | 32px | 56rpx | ⚠️ `space-y-N` 换算陷阱:严格按精确公式 `ceil(px × 1.7442 / 2) × 2`,不要心算跳步。 例:`space-y-3` = 12px → 12×1.7442=20.93 → ceil(20.93/2)×2 = ceil(10.47)×2 = 22rpx(不是 20rpx)。 ### 常用 px → rpx 对照 ``` 2px → 4rpx 3px → 6rpx 4px → 8rpx 5px → 10rpx 6px → 12rpx 8px → 14rpx 10px → 18rpx 11px → 20rpx 12px → 22rpx 14px → 26rpx 16px → 28rpx 18px → 32rpx 20px → 36rpx 24px → 42rpx 28px → 50rpx 32px → 56rpx 36px → 64rpx 40px → 70rpx 44px → 78rpx 48px → 84rpx ``` --- ## 附录 A:measure_gaps.py 详细用法 路径:`scripts/ops/measure_gaps.py` ### 基本用法 ```bash # 测量页面内所有 .task-card 元素的尺寸和间距 uv run python scripts/ops/measure_gaps.py task-list --selectors ".task-card" # 测量多个选择器(按 DOM 顺序,计算相邻间距) uv run python scripts/ops/measure_gaps.py board-finance --selectors ".summary-header" ".summary-content" ".grid-cols-3" # 指定两个元素的直接间距 uv run python scripts/ops/measure_gaps.py task-list --pairs ".sticky-header" ".task-card:first-child" # 页面中下方元素(需 scrollTop) uv run python scripts/ops/measure_gaps.py performance --selectors ".perf-section" --scroll 1200 ``` ### 输出内容 - 元素尺寸表:top_px, h_px, paddingT/B, marginT/B, gap, fontSize, lineHeight, h_rpx - 相邻元素垂直间距表:gap_px 和 gap_rpx - audit.md 可直接粘贴的 Markdown 表格 ### 常用 CSS 选择器快查 | 元素类型 | 选择器示例 | |---|---| | 页面内边距容器 | `.px-4`, `.px-6`, `[class*="px-"]` | | 卡片 | `.task-card`, `[class*="card"]` | | 列表项间距 | `.list-item`, `li`, `[class*="item"]` | | Sticky 头部 | `.sticky`, `.filter-bar`, `[class*="sticky"]` | | Banner | `.banner-bg`, `[class*="banner"]` | | 标签/徽章 | `.tag`, `.badge`, `[class*="tag"]` | --- ## 附录 B:图像反推验证(截图偏差定位) 当 diff 图显示某元素位置偏移时,用截图像素反推实际间距: ``` 实际间距(px) = diff 图中偏移像素数 ÷ DPR DPR = 1.5 示例:diff 图中元素 A 比 H5 下移 9 像素 实际偏差 = 9 / 1.5 = 6px → 查速查表 → 12rpx 应调整 WXSS 中对应 margin/padding ```