# 设计稿还原通用规范

> 适用于使用 Sketch、Figma 等设计工具 MCP 进行 UI 还原的所有场景

## 🔴 核心问题

本规范基于真实案例总结：**一个简单的输入框垂直居中问题，经历了 15+ 轮对话才修复**。

### 问题根源分析

| 问题 | 表现 | 后果 |
|------|------|------|
| **属性读取不完整** | 只读容器尺寸，忽略内部文本位置 | 无法精确计算边距和行高 |
| **分散查询** | 每次只问一个属性 | 信息不完整，反复补充 |
| **假设而非验证** | "应该差不多居中" | 实际偏差明显 |
| **试错式调整** | 逐个调整参数看效果 | 改了 A 问题引发 B 问题 |

---

## ✅ 强制执行规范

### 1. 完整读取选中元素及其所有子集

**⚠️ 这是最重要的规则**

```javascript
// 正确示例：一次性获取完整信息
function extractCompleteInfo(element) {
  console.log('=== 容器信息 ===');
  console.log(`尺寸: ${element.frame.width}x${element.frame.height}px`);
  
  console.log('=== 所有子元素 ===');
  element.layers.forEach((child, index) => {
    console.log(`\n子元素 ${index}: ${child.name} (${child.type})`);
    console.log(`  X: ${child.frame.x}px, Y: ${child.frame.y}px`);
    console.log(`  宽: ${child.frame.width}px, 高: ${child.frame.height}px`);
    
    if (child.type === 'Text') {
      console.log(`  字号: ${child.style.fontSize}px`);
      console.log(`  字重: ${child.style.fontWeight}`);
      console.log(`  颜色: ${child.style.textColor}`);
      console.log(`  行高倍数: ${(child.frame.height / child.style.fontSize).toFixed(2)}`);
    }
    
    // 递归处理嵌套子元素
    if (child.layers && child.layers.length > 0) {
      child.layers.forEach(nested => {
        console.log(`    嵌套: ${nested.name} (${nested.type})`);
        console.log(`    位置: Y=${nested.frame.y}px`);
      });
    }
  });
  
  console.log('=== 计算结果 ===');
  const textChild = element.layers.find(l => l.type === 'Text');
  if (textChild) {
    const topSpace = textChild.frame.y;
    const bottomSpace = element.frame.height - textChild.frame.y - textChild.frame.height;
    console.log(`文本上边距: ${topSpace}px`);
    console.log(`文本下边距: ${bottomSpace}px`);
    console.log(`是否居中: ${Math.abs(topSpace - bottomSpace) < 2 ? '是' : '否'}`);
  }
}
```

### 2. 必须获取的完整信息清单

| 元素类型 | 必须获取的属性 | 用途 |
|----------|----------------|------|
| **容器** | width, height, padding, 背景色/渐变 | 外层布局 |
| **文本** | Y 坐标, height, fontSize, fontWeight, color | 计算行高和边距 |
| **图标** | viewBox, path, fill, fill-opacity | 完整导出 SVG |
| **渐变** | 所有 stops（颜色+位置）, from, to | 精确还原 |
| **阴影** | x, y, blur, spread, color (全部 5 个) | 完整阴影 |
| **边框** | color, width, position | 边框样式 |

### 3. 计算而非试错

**正确流程**：
```
1. 从设计稿获取精确数值
2. 计算目标框架需要的参数
3. 一次性配置完整方案
4. 验证结果
```

**示例：TextField 行高计算**
```
设计稿数据:
- 容器高度: 36px
- 文本 Y 坐标: 8px
- 文本高度: 20px
- 字号: 14px

计算:
- 行高倍数 = 文本高度 ÷ 字号 = 20 ÷ 14 = 1.43
- 上边距 = 文本 Y 坐标 = 8px
- 下边距 = 容器高度 - Y - 文本高度 = 36 - 8 - 20 = 8px

Flutter 配置:
style: TextStyle(fontSize: 14, height: 1.43)
Container: height=36, alignment=Alignment.center
```

---

## 📋 框架特定注意事项

### Flutter

| 问题场景 | 关键属性 | 注意事项 |
|----------|----------|----------|
| TextField 居中 | `style.height`, `hintStyle.height` | 两者必须相同 |
| **Text 垂直居中偏下** | `StrutStyle` | `forceStrutHeight:true, leading:0, height=frameHeight/fontSize` |
| 容器阴影 | `clipBehavior` | 设为 `Clip.none` 避免裁剪 |
| Gap 间距 | `Gap()` vs `Gap.h()` | 默认垂直，`.h()` 水平 |
| SVG 颜色 | `colorFilter` | 不要覆盖，保留原色 |

### Vue/CSS

| 问题场景 | 关键属性 | 注意事项 |
|----------|----------|----------|
| Flex 居中 | `align-items`, `justify-content` | 注意主轴方向 |
| 文本居中 | `line-height` | 设为容器高度实现垂直居中 |
| 边框问题 | `box-sizing` | 使用 `border-box` |
| 阴影裁剪 | `overflow` | 避免 `hidden` 裁剪阴影 |

### React Native

| 问题场景 | 关键属性 | 注意事项 |
|----------|----------|----------|
| 文本居中 | `textAlignVertical` | 仅 Android 生效 |
| 输入框 | `includeFontPadding` | Android 设为 false |
| 阴影 | `elevation` vs `shadow*` | 平台差异 |

---

## 🚫 禁止事项

1. ❌ **禁止只读容器属性** - 必须读取所有子元素
2. ❌ **禁止分散查询** - 一次性获取完整信息
3. ❌ **禁止假设数值** - 必须从设计稿读取精确像素
4. ❌ **禁止试错调整** - 先计算，再编码
5. ❌ **禁止忽略透明度** - 颜色 `#RRGGBBAA` 最后两位是透明度
6. ❌ **禁止属性不一致** - 相关属性（如 style 和 hintStyle）必须统一配置

---

## ✅ 检查清单

还原任何设计稿元素前：

- [ ] 已获取目标元素的**容器尺寸**
- [ ] 已获取**所有子元素**的位置和尺寸
- [ ] 已获取文本的**Y 坐标、帧高、字号**（注意帧高 ≠ 字号，差值为 topOffset）
- [ ] Text 图层已计算 `topOffset = (frameHeight - fontSize) / 2`（Sketch 适应布局偏差）
- [ ] 已**计算**行高倍数和边距
- [ ] 已确认相关属性（style/hintStyle）**配置一致**
- [ ] 已一次性配置**完整方案**
- [ ] 已验证**所有相关元素**符合预期

---

**维护团队**: MTA工作室  
**创建日期**: 2026-01-19  
**版本**: v1.1（2026-02-25 新增 Text topOffset / StrutStyle 垂直居中修正）
