Skip to main content
Glama
1yhy
by 1yhy
layout-detection-research.md30.5 kB
# 设计稿转代码布局检测研究 ## 概述 本文档提供了设计稿转代码(D2C, Design-to-Code)转换工具中使用的布局检测算法的全面分析。研究重点关注各种实现如何区分流式布局(flex)、堆叠布局(absolute)和网格布局(grid)。 ## 目录 1. [问题定义](#问题定义) 2. [业界实现](#业界实现) 3. [学术研究](#学术研究) 4. [算法对比](#算法对比) 5. [检测标准与阈值](#检测标准与阈值) 6. [重叠检测](#重叠检测) 7. [实现建议](#实现建议) --- ## 问题定义 ### 核心挑战 像 Figma 这样的设计工具将元素位置存储为绝对坐标(x, y, width, height)。将这些转换为语义化的 CSS 布局需要推断设计师的意图: | 布局类型 | CSS 属性 | 使用场景 | | ----------------- | --------------------------------------- | -------------------------- | | **流式(横向)** | `display: flex; flex-direction: row` | 具有一致间距的行内元素 | | **流式(纵向)** | `display: flex; flex-direction: column` | 垂直堆叠的元素 | | **网格** | `display: grid` | 对齐元素的二维矩阵 | | **堆叠/绝对定位** | `position: absolute` | 重叠元素、装饰性定位的元素 | ### 需要回答的关键问题 1. 如何检测元素是否形成**行**还是**列**? 2. 何时应该使用**Grid**而不是**嵌套的 Flex**? 3. 如何识别需要绝对定位的**重叠/堆叠**元素? 4. 什么样的**容差阈值**最适合现实世界的设计? --- ## 业界实现 ### 1. imgcook (阿里巴巴 D2C 平台) **架构**: 确定性优先的流水线,带有机器学习回退 ``` 设计 JSON → 扁平化 → 行/列分组 → 布局推断 → 语义标签 → 代码 ``` **流式布局 vs 堆叠布局检测算法**: ```typescript // 核心概念: 轴重叠决定布局类型 function detectLayoutDirection(elements: Element[]): 'row' | 'column' | 'stacked' { // 步骤 1: 检查 Y 轴重叠(同一水平行) const yOverlapGroups = groupByAxisOverlap(elements, 'y', tolerance: 2); // 步骤 2: 如果只有一个 Y 组,检查 X 轴重叠(垂直列) if (yOverlapGroups.length === 1) { const xOverlapGroups = groupByAxisOverlap(elements, 'x', tolerance: 2); if (xOverlapGroups.length > 1) return 'column'; } // 步骤 3: 检查重叠元素 const hasOverlap = elements.some((a, i) => elements.slice(i + 1).some(b => calculateIoU(a, b) > 0.1) ); return hasOverlap ? 'stacked' : 'row'; } ``` **分组算法**: ```typescript function groupByAxisOverlap(elements: Element[], axis: "x" | "y", tolerance: number): Element[][] { // 按轴位置排序 const sorted = elements.sort((a, b) => a[axis] - b[axis]); const groups: Element[][] = [[sorted[0]]]; for (let i = 1; i < sorted.length; i++) { const current = sorted[i]; const lastGroup = groups[groups.length - 1]; const lastElement = lastGroup[lastGroup.length - 1]; // 检查当前元素是否在相反轴上与最后一个元素重叠 const overlaps = axis === "y" ? rangesOverlap(lastElement.y, lastElement.bottom, current.y, current.bottom, tolerance) : rangesOverlap(lastElement.x, lastElement.right, current.x, current.right, tolerance); if (overlaps) { lastGroup.push(current); } else { groups.push([current]); } } return groups; } ``` **关键阈值**: | 参数 | 值 | 用途 | | ------------ | -------- | ------------------ | | Y 轴容差 | 2px | 行分组 | | X 轴容差 | 2px | 列分组 | | 间距方差 | 20% | 检测一致的间距 | | 对齐容差 | 2px | 检测对齐的边缘 | | IoU 重叠阈值 | 0.1 | 标记为绝对定位 | | 最大递归深度 | 5 | 防止无限嵌套 | | 间距四舍五入 | 4px 网格 | 对齐到常见的设计值 | **置信度评分**: ```typescript interface LayoutConfidence { patternCoverage: number; // 符合模式的元素数量 gapConsistency: number; // 间距的标准差 alignmentAccuracy: number; // 元素对齐程度 } function calculateConfidence(analysis: LayoutAnalysis): number { let score = 0; score += analysis.patternCoverage * 0.4; // 40% 权重 score += analysis.gapConsistency * 0.35; // 35% 权重 score += analysis.alignmentAccuracy * 0.25; // 25% 权重 return score; } // 仅当置信度 >= 0.3 时才接受布局 const MIN_CONFIDENCE = 0.3; ``` --- ### 2. FigmaToCode (开源) **方法**: 元数据优先(信任 Figma 自动布局) ```typescript // FigmaToCode 不从坐标推断布局 // 相反,它读取 Figma 的原生自动布局元数据 function convertLayout(node: FigmaNode): CSSLayout { if (node.layoutMode === "HORIZONTAL") { return { display: "flex", flexDirection: "row" }; } if (node.layoutMode === "VERTICAL") { return { display: "flex", flexDirection: "column" }; } // 无自动布局 = 回退到绝对定位 return { position: "absolute" }; } ``` **局限性**: - 要求设计师使用 Figma 自动布局功能 - 没有自动布局的旧设计会获得绝对定位 - 没有基于坐标的布局推断 **关键创新**: AltNodes 中间表示 - 将 Figma 节点转换为简化的虚拟节点 - 处理混合定位(绝对定位 + 自动布局子元素) - 对代码结构做出智能决策 --- ### 3. Locofy **方法**: 自动布局 + 绝对定位混合 Locofy 使用 LocoAI 分析设计并应用适当的 CSS 属性: ```typescript // Locofy 的布局检测方法 function analyzeLayout(elements: Element[]): LayoutDecision { // LocoAI 将元素分组以获得更好的结构 const groups = locoAI.groupElements(elements); // 应用相关的 CSS 属性(flex)以实现响应式 for (const group of groups) { if (group.hasAutoLayout) { // 自动布局对应 CSS 中的 Flexbox applyFlexLayout(group); } else if (group.isFloating) { // 浮动元素使用绝对定位 applyAbsolutePosition(group); } } } ``` **关键特性**: - 更改绝对位置状态并重新运行算法以重新分组 - 浮动元素在自动布局设置中使用 absolute 属性 - 绝对定位元素的父元素决定定位上下文 --- ### 4. Anima Auto-Flexbox **方法**: 自动 Flexbox 的计算机视觉算法 ```typescript // Anima 的 Auto-Flexbox 算法 // 从开发者思考过程逆向工程 function applyAutoFlexbox(design: Design): Layout { // 没有 Auto-Flexbox: 生成绝对布局 // 使用 Auto-Flexbox: 生成相对定位(Flexbox) // 来自计算机视觉领域的 CV 算法 const flexboxLayout = cvAlgorithm.analyzeAndApply(design); // 相对定位允许层相互推动 return flexboxLayout; } ``` **关键见解**: "绝对布局对于设计阶段很好,但对于最终产品则不太理想。Flexbox 布局意味着相对定位。" --- ### 5. Gridaco / Grida **方法**: 规则 + 机器学习混合(类似于 imgcook) ```typescript // 通过启发式实现高可用性布局方向检测 // 机器学习保留用于组件/循环识别 function detectLayout(elements: Element[]): LayoutType { // 首先基于规则的方向检测 const direction = detectDirectionByRules(elements); // 机器学习用于语义理解 const semantics = mlModel.predictSemantics(elements); return combineResults(direction, semantics); } ``` --- ### 6. Phoenix Codie 位置检测系统 **关键创新**: 明确避免"position: absolute 滥用" ```typescript interface PositionDecision { element: Element; shouldBeAbsolute: boolean; reason: "overlap" | "decorative" | "anchor" | "flow"; } function analyzePosition(element: Element, siblings: Element[]): PositionDecision { // 仅在以下情况使用绝对定位: // 1. 重叠元素 // 2. 装饰性元素(徽章、自由定位的图标) // 3. 锚点元素(工具提示、模态框) const overlaps = siblings.some((s) => hasSignificantOverlap(element, s)); const isDecorative = element.type === "DECORATIVE"; const isAnchor = element.hasAnchorConstraint; return { element, shouldBeAbsolute: overlaps || isDecorative || isAnchor, reason: overlaps ? "overlap" : isDecorative ? "decorative" : isAnchor ? "anchor" : "flow", }; } ``` --- ## 学术研究 ### 1. Allen 区间代数(基础理论) **来源**: "A Layout Inference Algorithm for GUIs" (ScienceDirect) Allen 的 13 个区间关系描述了一维空间关系: ``` | 关系 | 可视化 | 描述 | |----------|--------|----------------------| | before | A---B | A 完全在 B 之前 | | meets | A--B | A 的结束处是 B 的开始处 | | overlaps | A-B- | A 部分重叠 B | | starts | AB-- | A 与 B 在同一位置开始 | | during | -A-B | A 完全在 B 内部 | | equals | A=B | 相同的位置和大小 | ``` **在布局检测中的应用**: ```typescript function getAllenRelation(a: Interval, b: Interval): AllenRelation { if (a.end < b.start) return "before"; if (a.end === b.start) return "meets"; if (a.start < b.start && a.end > b.start && a.end < b.end) return "overlaps"; if (a.start === b.start && a.end < b.end) return "starts"; if (a.start > b.start && a.end < b.end) return "during"; if (a.start === b.start && a.end === b.end) return "equals"; // ... 反向关系 } // 对于 2D 布局,将 Allen 关系应用于 X 和 Y 轴 function get2DRelation(a: Rect, b: Rect): [AllenRelation, AllenRelation] { return [ getAllenRelation({ start: a.x, end: a.right }, { start: b.x, end: b.right }), getAllenRelation({ start: a.y, end: a.bottom }, { start: b.y, end: b.bottom }), ]; } ``` **两阶段算法**: 1. **阶段 1**: 使用有向图将绝对坐标转换为相对定位 2. **阶段 2**: 应用模式匹配和图重写进行布局组合 **结果**: 97% 的布局保真度,84% 的调整大小时比例保持 --- ### 2. GRIDS - 基于 MILP 的布局推断(Aalto 大学) **方法**: 网格生成的数学优化 ```python # 将网格推断制定为混合整数线性规划 def solve_grid_layout(elements): model = gp.Model("grid_layout") # 变量: 轨道大小、元素放置 track_widths = model.addVars(max_columns, lb=0, name="col_width") track_heights = model.addVars(max_rows, lb=0, name="row_height") placements = model.addVars(len(elements), max_rows, max_columns, vtype=GRB.BINARY) # 约束: 元素必须适合轨道,无重叠 for i, elem in enumerate(elements): # 元素宽度必须等于跨越的轨道宽度之和 model.addConstr(sum(track_widths[c] * placements[i,r,c] for r in range(max_rows) for c in range(max_columns)) >= elem.width) # 目标: 最小化与原始位置的偏差 model.setObjective(sum_position_errors, GRB.MINIMIZE) model.optimize() return extract_grid_template(model) ``` **优势**: - 数学上最优的解决方案 - 处理复杂的网格配置 - 精确的轨道大小计算 **劣势**: - 计算成本高 - 需要 Gurobi 优化器 - 对于简单布局过于复杂 --- ### 3. UI 语义组检测(arXiv 2024) **关键创新**: 基于格式塔的预聚类 ```typescript // 在布局检测之前应用格式塔原则 interface GestaltAnalysis { proximityGroups: Element[][]; // 相近元素分组 similarityGroups: Element[][]; // 相似外观元素分组 continuityChains: Element[][]; // 形成视觉线的元素 closureGroups: Element[][]; // 形成封闭形状的元素 } function applyGestaltPrinciples(elements: Element[]): GestaltAnalysis { return { proximityGroups: clusterByProximity(elements, distanceThreshold: 20), similarityGroups: clusterBySimilarity(elements, sizeVariance: 0.2), continuityChains: detectVisualLines(elements), closureGroups: detectEnclosures(elements) }; } // 使用格式塔分组改进网格检测 function detectGridWithGestalt(elements: Element[]): GridResult { const gestalt = applyGestaltPrinciples(elements); // 仅考虑相似性分组进行网格检测 for (const group of gestalt.similarityGroups) { if (group.length >= 4) { const gridResult = detectGridLayout(group); if (gridResult.confidence >= 0.6) { return gridResult; } } } return { isGrid: false }; } ``` --- ### 4. Screen Parsing (CMU UIST 2021) **方法**: 基于机器学习的容器类型预测 - 在 210K 移动屏幕上训练(130K iOS + 80K Android) - 预测包括网格在内的 7 种容器类型 - 使用视觉特征和空间关系 ```typescript interface ContainerPrediction { type: "list" | "grid" | "carousel" | "tabs" | "form" | "navigation" | "content"; confidence: number; children: Element[]; } // 机器学习模型输入特征 interface ScreenFeatures { elementBoundingBoxes: Rect[]; elementTypes: string[]; visualSimilarities: number[][]; // 成对视觉相似度 spatialRelations: AllenRelation[][]; // 成对空间关系 } ``` --- ## 算法对比 ### 检测方法总结 | 工具/研究 | 方法 | 流式布局检测 | 网格布局检测 | 重叠处理 | | ------------------ | ------------- | ------------------- | -------------- | -------------------- | | **imgcook** | 基于规则 + ML | Y 轴重叠分组 | 行/列对齐检查 | IoU > 0.1 → 绝对定位 | | **FigmaToCode** | 元数据优先 | 读取 Figma 自动布局 | 无(无推断) | N/A | | **Grida** | 规则 + ML | 类似于 imgcook | 类似于 imgcook | 类似于 imgcook | | **Phoenix Codie** | 基于规则 | 位置分析 | N/A | 明确的重叠检查 | | **Allen 算法** | 基于图 | 区间关系 | 两阶段推断 | 包含/重叠关系 | | **GRIDS** | MILP 优化 | N/A | 数学优化 | 基于约束 | | **Screen Parsing** | 基于 ML | ML 预测 | ML 预测 | ML 预测 | ### 优势和劣势 | 方法 | 优势 | 劣势 | | -------------- | -------------------- | ------------------------ | | **基于规则** | 可预测、可解释、快速 | 灵活性有限、需要手动调整 | | **元数据优先** | 元数据存在时准确 | 没有设计师配合时失败 | | **基于 ML** | 处理复杂模式 | 需要训练数据、黑盒 | | **MILP** | 数学上最优 | 计算成本高 | | **混合** | 两全其美 | 实现复杂 | --- ## 检测标准与阈值 ### 通用阈值(业界共识) ```typescript const LAYOUT_THRESHOLDS = { // 轴重叠容差 ROW_GROUPING_TOLERANCE: 2, // px - 同一行的 Y 轴重叠 COLUMN_GROUPING_TOLERANCE: 2, // px - 同一列的 X 轴重叠 // 间距分析 GAP_VARIANCE_THRESHOLD: 0.2, // 20% - 最大变异系数 GAP_ROUNDING_GRID: 4, // px - 将间距对齐到倍数 // 对齐 ALIGNMENT_TOLERANCE: 2, // px - 边缘对齐检测 // 大小同质性 SIZE_CV_THRESHOLD: 0.2, // 20% - 网格的最大尺寸方差 // 重叠检测 IOU_OVERLAP_THRESHOLD: 0.1, // 10% - 标记为重叠 IOU_SIGNIFICANT_OVERLAP: 0.5, // 50% - 确定重叠 // 置信度阈值 MIN_FLOW_CONFIDENCE: 0.3, // 接受流式布局的最小值 MIN_GRID_CONFIDENCE: 0.6, // 接受网格布局的最小值 // 元素数量 MIN_GRID_ELEMENTS: 4, // 网格检测的最小值 MIN_GRID_ROWS: 2, // 网格的最小行数 MIN_GRID_COLUMNS: 2, // 网格的最小列数 }; ``` ### 间距四舍五入值 ```typescript // 常见设计系统间距值 const COMMON_GAP_VALUES = [0, 4, 8, 12, 16, 20, 24, 32, 40, 48, 64]; function roundGapToCommon(gap: number): number { return COMMON_GAP_VALUES.reduce((prev, curr) => Math.abs(curr - gap) < Math.abs(prev - gap) ? curr : prev, ); } ``` --- ## 重叠检测 ### IoU (交并比) 计算 ```typescript interface Rect { x: number; y: number; width: number; height: number; } function calculateIoU(a: Rect, b: Rect): number { // 计算交集 const xOverlap = Math.max(0, Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x)); const yOverlap = Math.max(0, Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y)); const intersection = xOverlap * yOverlap; // 计算并集 const areaA = a.width * a.height; const areaB = b.width * b.height; const union = areaA + areaB - intersection; return union > 0 ? intersection / union : 0; } ``` ### 重叠分类 ```typescript type OverlapType = "none" | "adjacent" | "partial" | "significant" | "contained"; function classifyOverlap(a: Rect, b: Rect): OverlapType { const iou = calculateIoU(a, b); if (iou === 0) { // 检查是否相邻(接触但不重叠) const gap = calculateGap(a, b); return gap <= 2 ? "adjacent" : "none"; } if (iou < 0.1) return "partial"; if (iou < 0.5) return "significant"; return "contained"; } function shouldUseAbsolutePosition(element: Rect, siblings: Rect[]): boolean { for (const sibling of siblings) { const overlap = classifyOverlap(element, sibling); if (overlap === "partial" || overlap === "significant" || overlap === "contained") { return true; } } return false; } ``` ### Z-Index 推断 ```typescript // 当元素重叠时,从以下推断 z 顺序: // 1. Figma 图层顺序(后来的 = 在顶部) // 2. 大小(较小的元素通常在顶部) // 3. 类型(文本/图标通常在背景之上) function inferZIndex(elements: Element[]): Map<Element, number> { const zIndexMap = new Map<Element, number>(); // 按图层顺序排序(Figma 提供此信息) const sorted = [...elements].sort((a, b) => a.layerOrder - b.layerOrder); sorted.forEach((el, index) => { zIndexMap.set(el, index); }); return zIndexMap; } ``` --- ## 实现建议 ### 针对本项目 (Figma-Context-MCP) 基于研究,这里是推荐的算法: #### 1. 预过滤: 同质性检查(已实现) ```typescript // 已在 detector.ts 中实现 export function filterHomogeneousForGrid( rects: ElementRect[], nodeTypes?: string[], ): ElementRect[] { const homogeneity = analyzeHomogeneity(rects, nodeTypes, 0.2); return homogeneity.homogeneousElements; } ``` #### 2. 重叠检测(待实现) ```typescript function detectOverlappingElements(nodes: SimplifiedNode[]): { flowElements: SimplifiedNode[]; stackedElements: SimplifiedNode[]; } { const rects = nodes.map(nodeToRect); const flowElements: SimplifiedNode[] = []; const stackedElements: SimplifiedNode[] = []; for (let i = 0; i < nodes.length; i++) { const hasOverlap = rects.some((other, j) => i !== j && calculateIoU(rects[i], other) > 0.1); if (hasOverlap) { stackedElements.push(nodes[i]); } else { flowElements.push(nodes[i]); } } return { flowElements, stackedElements }; } ``` #### 3. 子元素样式清理(待实现) ```typescript function cleanChildStylesForFlexParent(child: SimplifiedNode): void { if (child.cssStyles) { // 当父元素是 flex/grid 时删除 position: absolute if (child.cssStyles.position === "absolute") { delete child.cssStyles.position; } // 删除 left/top(现在由 flex/grid 处理) delete child.cssStyles.left; delete child.cssStyles.top; // 为 flex 项保留 width/height(由 flex-basis 使用) } } function cleanChildStylesForGridParent(child: SimplifiedNode): void { if (child.cssStyles) { delete child.cssStyles.position; delete child.cssStyles.left; delete child.cssStyles.top; // 如果网格轨道定义了大小,width/height 可能是多余的 // 但保留它们以进行显式大小调整 } } ``` #### 4. 默认值删除(待实现) ```typescript const CSS_DEFAULT_VALUES: Record<string, string[]> = { fontWeight: ["400", "normal"], textAlign: ["left", "start"], flexDirection: ["row"], position: ["static"], opacity: ["1"], backgroundColor: ["transparent", "rgba(0,0,0,0)"], borderWidth: ["0", "0px"], }; function removeDefaultValues(cssStyles: CSSStyle): CSSStyle { const cleaned: CSSStyle = {}; for (const [key, value] of Object.entries(cssStyles)) { const defaults = CSS_DEFAULT_VALUES[key]; if (defaults && defaults.includes(String(value))) { continue; // 跳过默认值 } cleaned[key] = value; } return cleaned; } ``` ### 完整检测流水线 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 布局检测流水线 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. 输入: 具有绝对坐标的子元素数组 │ │ ↓ │ │ 2. 重叠检查: 分离重叠元素 │ │ - IoU > 0.1 → stackedElements(保持绝对定位) │ │ - IoU ≤ 0.1 → flowElements(继续分析) │ │ ↓ │ │ 3. 同质性检查: 过滤相似元素 │ │ - 尺寸 CV < 20% 且相同类型 → 同质 │ │ - 混合尺寸/类型 → 异质 │ │ ↓ │ │ 4. 行分组: Y 轴重叠(2px 容差) │ │ - 1 行 → 水平 flex │ │ - 多行 → 继续 │ │ ↓ │ │ 5. 网格检查(仅同质): │ │ - rows ≥ 2 且 columns ≥ 2 │ │ - 列对齐 ≥ 80% │ │ - 置信度 ≥ 0.6 │ │ → 是: display: grid │ │ → 否: 继续 │ │ ↓ │ │ 6. FLEX 方向: │ │ - 按元素数量确定主要方向 │ │ - 如果大多数元素水平则为 row │ │ - 如果大多数元素垂直则为 column │ │ ↓ │ │ 7. 子元素清理: │ │ - 删除 position: absolute │ │ - 删除 left/top │ │ - 删除默认 CSS 值 │ │ ↓ │ │ 8. 输出: 带有干净子元素样式的 LayoutInfo │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## 参考文献 ### 学术论文 1. **"A Layout Inference Algorithm for Graphical User Interfaces"** (2015) - [ScienceDirect](https://www.sciencedirect.com/science/article/abs/pii/S0950584915001718) - [ResearchGate PDF](https://www.researchgate.net/publication/283526120_A_layout_inference_algorithm_for_Graphical_User_Interfaces) - 关键: Allen 区间代数,97% 布局保真度 2. **"GRIDS: Interactive Layout Design with Integer Programming"** (CHI 2020) - [项目页面](https://userinterfaces.aalto.fi/grids/) - [GitHub](https://github.com/aalto-ui/GRIDS) - [论文 PDF](https://acris.aalto.fi/ws/portalfiles/portal/40720569/CHI2020_Dayama_GRIDS.pdf) - 作者: Niraj Dayama, Kashyap Todi, Taru Saarelainen, Antti Oulasvirta (Aalto 大学) 3. **"Screen Parsing: Towards Reverse Engineering of UI Models from Screenshots"** (UIST 2021) - [CMU ML 博客](https://blog.ml.cmu.edu/2021/12/10/understanding-user-interfaces-with-screen-parsing/) - [论文 PDF](https://www.cs.cmu.edu/~jbigham/pubs/pdfs/2021/screen-parsing.pdf) - [ACM 数字图书馆](https://dl.acm.org/doi/fullHtml/10.1145/3472749.3474763) - 作者: Jason Wu, Xiaoyi Zhang, Jeff Nichols, Jeffrey P. Bigham 4. **"UI Semantic Group Detection"** (arXiv 2024) - arXiv:2403.04984v1 5. **"UIHASH: Grid-Based UI Similarity"** - Jun Zeng et al. ### 开源项目 1. [FigmaToCode](https://github.com/bernaferrari/FigmaToCode) - 在 HTML、Tailwind、Flutter、SwiftUI 上生成响应式页面 2. [GRIDS](https://github.com/aalto-ui/GRIDS) - 基于 MILP 的网格布局生成(Python + Gurobi) 3. [Grida](https://github.com/gridaco/grida) - 设计稿转代码平台 4. [Yoga](https://github.com/facebook/yoga) - Facebook 的跨平台 Flexbox 布局引擎 ### 业界资源 1. **imgcook (阿里巴巴)** - [布局算法博客](https://www.alibabacloud.com/blog/imgcook-3-0-series-layout-algorithm-design-based-code-generation_597856) - [imgcook 工作原理](https://medium.com/imgcook/imgcook-how-are-codes-generated-intelligently-from-design-files-in-alibaba-98ba8e55246d) - [100% 准确率](https://www.alibabacloud.com/blog/imgcook-intelligent-code-generation-from-design-drafts-with-a-100%25-accuracy-rate_598093) 2. **Locofy** - [设计优化器文档](https://www.locofy.ai/docs/lightning/design-optimiser/) - [自动布局到响应式代码](https://www.locofy.ai/docs/classic/design-structure/responsiveness/auto-layout/) 3. **Anima** - [Auto-Flexbox 介绍](https://www.animaapp.com/blog/design-to-code/introducing-auto-flexbox/) - [从约束生成 Flexbox](https://www.animaapp.com/blog/product-updates/producing-flexbox-responsive-code-based-on-figma-adobe-xd-and-sketch-constraints/) 4. **CSS 标准** - [CSS Grid Layout Module Level 1](https://www.w3.org/TR/css-grid-1/) - W3C - [CSS Flexible Box Layout](https://www.w3.org/TR/css-flexbox-1/) - W3C - [理解布局算法](https://www.joshwcomeau.com/css/understanding-layout-algorithms/) - Josh W. Comeau 5. **基础** - [Allen 区间代数](https://en.wikipedia.org/wiki/Allen's_interval_algebra) - Wikipedia - [Figma Grid 自动布局](https://help.figma.com/hc/en-us/articles/31289469907863) - Figma 帮助 - [IoU 解释](https://www.v7labs.com/blog/intersection-over-union-guide) - V7 Labs --- ## 附录: 代码示例 ### A. 完整流式布局检测 ```typescript export function detectFlowLayout(elements: Element[]): LayoutInfo { // 步骤 1: 检查重叠 const { flowElements, stackedElements } = detectOverlappingElements(elements); if (flowElements.length < 2) { return { type: "absolute", elements: stackedElements }; } // 步骤 2: 分组成行 const rows = groupIntoRows(flowElements, THRESHOLDS.ROW_GROUPING_TOLERANCE); // 步骤 3: 确定方向 if (rows.length === 1) { // 单行 = 水平 flex const gapAnalysis = analyzeGaps(rows[0], "horizontal"); return { type: "flex", direction: "row", gap: gapAnalysis.averageGap, alignment: detectAlignment(rows[0], "vertical"), justifyContent: detectJustifyContent(rows[0], "horizontal"), }; } // 步骤 4: 检查网格(多行) const gridResult = detectGridLayout(flowElements); if (gridResult.isGrid && gridResult.confidence >= 0.6) { return { type: "grid", columns: gridResult.columnCount, rows: gridResult.rowCount, columnGap: gridResult.columnGap, rowGap: gridResult.rowGap, trackWidths: gridResult.trackWidths, }; } // 步骤 5: 回退到列 flex const rowGapAnalysis = analyzeGaps( rows.map((r) => r[0]), "vertical", ); return { type: "flex", direction: "column", gap: rowGapAnalysis.averageGap, alignment: detectAlignment(rows.flat(), "horizontal"), }; } ``` ### B. 完整网格布局检测 ```typescript export function detectGridLayout(elements: ElementRect[]): GridAnalysisResult { // 步骤 1: 分组成行 const rows = groupIntoRows(elements, 2); if (rows.length < 2) { return { isGrid: false, confidence: 0 }; } // 步骤 2: 检查列数一致性 const columnCounts = rows.map((r) => r.length); const countVariance = coefficientOfVariation(columnCounts); if (countVariance > 0.2) { return { isGrid: false, confidence: 0 }; } // 步骤 3: 提取列位置 const columnPositions = extractColumnPositions(rows); const alignmentResult = checkColumnAlignment(columnPositions, 4); // 步骤 4: 计算间距 const columnGaps = calculateColumnGaps(rows); const rowGaps = calculateRowGaps(rows); // 步骤 5: 计算置信度 let confidence = 0; if (rows.length >= 2) confidence += 0.2; if (rows.length >= 3) confidence += 0.1; if (countVariance < 0.1) confidence += 0.2; if (alignmentResult.isAligned) confidence += 0.25; if (coefficientOfVariation(columnGaps) < 0.2) confidence += 0.1; if (coefficientOfVariation(rowGaps) < 0.2) confidence += 0.1; // 步骤 6: 计算轨道大小 const trackWidths = calculateTrackWidths(rows, alignmentResult.alignedPositions); const trackHeights = rows.map((row) => Math.max(...row.map((el) => el.height))); return { isGrid: confidence >= 0.6, confidence, rowCount: rows.length, columnCount: Math.max(...columnCounts), rowGap: roundGapToCommon(average(rowGaps)), columnGap: roundGapToCommon(average(columnGaps)), trackWidths, trackHeights, }; } ```

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/1yhy/Figma-Context-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server