# Sketch 设计稿样式不完整提取问题

> **问题标签**: `sketch`, `design`, `extraction`, `incomplete`, `一次性提取`  
> **框架**: Flutter  
> **严重程度**: 高（导致多轮对话和返工）

---

## 🔍 问题识别

### 用户描述关键词
- "漏了xxx属性"
- "渐变怎么还原"
- "阴影参数是多少"
- "这个图标是圆形的吗"
- "设计稿上的圆角多大"

### 问题特征
- [ ] AI 每次只查询单个属性（颜色、尺寸、阴影等）
- [ ] 需要多轮对话才能获取完整样式信息
- [ ] 使用近似值代替精确值（如 Material Icons 代替 SVG）
- [ ] 假设属性而非验证（假设是圆形、假设颜色相同等）

---

## 🎯 核心原理

**根本原因**：分散查询导致信息不完整，浪费时间。

| 问题 | 表现 | 根因 |
|------|------|------|
| 属性读取不完整 | 漏读渐变、圆角、阴影参数 | 只读取部分属性 |
| 假设而非验证 | 假设圆形/颜色/图标 | 未从设计稿验证 |
| 使用近似值 | 用 Material Icons 代替 | 未导出原始 SVG |
| 分散查询 | 多轮对话才获取完整信息 | 每次只查一个属性 |

---

## ✅ 正确解决方案

### 强制执行：一次性完整提取脚本

**在还原任何 UI 元素前，必须使用以下脚本一次性提取所有属性：**

```javascript
// Sketch 完整样式提取脚本 - 必须使用
const sketch = require('sketch');
const document = sketch.getSelectedDocument();
const page = document.selectedPage;

function extractFullStyle(layerName) {
  const layer = sketch.find(`[name="${layerName}"]`, page)[0];
  if (!layer) return console.log(`Layer "${layerName}" not found`);

  console.log('=== 基本信息 ===');
  console.log(`Name: ${layer.name} (${layer.type})`);
  console.log(`Frame: ${layer.frame.width}x${layer.frame.height} @ (${layer.frame.x}, ${layer.frame.y})`);

  const style = layer.style;

  // 1. 填充（颜色/渐变）
  console.log('=== 填充 ===');
  if (style.fills?.length) {
    style.fills.forEach((fill, i) => {
      console.log(`Fill ${i}: Type=${fill.fillType}, Enabled=${fill.enabled}`);
      if (fill.fillType === 'Color') {
        console.log(`  Color: ${fill.color}`);
      } else if (fill.fillType === 'Gradient') {
        console.log(`  Gradient: ${fill.gradient.gradientType}`);
        console.log(`  From: (${fill.gradient.from.x.toFixed(2)}, ${fill.gradient.from.y.toFixed(2)})`);
        console.log(`  To: (${fill.gradient.to.x.toFixed(2)}, ${fill.gradient.to.y.toFixed(2)})`);
        fill.gradient.stops.forEach((stop, j) => {
          console.log(`  Stop ${j}: ${stop.color} @ ${stop.position}`);
        });
      }
    });
  } else {
    console.log('No fills');
  }

  // 2. 边框
  console.log('=== 边框 ===');
  if (style.borders?.length) {
    style.borders.forEach((border, i) => {
      console.log(`Border ${i}: Color=${border.color}, Width=${border.thickness}, Position=${border.position}, Enabled=${border.enabled}`);
    });
  } else {
    console.log('No borders');
  }

  // 3. 阴影
  console.log('=== 阴影 ===');
  if (style.shadows?.length) {
    style.shadows.forEach((s, i) => {
      console.log(`Shadow ${i}: Color=${s.color}, Offset=(${s.x}, ${s.y}), Blur=${s.blur}, Spread=${s.spread}`);
    });
  } else {
    console.log('No shadows');
  }

  // 4. 内阴影
  console.log('=== 内阴影 ===');
  if (style.innerShadows?.length) {
    style.innerShadows.forEach((s, i) => {
      console.log(`InnerShadow ${i}: Color=${s.color}, Offset=(${s.x}, ${s.y}), Blur=${s.blur}, Spread=${s.spread}`);
    });
  } else {
    console.log('No inner shadows');
  }

  // 5. 圆角
  console.log('=== 圆角 ===');
  if (layer.layers?.[0]?.points) {
    const points = layer.layers[0].points;
    const radii = points.map(p => p.cornerRadius);
    const allSame = radii.every(r => r === radii[0]);
    if (allSame) {
      console.log(`CornerRadius: ${radii[0]} (all corners)`);
    } else {
      console.log(`CornerRadius: [${radii.join(', ')}] (TL, TR, BR, BL)`);
    }
  } else if (layer.points) {
    const radii = layer.points.map(p => p.cornerRadius);
    console.log(`CornerRadius: ${radii[0]}`);
  } else {
    console.log('No corner radius (circle or custom shape)');
  }

  // 6. 不透明度
  console.log('=== 不透明度 ===');
  console.log(`Opacity: ${style.opacity}`);

  // 7. 子元素（如果是 Group）
  if (layer.layers?.length) {
    console.log('=== 子元素 ===');
    layer.layers.forEach(child => {
      console.log(`- ${child.name} (${child.type}): ${child.frame.width}x${child.frame.height}`);
    });
  }
}

// 使用示例
extractFullStyle('Button Background');
extractFullStyle('Icon');
```

### 图标导出：必须使用 SVG

**禁止使用 Material Icons 或其他近似图标：**

```javascript
// 导出图标 SVG
const sketch = require('sketch');
const layer = sketch.find('[name="Icon Name"]', sketch.getSelectedDocument().selectedPage)[0];
if (layer) {
  const svg = sketch.export(layer, { formats: 'svg', output: false });
  console.log(svg.toString());
}
```

### 还原检查清单（强制）

在还原任何 UI 元素前，必须确认以下所有属性：

| 属性 | 检查项 | Flutter 对应 |
|------|--------|--------------|
| 尺寸 | width, height | `SizedBox(width:, height:)` |
| 位置 | x, y | `Positioned(left:, top:)` |
| 颜色/渐变 | fills[0] | `BoxDecoration(color:, gradient:)` |
| 圆角 | cornerRadius | `BorderRadius.circular()` |
| 边框 | borders | `Border.all()` |
| 阴影 | shadows | `BoxShadow()` |
| 透明度 | opacity | `Opacity(opacity:)` |

---

## ❌ 常见错误排查路线（避免重复）

| 尝试方向 | 为什么无效 | 浪费时间 |
|----------|-----------|---------|
| 每次只查一个属性 | 需要多轮对话才能获取完整信息 | 5-10 轮 |
| 假设是圆形 | 未验证 cornerRadius，可能是圆角矩形 | 2-3 轮 |
| 使用 Material Icons | 与设计稿不完全一致 | 3-5 轮 |
| 忽略渐变参数 | 只看到 from/to，未读取 stops | 2-4 轮 |

---

## 📁 适用场景

- ✅ 从 Sketch 设计稿还原任何 UI 元素
- ✅ 需要获取完整样式信息
- ✅ 包含渐变、阴影、复杂圆角的设计
- ✅ 图标还原（必须导出 SVG）

---

## 🔗 相关案例

- [sketch-图标尺寸.md](./sketch-图标尺寸.md) - 图标尺寸提取错误
- [svg-颜色异常.md](./svg-颜色异常.md) - SVG 颜色处理问题
- [svg-未居中.md](./svg-未居中.md) - SVG 居中问题
