mta-mcp 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents/flutter.agent.md +15 -92
- package/agents/vue3.agent.md +15 -28
- package/agents/wechat-miniprogram.agent.md +23 -0
- package/package.json +1 -1
- package/standards/mcp-tools/sketch-mcp.md +349 -0
- package/standards/syntax-mapping.md +256 -0
- package/standards/workflows/design-restoration.md +363 -0
- package/templates/README.md +14 -0
- package/templates/design-measurement/README.md +81 -0
- package/templates/design-measurement/component-measurement.js +52 -0
- package/templates/design-measurement/gap-measurement.js +79 -0
- package/templates/design-measurement/style-extraction.js +109 -0
package/agents/flutter.agent.md
CHANGED
|
@@ -136,103 +136,26 @@ get_standard_by_id({ ids: ['flutter', 'flutter-ui-system'] })
|
|
|
136
136
|
|
|
137
137
|
---
|
|
138
138
|
|
|
139
|
-
## 🎨
|
|
139
|
+
## 🎨 设计稿还原
|
|
140
140
|
|
|
141
|
-
> ⚠️
|
|
141
|
+
> ⚠️ 详细规范请调用:`get_standard_by_id({ id: 'design-restoration' })`
|
|
142
142
|
|
|
143
|
-
###
|
|
143
|
+
### 快速提示
|
|
144
144
|
|
|
145
|
-
|
|
145
|
+
| 关键点 | 说明 |
|
|
146
|
+
|--------|------|
|
|
147
|
+
| **渐变色** | 检查 `fillType` 后读取 `gradient.stops`,勿直接用 `color` 属性 |
|
|
148
|
+
| **全局样式** | 还原前先 grep 搜索项目现有 Token(颜色、圆角、阴影) |
|
|
149
|
+
| **新拟态阴影** | 双阴影:右下深色 + 左上高光 |
|
|
150
|
+
| **行高** | Flutter `height` 是比例值,非像素(如 24px/16px = 1.5) |
|
|
146
151
|
|
|
147
|
-
|
|
152
|
+
### 相关规范
|
|
148
153
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
# 搜索圆角定义
|
|
155
|
-
grep -r "radius\|Radius\|RADIUS\|borderRadius" lib/ --include="*.dart"
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
**寻找模式**:
|
|
159
|
-
- `DesignRadius` / `AppRadius` / `Radius` 类定义
|
|
160
|
-
- `BorderRadius.circular(数字)` 的常量定义
|
|
161
|
-
- 查看最常用的数值(如 12、16、20)
|
|
162
|
-
|
|
163
|
-
**使用优先级**:
|
|
164
|
-
1. 项目已有的 Radius 类常量(如 `DesignRadius.lg`)
|
|
165
|
-
2. 查看现有组件中使用频率最高的值
|
|
166
|
-
3. 如无规范,则直接使用设计稿数值,待后续统一
|
|
167
|
-
|
|
168
|
-
##### 2. 按钮高度检查
|
|
169
|
-
|
|
170
|
-
```bash
|
|
171
|
-
# 搜索按钮高度定义
|
|
172
|
-
grep -r "ButtonSize\|buttonHeight\|height.*button" lib/ --include="*.dart"
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
**寻找模式**:
|
|
176
|
-
- `DesignButtonSizes` / `AppButtonSizes` 类
|
|
177
|
-
- `Container(height: 常量)` 包裹的 Button
|
|
178
|
-
- 查看主 CTA、标准按钮、小按钮的高度
|
|
179
|
-
|
|
180
|
-
##### 3. 阴影检查
|
|
181
|
-
|
|
182
|
-
```bash
|
|
183
|
-
# 搜索阴影定义
|
|
184
|
-
grep -r "Shadow\|boxShadow\|elevation" lib/ --include="*.dart"
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
**寻找模式**:
|
|
188
|
-
- `DesignShadows` / `AppShadows` 类
|
|
189
|
-
- `BoxShadow` 配置(offset、blur、color)
|
|
190
|
-
- Neumorphism 阴影模式
|
|
191
|
-
|
|
192
|
-
##### 4. 颜色检查
|
|
193
|
-
|
|
194
|
-
```bash
|
|
195
|
-
# 搜索颜色定义
|
|
196
|
-
grep -r "Colors\|AppColors\|DesignColors\|Color(0x" lib/ --include="*.dart"
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
**寻找模式**:
|
|
200
|
-
- 颜色类定义(通常在 `lib/core/themes/` 或 `lib/constants/`)
|
|
201
|
-
- 查看主色、文字色、背景色的命名规范
|
|
202
|
-
- 渐变色定义(`LinearGradient`)
|
|
203
|
-
|
|
204
|
-
#### 通用禁止事项
|
|
205
|
-
|
|
206
|
-
- ❌ **直接硬编码数值** - 应先搜索项目规范
|
|
207
|
-
- ❌ **假设特定类名** - 不同项目命名不同(AppColors vs DesignColors)
|
|
208
|
-
- ❌ **跳过搜索步骤** - 即使觉得"简单"也必须先搜索
|
|
209
|
-
|
|
210
|
-
### 强制要求:完整读取选中元素及其所有子集
|
|
211
|
-
|
|
212
|
-
```javascript
|
|
213
|
-
// 正确:一次性获取容器+所有子元素的完整信息
|
|
214
|
-
function extractComplete(element) {
|
|
215
|
-
// 1. 容器信息
|
|
216
|
-
console.log(`容器: ${element.frame.width}x${element.frame.height}`);
|
|
217
|
-
|
|
218
|
-
// 2. 所有子元素信息(关键!)
|
|
219
|
-
element.layers.forEach(child => {
|
|
220
|
-
console.log(`子元素: ${child.name}`);
|
|
221
|
-
console.log(` 位置: Y=${child.frame.y}px`);
|
|
222
|
-
console.log(` 尺寸: ${child.frame.width}x${child.frame.height}`);
|
|
223
|
-
if (child.type === 'Text') {
|
|
224
|
-
console.log(` 字号: ${child.style.fontSize}px`);
|
|
225
|
-
console.log(` 行高: ${child.frame.height / child.style.fontSize}`);
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
### 禁止事项
|
|
232
|
-
- ❌ 只读容器属性,忽略内部元素位置
|
|
233
|
-
- ❌ 每次只查一个属性,分散多轮获取
|
|
234
|
-
- ❌ 假设"差不多"而不验证精确像素值
|
|
235
|
-
- ❌ 用"试错法"调整参数
|
|
154
|
+
| 规范 | 调用方式 |
|
|
155
|
+
|------|----------|
|
|
156
|
+
| 设计稿还原流程 | `get_standard_by_id({ id: 'design-restoration' })` |
|
|
157
|
+
| Sketch MCP 用法 | `get_standard_by_id({ id: 'sketch-mcp' })` |
|
|
158
|
+
| 跨框架语法映射 | `get_standard_by_id({ id: 'syntax-mapping' })` |
|
|
236
159
|
|
|
237
160
|
---
|
|
238
161
|
|
package/agents/vue3.agent.md
CHANGED
|
@@ -136,39 +136,26 @@ get_standard_by_id({ ids: ['vue3-composition', 'element-plus', 'i18n'] })
|
|
|
136
136
|
|
|
137
137
|
---
|
|
138
138
|
|
|
139
|
-
## 🎨
|
|
139
|
+
## 🎨 设计稿还原
|
|
140
140
|
|
|
141
|
-
> ⚠️
|
|
141
|
+
> ⚠️ 详细规范请调用:`get_standard_by_id({ id: 'design-restoration' })`
|
|
142
142
|
|
|
143
|
-
###
|
|
143
|
+
### 快速提示
|
|
144
144
|
|
|
145
|
-
|
|
145
|
+
| 关键点 | 说明 |
|
|
146
|
+
|--------|------|
|
|
147
|
+
| **渐变色** | 检查 `fillType` 后读取 `gradient.stops`,勿直接用 `color` 属性 |
|
|
148
|
+
| **全局样式** | 还原前先搜索项目现有 CSS 变量 / Token |
|
|
149
|
+
| **渐变语法** | `background: linear-gradient(135deg, #E01020, #B8101A)` |
|
|
150
|
+
| **阴影语法** | `box-shadow: 8px 8px 20px rgba(28,43,69,0.15)` |
|
|
146
151
|
|
|
147
|
-
|
|
152
|
+
### 相关规范
|
|
148
153
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
| 类型 | 常见值 | 应使用 |
|
|
156
|
-
|------|--------|--------|
|
|
157
|
-
| **圆角** | 16px | `$radius.lg` / CSS 变量 `--radius-lg` |
|
|
158
|
-
| **圆角** | 12px | `$radius.base` |
|
|
159
|
-
| **圆角** | 8px | `$radius.xs` |
|
|
160
|
-
| **按钮高度** | 56px | `$button.heightCTA` |
|
|
161
|
-
| **按钮高度** | 40px | `$button.heightMedium` |
|
|
162
|
-
| **按钮高度** | 32px | `$button.heightSmall` |
|
|
163
|
-
| **阴影** | 拟物卡片 | `$shadow.card` |
|
|
164
|
-
| **主色** | 红色CTA | `$color.primary` |
|
|
165
|
-
| **文字色** | 深色 | `$color.textPrimary` |
|
|
166
|
-
|
|
167
|
-
#### 禁止事项
|
|
168
|
-
- ❌ 硬编码 `border-radius: 16px` - 应使用 CSS 变量或常量
|
|
169
|
-
- ❌ 硬编码 `height: 56px` - 应使用按钮尺寸常量
|
|
170
|
-
- ❌ 硬编码 `color: #1C2B45` - 应使用颜色变量
|
|
171
|
-
- ❌ 手写 `box-shadow` - 应使用预设阴影 mixin
|
|
154
|
+
| 规范 | 调用方式 |
|
|
155
|
+
|------|----------|
|
|
156
|
+
| 设计稿还原流程 | `get_standard_by_id({ id: 'design-restoration' })` |
|
|
157
|
+
| Sketch MCP 用法 | `get_standard_by_id({ id: 'sketch-mcp' })` |
|
|
158
|
+
| 跨框架语法映射 | `get_standard_by_id({ id: 'syntax-mapping' })` |
|
|
172
159
|
|
|
173
160
|
---
|
|
174
161
|
|
|
@@ -85,5 +85,28 @@ miniprogram/
|
|
|
85
85
|
|
|
86
86
|
---
|
|
87
87
|
|
|
88
|
+
## 🎨 设计稿还原
|
|
89
|
+
|
|
90
|
+
> ⚠️ 详细规范请调用:`get_standard_by_id({ id: 'design-restoration' })`
|
|
91
|
+
|
|
92
|
+
### 快速提示
|
|
93
|
+
|
|
94
|
+
| 关键点 | 说明 |
|
|
95
|
+
|--------|------|
|
|
96
|
+
| **渐变色** | 检查 `fillType` 后读取 `gradient.stops`,勿直接用 `color` 属性 |
|
|
97
|
+
| **全局样式** | 还原前先搜索项目现有变量(app.wxss 中的 CSS 变量) |
|
|
98
|
+
| **rpx 换算** | 设计稿 px * 2 = rpx(基于 750px 设计稿) |
|
|
99
|
+
| **阴影语法** | `box-shadow: 8px 8px 20px rgba(28,43,69,0.15)` |
|
|
100
|
+
|
|
101
|
+
### 相关规范
|
|
102
|
+
|
|
103
|
+
| 规范 | 调用方式 |
|
|
104
|
+
|------|----------|
|
|
105
|
+
| 设计稿还原流程 | `get_standard_by_id({ id: 'design-restoration' })` |
|
|
106
|
+
| Sketch MCP 用法 | `get_standard_by_id({ id: 'sketch-mcp' })` |
|
|
107
|
+
| 跨框架语法映射 | `get_standard_by_id({ id: 'syntax-mapping' })` |
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
88
111
|
**维护团队**: MTA工作室
|
|
89
112
|
**设计理念**: Agent 只提供获取指引,详细规范由 MCP 工具从 npm 包动态获取
|
package/package.json
CHANGED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
# Sketch MCP 最佳实践
|
|
2
|
+
|
|
3
|
+
> 使用 Sketch MCP 工具准确测量设计稿的规范指南
|
|
4
|
+
|
|
5
|
+
## 📋 概述
|
|
6
|
+
|
|
7
|
+
Sketch MCP 是用于从 Sketch 设计文件中提取 UI 参数的工具。本文档记录了使用过程中发现的问题和最佳实践。
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## ⚠️ 已知问题与解决方案
|
|
12
|
+
|
|
13
|
+
### 问题 1:渐变色被读取为单色
|
|
14
|
+
|
|
15
|
+
**严重程度**: 🔴 高
|
|
16
|
+
|
|
17
|
+
**现象**:
|
|
18
|
+
```javascript
|
|
19
|
+
layer.style.fills[0].color // 返回 "#4a60b2ff"
|
|
20
|
+
layer.style.fills[0].fillType // 返回 "Gradient"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
当 `fillType` 是 `Gradient` 时,`color` 属性返回的是一个"代表色",而非实际的渐变颜色。
|
|
24
|
+
|
|
25
|
+
**解决方案**:
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
function getLayerColor(layer) {
|
|
29
|
+
const fills = layer.style?.fills?.filter(f => f.enabled);
|
|
30
|
+
if (!fills || fills.length === 0) return null;
|
|
31
|
+
|
|
32
|
+
const fill = fills[0];
|
|
33
|
+
|
|
34
|
+
// 关键:先判断 fillType
|
|
35
|
+
if (fill.fillType === 'Gradient' && fill.gradient) {
|
|
36
|
+
const g = fill.gradient;
|
|
37
|
+
return {
|
|
38
|
+
type: 'gradient',
|
|
39
|
+
gradientType: g.gradientType, // Linear | Radial | Angular
|
|
40
|
+
direction: { from: g.from, to: g.to },
|
|
41
|
+
colors: g.stops.map(s => ({
|
|
42
|
+
position: s.position,
|
|
43
|
+
color: s.color
|
|
44
|
+
}))
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
type: 'solid',
|
|
50
|
+
color: fill.color
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 问题 2:Symbol 内部样式读取
|
|
56
|
+
|
|
57
|
+
**严重程度**: 🟡 中
|
|
58
|
+
|
|
59
|
+
**现象**:Symbol 实例的内部图层样式可能需要通过 `expandedLayers` 访问。
|
|
60
|
+
|
|
61
|
+
**解决方案**:
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
if (layer.type === 'SymbolInstance') {
|
|
65
|
+
layer.expandedLayers.forEach(nestedLayer => {
|
|
66
|
+
// 递归处理嵌套图层
|
|
67
|
+
measureLayer(nestedLayer);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 问题 3:阴影数组顺序
|
|
73
|
+
|
|
74
|
+
**严重程度**: 🟢 低
|
|
75
|
+
|
|
76
|
+
**现象**:多重阴影的顺序可能与视觉效果不一致。
|
|
77
|
+
|
|
78
|
+
**解决方案**:记录所有阴影,并按 blur 值或 enabled 状态排序。
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 📐 标准测量脚本
|
|
83
|
+
|
|
84
|
+
### 完整测量函数
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
const sketch = require('sketch');
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 完整的图层测量函数
|
|
91
|
+
* 正确处理渐变色、阴影、圆角等
|
|
92
|
+
*/
|
|
93
|
+
function measureLayerComplete(layer) {
|
|
94
|
+
const result = {
|
|
95
|
+
name: layer.name,
|
|
96
|
+
type: layer.type,
|
|
97
|
+
frame: {
|
|
98
|
+
x: Math.round(layer.frame.x),
|
|
99
|
+
y: Math.round(layer.frame.y),
|
|
100
|
+
width: Math.round(layer.frame.width),
|
|
101
|
+
height: Math.round(layer.frame.height),
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
if (!layer.style) return result;
|
|
106
|
+
|
|
107
|
+
// ========== 填充色 ==========
|
|
108
|
+
const enabledFills = layer.style.fills?.filter(f => f.enabled) || [];
|
|
109
|
+
if (enabledFills.length > 0) {
|
|
110
|
+
result.fills = enabledFills.map(fill => {
|
|
111
|
+
if (fill.fillType === 'Gradient' && fill.gradient) {
|
|
112
|
+
const g = fill.gradient;
|
|
113
|
+
return {
|
|
114
|
+
type: 'gradient',
|
|
115
|
+
gradientType: g.gradientType,
|
|
116
|
+
from: g.from,
|
|
117
|
+
to: g.to,
|
|
118
|
+
stops: g.stops.map(s => ({
|
|
119
|
+
position: s.position,
|
|
120
|
+
color: s.color
|
|
121
|
+
}))
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
type: 'solid',
|
|
126
|
+
color: fill.color
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ========== 边框 ==========
|
|
132
|
+
const enabledBorders = layer.style.borders?.filter(b => b.enabled) || [];
|
|
133
|
+
if (enabledBorders.length > 0) {
|
|
134
|
+
result.borders = enabledBorders.map(b => ({
|
|
135
|
+
color: b.color,
|
|
136
|
+
thickness: b.thickness,
|
|
137
|
+
position: b.position // inside | outside | center
|
|
138
|
+
}));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ========== 阴影 ==========
|
|
142
|
+
const enabledShadows = layer.style.shadows?.filter(s => s.enabled) || [];
|
|
143
|
+
if (enabledShadows.length > 0) {
|
|
144
|
+
result.shadows = enabledShadows.map(s => ({
|
|
145
|
+
color: s.color,
|
|
146
|
+
blur: s.blur,
|
|
147
|
+
x: s.x,
|
|
148
|
+
y: s.y,
|
|
149
|
+
spread: s.spread || 0,
|
|
150
|
+
isInner: s.isInnerShadow || false
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ========== 内阴影 ==========
|
|
155
|
+
const innerShadows = layer.style.innerShadows?.filter(s => s.enabled) || [];
|
|
156
|
+
if (innerShadows.length > 0) {
|
|
157
|
+
result.innerShadows = innerShadows.map(s => ({
|
|
158
|
+
color: s.color,
|
|
159
|
+
blur: s.blur,
|
|
160
|
+
x: s.x,
|
|
161
|
+
y: s.y,
|
|
162
|
+
spread: s.spread || 0
|
|
163
|
+
}));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ========== 圆角 ==========
|
|
167
|
+
if (layer.style.corners) {
|
|
168
|
+
const radii = layer.style.corners.radii;
|
|
169
|
+
// 检查是否四角相同
|
|
170
|
+
if (radii.every(r => r === radii[0])) {
|
|
171
|
+
result.borderRadius = radii[0];
|
|
172
|
+
} else {
|
|
173
|
+
result.borderRadius = radii; // [topLeft, topRight, bottomRight, bottomLeft]
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ========== 文字属性 ==========
|
|
178
|
+
if (layer.type === 'Text') {
|
|
179
|
+
result.text = {
|
|
180
|
+
content: layer.text,
|
|
181
|
+
fontSize: layer.style.fontSize,
|
|
182
|
+
fontFamily: layer.style.fontFamily,
|
|
183
|
+
fontWeight: layer.style.fontWeight,
|
|
184
|
+
textColor: layer.style.textColor,
|
|
185
|
+
alignment: layer.style.alignment,
|
|
186
|
+
lineHeight: layer.style.lineHeight,
|
|
187
|
+
letterSpacing: layer.style.kerning
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// ========== 透明度 ==========
|
|
192
|
+
if (layer.style.opacity !== undefined && layer.style.opacity !== 1) {
|
|
193
|
+
result.opacity = layer.style.opacity;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ========== 模糊效果 ==========
|
|
197
|
+
if (layer.style.blur?.enabled) {
|
|
198
|
+
result.blur = {
|
|
199
|
+
type: layer.style.blur.blurType,
|
|
200
|
+
radius: layer.style.blur.radius
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return result;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* 递归测量整个画板
|
|
209
|
+
*/
|
|
210
|
+
function measureArtboard(artboard) {
|
|
211
|
+
const results = [];
|
|
212
|
+
|
|
213
|
+
function traverse(layer, depth = 0) {
|
|
214
|
+
if (layer.hidden) return;
|
|
215
|
+
|
|
216
|
+
const data = measureLayerComplete(layer);
|
|
217
|
+
data.depth = depth;
|
|
218
|
+
results.push(data);
|
|
219
|
+
|
|
220
|
+
// 递归子图层
|
|
221
|
+
if (layer.layers) {
|
|
222
|
+
layer.layers.forEach(child => traverse(child, depth + 1));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Symbol 展开图层
|
|
226
|
+
if (layer.expandedLayers) {
|
|
227
|
+
layer.expandedLayers.forEach(child => traverse(child, depth + 1));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
traverse(artboard);
|
|
232
|
+
return results;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// 使用示例
|
|
236
|
+
const doc = sketch.getSelectedDocument();
|
|
237
|
+
const artboard = doc.selectedLayers.layers[0];
|
|
238
|
+
const measurements = measureArtboard(artboard);
|
|
239
|
+
console.log(JSON.stringify(measurements, null, 2));
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## 🎨 颜色转换
|
|
245
|
+
|
|
246
|
+
### Sketch 颜色到各框架
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
249
|
+
/**
|
|
250
|
+
* 将 Sketch 颜色转换为各框架格式
|
|
251
|
+
* @param {string} sketchColor - Sketch 颜色格式 "#rrggbbaa"
|
|
252
|
+
* @param {string} framework - 目标框架
|
|
253
|
+
*/
|
|
254
|
+
function convertColor(sketchColor, framework) {
|
|
255
|
+
// Sketch 格式: #rrggbbaa
|
|
256
|
+
const r = parseInt(sketchColor.slice(1, 3), 16);
|
|
257
|
+
const g = parseInt(sketchColor.slice(3, 5), 16);
|
|
258
|
+
const b = parseInt(sketchColor.slice(5, 7), 16);
|
|
259
|
+
const a = parseInt(sketchColor.slice(7, 9), 16) / 255;
|
|
260
|
+
|
|
261
|
+
switch (framework) {
|
|
262
|
+
case 'flutter':
|
|
263
|
+
// Flutter: Color(0xAARRGGBB)
|
|
264
|
+
const flutterHex = sketchColor.slice(7, 9) + sketchColor.slice(1, 7);
|
|
265
|
+
return `Color(0x${flutterHex.toUpperCase()})`;
|
|
266
|
+
|
|
267
|
+
case 'css':
|
|
268
|
+
// CSS: rgba(r, g, b, a)
|
|
269
|
+
if (a === 1) {
|
|
270
|
+
return sketchColor.slice(0, 7);
|
|
271
|
+
}
|
|
272
|
+
return `rgba(${r}, ${g}, ${b}, ${a.toFixed(2)})`;
|
|
273
|
+
|
|
274
|
+
case 'react-native':
|
|
275
|
+
// React Native: '#RRGGBBAA' 或 'rgba(...)'
|
|
276
|
+
if (a === 1) {
|
|
277
|
+
return `'${sketchColor.slice(0, 7)}'`;
|
|
278
|
+
}
|
|
279
|
+
return `'rgba(${r}, ${g}, ${b}, ${a.toFixed(2)})'`;
|
|
280
|
+
|
|
281
|
+
default:
|
|
282
|
+
return sketchColor;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// 渐变转换
|
|
287
|
+
function convertGradient(gradient, framework) {
|
|
288
|
+
const colors = gradient.stops.map(s =>
|
|
289
|
+
convertColor(s.color, framework)
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
switch (framework) {
|
|
293
|
+
case 'flutter':
|
|
294
|
+
return `LinearGradient(
|
|
295
|
+
begin: Alignment(${gradient.from.x * 2 - 1}, ${gradient.from.y * 2 - 1}),
|
|
296
|
+
end: Alignment(${gradient.to.x * 2 - 1}, ${gradient.to.y * 2 - 1}),
|
|
297
|
+
colors: [${colors.join(', ')}],
|
|
298
|
+
)`;
|
|
299
|
+
|
|
300
|
+
case 'css':
|
|
301
|
+
const angle = Math.atan2(
|
|
302
|
+
gradient.to.y - gradient.from.y,
|
|
303
|
+
gradient.to.x - gradient.from.x
|
|
304
|
+
) * 180 / Math.PI + 90;
|
|
305
|
+
return `linear-gradient(${angle}deg, ${colors.join(', ')})`;
|
|
306
|
+
|
|
307
|
+
default:
|
|
308
|
+
return colors;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## ✅ 使用检查清单
|
|
316
|
+
|
|
317
|
+
### 开始测量前
|
|
318
|
+
|
|
319
|
+
- [ ] 确认选中正确的画板/Frame
|
|
320
|
+
- [ ] 检查是否有隐藏图层需要测量
|
|
321
|
+
- [ ] 确认设计稿是最新版本
|
|
322
|
+
|
|
323
|
+
### 测量过程中
|
|
324
|
+
|
|
325
|
+
- [ ] 对每个颜色检查 `fillType` 是否为 Gradient
|
|
326
|
+
- [ ] 阴影检查是否有多重阴影
|
|
327
|
+
- [ ] 圆角检查四角是否相同
|
|
328
|
+
- [ ] 文字检查行高是否有定义
|
|
329
|
+
|
|
330
|
+
### 测量完成后
|
|
331
|
+
|
|
332
|
+
- [ ] 与设计稿视觉对比
|
|
333
|
+
- [ ] 渐变色验证(最容易出错)
|
|
334
|
+
- [ ] 透明度验证
|
|
335
|
+
- [ ] 响应式适配验证
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## 📚 相关资源
|
|
340
|
+
|
|
341
|
+
- [Sketch JavaScript API 文档](https://developer.sketch.com/reference/api/)
|
|
342
|
+
- [设计稿还原规范](../workflows/design-restoration.md)
|
|
343
|
+
- [颜色系统规范](../color-system.md)
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
**维护者**: MTA工作室
|
|
348
|
+
**创建日期**: 2026-01-20
|
|
349
|
+
**最后更新**: 2026-01-20
|