# 图片资源处理：从 DSL 提取、下载到代码引用

本文档约定设计稿中图片资源的完整处理流程，包括 URL 提取、分类下载、代码引用方式，以及 SVG 内联图标的处理规则。

---

## 1. 从 DSL 提取图片 URL

设计稿中的图片存储在 `data.styles`（或 `data.paints`）字典中，以 `paint_xxx` 为 key。当 `value` 数组中的元素为对象且含 `url` 字段时，该 paint 为图片资源。

```json
{
  "paint_10:3544": {
    "value": [{ "url": "https://xxx/.../57a5060c.png", "filters": "" }]
  }
}
```

**提取步骤**：

1. 遍历 `data.styles`（或 `data.paints`）中所有 key 以 `paint_` 开头的条目。
2. 检查 `value[0]` 是否为对象且含 `url` 字段 → 记录为图片资源。
3. 纯色（如 `"#FFA01A"`）或渐变（如 `"linear-gradient(...)"` ）不属于图片资源，跳过。

---

## 2. 图片用途识别与命名

通过追踪 paint ID 在 `nodes` 树中的 `fill` 引用位置，判断图片用途并命名：

| 用途 | 节点特征 | 命名示例 |
|------|---------|---------|
| **背景图** | 位于最外层 FRAME 内，尺寸接近画布宽度 | `bg_light.png`、`bg_main.png` |
| **功能图标** | 位于 Tab/Mode 等功能区，尺寸较小（< 40px） | `icon_color.png`、`icon_rhythm.png` |
| **场景/列表项图标** | 位于网格或列表的重复项内 | `scene_01.png` ~ `scene_12.png` |
| **装饰/插图** | 独立的装饰元素 | `illus_empty.png`、`deco_star.png` |

---

## 3. 图片下载

所有图片资源**必须下载到 `src/res/` 目录**，不直接在代码中使用 CDN URL（CDN URL 可能需要认证、有跨域限制，且小程序离线时不可用）。

**下载方式**（在 Agent 环境中）：

```bash
mkdir -p src/res
curl -sSL -o src/res/bg_light.png "<paint_url>"
curl -sSL -o src/res/icon_color.png "<paint_url>"
# ...批量下载所有图片
```

**注意事项**：
* 若 CDN URL 返回 401/403，需先通过 SSO 工具获取认证 Token。
* 下载后检查文件大小，确认非 HTML 错误页。
* 设计稿导出图片可能较大（1-3MB），功能上先保证显示正确，后续可优化压缩。

---

## 4. 代码中引用图片

### 4.1 TSX 中通过 import 引用（推荐）

**必须使用相对路径**引入图片，不使用 `@/res/` 别名：

```tsx
// ✅ 正确：相对路径
import bgLight from '../../res/bg_light.png';
import iconColor from '../../res/icon_color.png';

// ❌ 错误：别名路径（部分构建环境不支持）
import bgLight from '@/res/bg_light.png';
```

在 Image 组件中使用：

```tsx
<Image className={styles.sceneIcon} src={scene.icon} mode="aspectFill" />
```

### 4.2 Less 中引用背景图

**同样使用相对路径**：

```less
// ✅ 正确
.bgWrap {
  background: url('../../res/bg_light.png') center top / cover no-repeat;
}

// ❌ 错误
.bgWrap {
  background: url('@/res/bg_light.png') center top / cover no-repeat;
}
```

### 4.3 Image 组件的 mode 选择

| mode | 行为 | 适用场景 | 注意事项 |
|------|------|---------|---------|
| `aspectFill` | 保持宽高比缩放，短边填满，裁剪长边 | **蒙版裁剪图片**（圆形/形状蒙版内的图片）、需填满容器的场景图标 | 推荐用于所有需要裁剪填满的场景 |
| `aspectFit` | 保持宽高比缩放，长边完全显示，不裁剪 | 功能图标（需保持完整显示）、Tab 图标 | 容器可能有留白 |
| `scaleToFill` | 拉伸图片填满元素，**不保持宽高比** | 仅用于全屏背景图 | ⚠️ 会导致图片变形，**禁止**用于蒙版裁剪图片 |

**常见错误**：对蒙版裁剪的图片使用 `scaleToFill`，导致图片被拉伸变形。蒙版场景下图片的尺寸和偏移已由 DSL 精确定义，使用 `aspectFill` 既能保持比例又能填满指定区域。

### 4.4 蒙版图片的处理流程

当 DSL 中出现 `mask: "alpha"` 的蒙版结构时（详见 [07-dsl-fields.md](./07-dsl-fields.md#蒙版-maskalpha)），图片的展示方式需要特殊处理：

1. **提取每张图片的蒙版数据**：从 DSL 的蒙版 GROUP 中逐一提取内容节点的 `width`/`height`/`relativeX`/`relativeY`。
2. **数据存入组件状态**：将蒙版数据作为数据数组的一部分，每个条目携带独立的 mask 参数（rpx 单位，设计稿 px × 2）。
3. **通过 inline style 传入**：由于每张图片的偏移不同，不能用统一的 CSS class，需通过 `style` 属性传入精确值。
4. **mode 使用 `aspectFill`**：保持图片比例，配合 `overflow: hidden` 的父容器实现裁剪。

完整实现示例见 → [02-styles.md](./02-styles.md#图片蒙版裁剪合法-absolute-场景三)。

---

## 5. SVG 内联图标（小型矢量图标）

对于设计稿中由 PATH / SVG_ELLIPSE 等矢量节点组成的**小型图标**（如刷新箭头、列表图标、关闭按钮），无需下载为图片文件，可直接用 SVG data URI 内联到 CSS 中：

```less
.refreshBtn {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23fff'%3E%3Cpath d='M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z'/%3E%3C/svg%3E");
  background-position: center;
  background-repeat: no-repeat;
  background-size: 50rpx 50rpx;
}
```

**判断标准**：
* 节点由 PATH / SVG_ELLIPSE / VECTOR 等矢量类型组成。
* 整体尺寸 ≤ 32px（设计稿坐标）。
* 颜色为纯色（单一 fill 或 stroke）。
* 满足以上条件 → 构造 SVG data URI；否则按图片资源下载处理。

**构造规则**：
1. 从设计稿矢量节点中提取 `path.data` 作为 SVG `<path d="..."/>`。
2. 从 `fill`/`strokeColor` 对应的 paint ID 解析颜色。
3. 使用节点的 `layoutStyle` 宽高作为 `viewBox`。
4. URL 编码特殊字符（`#` → `%23`，`<` → `%3C`，`>` → `%3E`）。

---

## 6. className 传递规范

Ray 小程序的 `View`/`Text` 等组件的 `className` 属性**只接受字符串类型**，不支持数组。需要条件拼接 className 时，必须使用 `clsx` 工具：

```tsx
// ✅ 正确：使用 clsx 拼接为字符串
import clsx from 'clsx';

<View className={clsx(styles.modeItem, mode.active && styles.modeItemActive)} />

// ❌ 错误：传数组会导致 className 失效，元素样式全部丢失
<View className={[styles.modeItem, mode.active && styles.modeItemActive]} />
```

**此规则为强制约束**：传数组不会报运行时错误但会导致所有样式失效，是常见且难以排查的问题。

---

## 完整流程总结

```
DSL paints → 提取含 url 的 paint → 追踪 fill 引用确定用途
    ↓
下载到 src/res/（按用途命名）
    ↓
TSX: import xxx from '../../res/xxx.png' + <Image src={xxx} mode="..." />
Less: url('../../res/xxx.png') 用于背景
    ↓
蒙版图片（mask: "alpha"）→ 逐一提取 DSL 偏移/尺寸 → inline style + aspectFill
    ↓
小型矢量图标 → SVG data URI 内联到 CSS
    ↓
className 条件拼接 → clsx()，禁止数组
```
