# 第二步：样式解析与资源映射规则

布局与样式的**字段来源与解析约定**（圆角、fills、stroke、effect、文本、矢量等）见 → [07-dsl-fields.md](./07-dsl-fields.md)。以下为 Ray 输出侧约束与资源处理。

---

## 布局约束

* **严禁使用 `position: absolute`**（除非是背景装饰图层，见下方「背景图+渐变叠加层」模式）。背景矩形应合并为父容器的 `background-color` 或 `border-radius`。
* **宽度策略**：若容器宽度 > 父级 90%，设为 `width: 100%`；文字节点设为 `width: auto`。
* **z-index 层级**：若存在遮挡关系（根据 `relativeX/relativeY` 或设计层级判断），**需要显示在上层的元素**须设置比下层元素**更高的 `z-index`**，避免被遮挡；下层元素使用较小或默认 `z-index`。

---

## 资源与样式 Lookup 优化（精准映射）

* **样式字典**：从 `data.styles` 或 `data.paints` 按 styleId 查找；颜色、渐变、图片 URL 的解析规则见 [07-dsl-fields.md](./07-dsl-fields.md)。
* **SVG 路径与填充**：解析矢量节点（VECTOR/PEN/ELLIPSE 等）时，必须从 `styles` 字典中根据 `fill` 字段的 ID（如 `paint_sa2:17458`）动态查找十六进制颜色或渐变值，并应用到对应 View/Image 的样式（如背景色或 Ray 支持的 fill 等效属性）；不能写死颜色。
* **背景图片**：若 `paint` 类型包含 `url`，映射为 **Image** 组件（优先）或容器的 `background-image`；若父容器有圆角，子图片必须设置 `object-fit: cover`（或 Ray 等效）以防变形。图片填充应使用 Image 组件而非 div 背景，与 07-dsl-fields 约定一致。

---

## 资源处理

图片资源的**完整处理流程**（DSL 提取 → 下载 → 代码引用 → SVG 内联 → className 规范）见 → [08-image-assets.md](./08-image-assets.md)。以下为核心规则摘要：

* **图片必须下载到本地**：从 DSL 的 `styles`/`paints` 字典中提取含 `url` 的 paint，**下载到 `src/res/`** 目录，不直接使用 CDN URL。
* **引用方式**：TSX 中通过**相对路径** `import` 引入（如 `import img from '../../res/bg.png'`），**禁止**使用 `@/res/` 别名。Less 中同理使用相对路径 `url('../../res/bg.png')`。
* **Image 组件**：使用 `Image` 组件展示图片，`mode` 按场景选择 `aspectFill`（裁剪填满）、`aspectFit`（完整展示）或 `scaleToFill`（拉伸填满）。
* **小型矢量图标**：设计稿中 ≤ 32px 的纯色矢量图标（PATH/SVG_ELLIPSE），使用 **SVG data URI** 内联到 CSS `background-image` 中，无需下载为图片文件。
* **文本**：必须放在 **Text** 组件内。解析 `node.text[0].text`，根据 `font` ID 关联 `styles` 提取 `size`、`family`、`weight`、`lineHeight`，在 Less 中体现为对应类的字体样式；颜色、对齐（textAlign/textAlignVertical）、单行垂直居中等字段与优先级见 [07-dsl-fields.md](./07-dsl-fields.md)。

---

## className 传递规范（强制）

Ray 小程序 View/Text 等组件的 `className` **仅接受字符串**，传数组会导致样式全部失效且无运行时报错。条件拼接 className 必须使用 `clsx`：

```tsx
// ✅ 正确
import clsx from 'clsx';
<View className={clsx(styles.item, isActive && styles.itemActive)} />

// ❌ 错误：样式全部失效
<View className={[styles.item, isActive && styles.itemActive]} />
```

---

## 背景图 + 渐变叠加层模式

许多面板页设计稿顶部有装饰性背景图（如灯光散景、渐变色块），并在其上叠加一层渐变使其自然过渡到页面底色。这是**唯一合法使用 `position: absolute` 的场景**。

**标准实现**：

```tsx
<View className={styles.page}>
  {/* 背景层：absolute 定位，不参与 Flex 流 */}
  <View className={styles.bgWrap}>
    <View className={styles.bgGradient} />
  </View>

  {/* 主内容：正常 Flex 流 */}
  <View className={styles.navBar}>...</View>
  <View className={styles.content}>...</View>
</View>
```

```less
.page {
  position: relative;
  width: 100%;
  min-height: 100vh;
  background-color: #000;
  display: flex;
  flex-direction: column;
}

.bgWrap {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  height: 680rpx; /* 设计稿背景区域高度 × 2 */
  z-index: 0;
  overflow: hidden;
  background: url('../../res/bg_light.png') center top / cover no-repeat;
}

.bgGradient {
  position: absolute;
  left: 0; top: 0; right: 0; bottom: 0;
  background: linear-gradient(181deg, rgba(0,0,0,0) 3%, #000 50%, #000 96%);
  z-index: 1;
}

/* 主内容需设置 z-index > 0 以覆盖在背景之上 */
.navBar { position: relative; z-index: 2; }
.content { position: relative; z-index: 1; }
```

**关键要点**：
* 背景层 `z-index: 0`，所有主内容层 `z-index ≥ 1` + `position: relative`。
* 背景图用 Less `url()` 引用（装饰性），不用 Image 组件。
* 渐变叠加层的颜色和方向从 DSL 的 `paint` 中 `linear-gradient(...)` 值提取。
* 背景层固定高度，不参与 Flex，主内容正常 column 布局。

---

## 网格项内叠加徽章（合法 absolute 场景二）

设计稿中网格项（如颜色圆圈）上常有小型叠加元素（如"+"徽章、选中角标），需在网格单元内使用 `position: absolute`。这是除背景装饰层之外**第二个合法使用 absolute 的场景**。

```tsx
<View className={styles.cell}>
  <View className={styles.circle} style={{ backgroundColor: color }} />
  <View className={styles.badge}>
    <View className={styles.badgePlus} />
  </View>
</View>
```

```less
.cell {
  position: relative;
  width: 96rpx;
  height: 96rpx;
}
.circle {
  width: 96rpx;
  height: 96rpx;
  border-radius: 50%;
}
.badge {
  position: absolute;
  top: -4rpx;
  right: -4rpx;
  width: 48rpx;
  height: 48rpx;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.5);
  border: 1rpx solid rgba(255, 255, 255, 0.25);
  display: flex;
  align-items: center;
  justify-content: center;
}
```

**适用条件**：
* 徽章/角标与主体元素存在明确的叠加关系（DSL 中两者 relativeX/Y 有重叠）。
* 仅在单元格内部使用 absolute，**不跨容器**。

---

## 图片蒙版裁剪（合法 absolute 场景三）

设计稿中常见「用圆形/形状蒙版裁剪大图」的模式，DSL 中表现为 GROUP 内含 `mask: "alpha"` 的 SVG_ELLIPSE + 尺寸大于蒙版的 LAYER/IMAGE 节点。蒙版字段的识别规则见 → [07-dsl-fields.md](./07-dsl-fields.md#蒙版-maskalpha)。

### 实现方案（正圆蒙版，推荐）

对于正圆形蒙版（`SVG_ELLIPSE` 且 `width === height`），使用 `overflow: hidden` + `border-radius: 50%` 实现裁剪：

```tsx
// scenes 数组中存储每个场景的蒙版数据（从 DSL 逐一提取）
const scenes = [
  { id: 1, icon: scene01, mask: { w: 254, h: 167, x: -19, y: -10 } },
  { id: 2, icon: scene02, mask: { w: 255, h: 168, x: -16, y: -10 } },
  // ... 每个场景的 mask 数据各不相同
];

// JSX 中通过 inline style 传入精确偏移
<View className={styles.sceneIconWrap}>
  <Image
    className={styles.sceneIcon}
    src={scene.icon}
    mode="aspectFill"
    style={{
      width: `${scene.mask.w}rpx`,
      height: `${scene.mask.h}rpx`,
      left: `${scene.mask.x}rpx`,
      top: `${scene.mask.y}rpx`,
    }}
  />
</View>
```

```less
// 蒙版容器：圆形裁剪区域
.sceneIconWrap {
  position: relative;
  width: 124rpx;    // 蒙版尺寸 × 2 (rpx)
  height: 124rpx;
  border-radius: 50%;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.05); // 可选：图片加载前的占位色
}

// 内容图片：absolute 定位 + 精确偏移
.sceneIcon {
  position: absolute;
  // width/height/left/top 由 inline style 传入，每张图不同
}
```

### 蒙版数据换算规则

DSL 中的尺寸和偏移为**设计稿像素（px）**，转为 rpx 时 **×2**：

```
DSL: width=129.9, height=67.9, relativeX=-2.95, relativeY=-2.95
rpx: w=260,       h=136,       x=-6,            y=-6
```

### 关键注意事项

1. **每张图片的蒙版数据各不相同**：必须从 DSL 中逐一提取每个蒙版 GROUP 内的 LAYER 尺寸和偏移，**禁止使用通用居中值**（如 `left: (容器宽-图片宽)/2`），否则会导致图片位置偏移。
2. **Image mode 必须使用 `aspectFill`**：保持图片宽高比并裁剪填满，不使用 `scaleToFill`（会拉伸变形）。
3. **选中态处理**：若场景有选中态（如蓝色边框环），需在外层增加一个带 padding 的包裹层，内层仍使用相同的蒙版裁剪结构：

```tsx
<View className={styles.sceneIconWrapActive}>
  <View className={styles.sceneIconClip}>
    <Image className={styles.sceneIcon} src={scene.icon} mode="aspectFill"
      style={{ width: `${m.w}rpx`, height: `${m.h}rpx`, left: `${m.x}rpx`, top: `${m.y}rpx` }}
    />
  </View>
  <View className={styles.editBadge} />
</View>
```

```less
.sceneIconWrapActive {
  position: relative;
  width: 136rpx;
  height: 136rpx;
  border-radius: 50%;
  background-color: #1082FE; // 选中色作为环形边框
  padding: 6rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}
.sceneIconClip {
  position: relative;
  width: 124rpx;
  height: 124rpx;
  border-radius: 50%;
  overflow: hidden;
}
```

---

## 滑块（Slider）实现

### 推荐：smart-ui Slider 组件

当滑块需要交互时（如速度、亮度调节），优先使用 `@ray-js/smart-ui` 的 Slider 组件。深色主题下的配色通过 camelCase API 设置（**非** kebab-case），详见 → [09-smart-ui-dark-theme.md](./09-smart-ui-dark-theme.md#slider-使用指南)。

### 回退：纯 CSS 滑块（仅视觉还原）

当仅需视觉还原（不需交互）时，使用纯 CSS 实现：

```less
.sliderWrap {
  position: relative;
  height: 48rpx;
  display: flex;
  align-items: center;
}
.sliderTrack {
  position: absolute;
  left: 0;
  right: 0;
  height: 24rpx;
  border-radius: 12rpx;
  background: rgba(255, 255, 255, 0.1);
}
.sliderFill {
  position: absolute;
  left: 0;
  width: 53%; /* 根据设计稿填充比例 */
  height: 24rpx;
  border-radius: 12rpx;
  background: #0d84ff;
}
.sliderThumb {
  position: absolute;
  left: 46%; /* 填充比例附近 */
  width: 48rpx;
  height: 48rpx;
  border-radius: 50%;
  background: #ffffff;
  box-shadow: 0 8rpx 12rpx 0 rgba(0, 0, 0, 0.15);
}
```

**要点**：填充比例和圆点位置从 DSL 中轨道与填充条的宽度比计算（`fillWidth / trackWidth`）。

---

## SVG 图标渲染方案

小程序中 SVG 图标有多种渲染方式，按优先级选择：

### 推荐：@ray-js/svg 组件（交互区域内的图标）

当图标需要明确的颜色控制、位于按钮/可点击区域内、或 SVG path 较复杂时，使用 `@ray-js/svg` 包的 `Svg` 组件：

```tsx
import Svg from '@ray-js/svg';

function MyIcon() {
  return (
    <Svg width="16px" height="16px" viewBox="0 0 16 16">
      <path d="M2 3.5A1.5 1.5 0 013.5 2H5.5..." fill="#00CE52" />
    </Svg>
  );
}
```

**安装**：`npm install @ray-js/svg`（若遇 ERESOLVE 冲突，使用 `--legacy-peer-deps`）。

**JSX 注意**：SVG 属性需转为 camelCase —— `fill-rule` → `fillRule`，`clip-rule` → `clipRule`，`stroke-width` → `strokeWidth`。

**适用场景**：
* Button 内嵌图标（替代 `icon` prop 的 CSS mask 方案）
* 可交互区域内的图标
* 需要精确颜色的复杂 SVG

### 回退：CSS data URI（纯装饰图标）

对于纯装饰性的小型图标（≤ 32px、单色、无交互），仍可用 SVG data URI 内联到 CSS `background-image`，详见 → [08-image-assets.md](./08-image-assets.md#5-svg-内联图标小型矢量图标)。

### 避免：smart-ui Button icon prop

smart-ui Button 的 `icon` 属性内部使用 **CSS mask-image** 渲染，所有像素统一填充为组件的 `color` 值。对于包含多色或复杂路径的 SVG，会退化为纯色方块。仅适用于简单的单色几何图标。
