Figma Vector Networks: 形状、填充及描边

前言

《Figma Vector Networks: 重新定义矢量图形编辑》一文中,我们分析了Vector Networks (矢量网格)与SVG Path的根本差异。Vector Networks之所以被视为"更高维度"的表述,在于其独特的拓扑结构,这为矢量图形的编辑带来了前所未有的灵活性与效率。

本文将举例说明用Vector Networks表示下面图形的形状、填充及描边。

以上图形分为两类:左边是由直线组成的,右边是由曲线组成的,当然无论矢量图形有多复杂,都可以用Vector Networks表示。

概述

文章以具体图形为例,详细解析了 Vector Networks 的组成结构------包括节点(vertices)线条(segments)区域(regions) ,并展示了如何通过 JSON 结构描述直线、矩形、三角形、五角星、曲线和椭圆等常见图形。

同时,文章还区分了底层矢量引擎与上层工具开发者的职责,强调了 Vector Networks 在简化矢量图形编辑复杂度方面的重要作用。

化繁为简

Vector Networks主要由点、线、和区域组成。

对底层矢量引擎开发者来说,需要将Vector Networks这种描述协议转换成GPU可识别的渲染命令,而解析的核心功能是根据节点和线条自动识别区域。

对上层矢量工具开发者来说,不需要关心如何解析Vector Networks,只要关注矢量图形的编辑交互功能。

这样划分的好处是:底层矢量引擎将任意矢量图形抽象成Vector Networks,并自动识别出了矢量网格中的区域,暴露出矢量网格的点线编辑,大大简化了矢量图形编辑的复杂度。

公共部分

  • type 是图形类型
  • strokeWeight 描边线宽
  • strokeAlign 描边对齐方式,分为:Center、Inside、Outside
  • strokePaints 主要是描边颜色数组
  • fillPaints 主要是填充颜色数组
  • vectorNetwork存储的是矢量网格数据,包含节点数组、线条数组、区域数组
    • vertices是节点数组,每个节点有自己的坐标和样式索引styleID (默认对应到strokePaints数组下标)
    • segments是线条数组,每个线条有起始节点的索引vertex,和对应的控制点dx/dy(曲线会用到),结束点的索引vertex,和对应的控制点dx/dy,以及样式索引styleID
    • regions是区域数组,包含多个区域。
      • 每个区域的styleID (填充样式索引) 定义了当前区域的填充,默认对应到fillPaints数组下标,也可以对应到styleOverrideTable样式覆盖表里
      • 每个区域的loops (区域) 定义了当前区域的拓扑结构,由多个segments (环) 组成,如果只有一个segments则代表当前区域由一个环围城,多个segments一般代表由内环和外环共同围城的区域

直线

javascript 复制代码
const line = {
    "type": "VECTOR", // 图形类型是矢量网格
    "strokeWeight": 1, // 描边宽度为1个单位
    "strokeAlign": "CENTER", // 描边对齐方式为居中
    "strokeJoin": "MITER", // 描边连接方式为尖角连接
    "strokePaints": [
        {
            "type": "SOLID",
            "color": { // 描边颜色
                "r": 0,
                "g": 0,
                "b": 0,
                "a": 1
            },
            "opacity": 1,
            "visible": true,
            "blendMode": "NORMAL"
        }
    ],
    "vectorNetwork": { // 矢量网络数据
        "vertices": [ // 顶点数组
            {
                "styleID": 0, // 顶点样式ID,对应描边样式数组的第一个样式
                "x": 0,
                "y": 1
            },
            {
                "styleID": 0,
                "x": 100,
                "y": 0
            }
        ],
        "segments": [ // 线条数组
            {
                "styleID": 0, // 线条样式ID,对应描边样式数组的第一个样式
                "start": {
                    "vertex": 0,
                    "dx": 0,
                    "dy": 0
                },
                "end": {
                    "vertex": 1,
                    "dx": 0,
                    "dy": 0
                }
            }
        ],
        "regions": [] // 区域数组为空,表示没有可填充区域
    }
};
export default line;

矩形

javascript 复制代码
const rectangle = {
    "type": "ROUNDED_RECTANGLE",
    "strokeWeight": 1,
    "strokeAlign": "INSIDE",
    "strokeJoin": "MITER",
    "fillPaints": [
        {
            "type": "SOLID",
            "color": { // 填充颜色
                "r": 0.8509804010391235,
                "g": 0.8509804010391235,
                "b": 0.8509804010391235,
                "a": 1
            },
            "opacity": 1,
            "visible": true,
            "blendMode": "NORMAL"
        }
    ],
    "strokePaints": [
        {
            "type": "SOLID",
            "color": { // 描边颜色
                "r": 0,
                "g": 0,
                "b": 0,
                "a": 1
            },
            "opacity": 1,
            "visible": true,
            "blendMode": "NORMAL"
        }
    ],
    "vectorNetwork": {
        "vertices": [
            {
                "styleID": 0,
                "x": 0,
                "y": 0
            },
            {
                "styleID": 0,
                "x": 100,
                "y": 0
            },
            {
                "styleID": 0,
                "x": 100,
                "y": 50
            },
            {
                "styleID": 0,
                "x": 0,
                "y": 50
            }
        ],
        "segments": [
            {
                "styleID": 0,
                "start": {
                    "vertex": 0,
                    "dx": 0,
                    "dy": 0
                },
                "end": {
                    "vertex": 1,
                    "dx": 0,
                    "dy": 0
                }
            },
            {
                "styleID": 0,
                "start": {
                    "vertex": 1,
                    "dx": 0,
                    "dy": 0
                },
                "end": {
                    "vertex": 2,
                    "dx": 0,
                    "dy": 0
                }
            },
            {
                "styleID": 0,
                "start": {
                    "vertex": 2,
                    "dx": 0,
                    "dy": 0
                },
                "end": {
                    "vertex": 3,
                    "dx": 0,
                    "dy": 0
                }
            },
            {
                "styleID": 0,
                "start": {
                    "vertex": 3,
                    "dx": 0,
                    "dy": 0
                },
                "end": {
                    "vertex": 0,
                    "dx": 0,
                    "dy": 0
                }
            }
        ],
        "regions": [ // 区域数组里有一个区域
            {
                "styleID": 0,
                "windingRule": "NONZERO",
                "loops": [ // 当前区域由一个环围城
                    {
                        "segments": [ // 当前环由边 0/1/2/3 四条边围城
                            0,
                            1,
                            2,
                            3
                        ]
                    }
                ]
            }
        ]
    }
};
export default rectangle;

三角形

javascript 复制代码
const polygon = {
    "type": "REGULAR_POLYGON",
    "strokeWeight": 1,
    "strokeAlign": "INSIDE",
    "strokeJoin": "MITER",
    "fillPaints": [
        {
            "type": "SOLID",
            "color": {
                "r": 0.8509804010391235,
                "g": 0.8509804010391235,
                "b": 0.8509804010391235,
                "a": 1
            },
            "opacity": 1,
            "visible": true,
            "blendMode": "NORMAL"
        }
    ],
    "strokePaints": [
        {
            "type": "SOLID",
            "color": {
                "r": 0,
                "g": 0,
                "b": 0,
                "a": 1
            },
            "opacity": 1,
            "visible": true,
            "blendMode": "NORMAL"
        }
    ],
    "vectorNetwork": {
        "vertices": [
            {
                "styleID": 0,
                "x": 50,
                "y": 0
            },
            {
                "styleID": 0,
                "x": 93.30126953125,
                "y": 75
            },
            {
                "styleID": 0,
                "x": 6.69873046875,
                "y": 75
            }
        ],
        "segments": [
            {
                "styleID": 0,
                "start": {
                    "vertex": 0,
                    "dx": 0,
                    "dy": 0
                },
                "end": {
                    "vertex": 1,
                    "dx": 0,
                    "dy": 0
                }
            },
            {
                "styleID": 0,
                "start": {
                    "vertex": 1,
                    "dx": 0,
                    "dy": 0
                },
                "end": {
                    "vertex": 2,
                    "dx": 0,
                    "dy": 0
                }
            },
            {
                "styleID": 0,
                "start": {
                    "vertex": 2,
                    "dx": 0,
                    "dy": 0
                },
                "end": {
                    "vertex": 0,
                    "dx": 0,
                    "dy": 0
                }
            }
        ],
        "regions": [
            {
                "styleID": 0,
                "windingRule": "NONZERO",
                "loops": [
                    {
                        "segments": [ // 当前环由三个边围成
                            0,
                            1,
                            2
                        ]
                    }
                ]
            }
        ]
    }
};
export default polygon;

五角星

javascript 复制代码
const star = {
    "type": "STAR",
    "strokeWeight": 1,
    "strokeAlign": "INSIDE",
    "strokeJoin": "MITER",
    "fillPaints": [
        {
            "type": "SOLID",
            "color": {
                "r": 0.8509804010391235,
                "g": 0.8509804010391235,
                "b": 0.8509804010391235,
                "a": 1
            },
            "opacity": 1,
            "visible": true,
            "blendMode": "NORMAL"
        }
    ],
    "strokePaints": [
        {
            "type": "SOLID",
            "color": {
                "r": 0,
                "g": 0,
                "b": 0,
                "a": 1
            },
            "opacity": 1,
            "visible": true,
            "blendMode": "NORMAL"
        }
    ],
    "vectorNetwork": {
        "vertices": [
            {
                "styleID": 0,
                "x": 50,
                "y": 0
            },
            {
                "styleID": 0,
                "x": 61.22570037841797,
                "y": 34.54914855957031
            },
            {
                "styleID": 0,
                "x": 97.55282592773438,
                "y": 34.54914855957031
            },
            {
                "styleID": 0,
                "x": 68.16356658935547,
                "y": 55.90169906616211
            },
            {
                "styleID": 0,
                "x": 79.38926696777344,
                "y": 90.45085144042969
            },
            {
                "styleID": 0,
                "x": 50,
                "y": 69.09829711914062
            },
            {
                "styleID": 0,
                "x": 20.610736846923828,
                "y": 90.45085144042969
            },
            {
                "styleID": 0,
                "x": 31.836435317993164,
                "y": 55.90169906616211
            },
            {
                "styleID": 0,
                "x": 2.447174072265625,
                "y": 34.54914855957031
            },
            {
                "styleID": 0,
                "x": 38.77429962158203,
                "y": 34.54914855957031
            }
        ],
        "segments": [
            {
                "styleID": 0,
                "start": {
                    "vertex": 0,
                    "dx": 0,
                    "dy": 0
                },
                "end": {
                    "vertex": 1,
                    "dx": 0,
                    "dy": 0
                }
            },
            {
                "styleID": 0,
                "start": {
                    "vertex": 1,
                    "dx": 0,
                    "dy": 0
                },
                "end": {
                    "vertex": 2,
                    "dx": 0,
                    "dy": 0
                }
            },
            {
                "styleID": 0,
                "start": {
                    "vertex": 2,
                    "dx": 0,
                    "dy": 0
                },
                "end": {
                    "vertex": 3,
                    "dx": 0,
                    "dy": 0
                }
            },
            {
                "styleID": 0,
                "start": {
                    "vertex": 3,
                    "dx": 0,
                    "dy": 0
                },
                "end": {
                    "vertex": 4,
                    "dx": 0,
                    "dy": 0
                }
            },
            {
                "styleID": 0,
                "start": {
                    "vertex": 4,
                    "dx": 0,
                    "dy": 0
                },
                "end": {
                    "vertex": 5,
                    "dx": 0,
                    "dy": 0
                }
            },
            {
                "styleID": 0,
                "start": {
                    "vertex": 5,
                    "dx": 0,
                    "dy": 0
                },
                "end": {
                    "vertex": 6,
                    "dx": 0,
                    "dy": 0
                }
            },
            {
                "styleID": 0,
                "start": {
                    "vertex": 6,
                    "dx": 0,
                    "dy": 0
                },
                "end": {
                    "vertex": 7,
                    "dx": 0,
                    "dy": 0
                }
            },
            {
                "styleID": 0,
                "start": {
                    "vertex": 7,
                    "dx": 0,
                    "dy": 0
                },
                "end": {
                    "vertex": 8,
                    "dx": 0,
                    "dy": 0
                }
            },
            {
                "styleID": 0,
                "start": {
                    "vertex": 8,
                    "dx": 0,
                    "dy": 0
                },
                "end": {
                    "vertex": 9,
                    "dx": 0,
                    "dy": 0
                }
            },
            {
                "styleID": 0,
                "start": {
                    "vertex": 9,
                    "dx": 0,
                    "dy": 0
                },
                "end": {
                    "vertex": 0,
                    "dx": 0,
                    "dy": 0
                }
            }
        ],
        "regions": [
            {
                "styleID": 0,
                "windingRule": "NONZERO",
                "loops": [
                    {
                        "segments": [ 当前环由十条边围城
                            0,
                            1,
                            2,
                            3,
                            4,
                            5,
                            6,
                            7,
                            8,
                            9
                        ]
                    }
                ]
            }
        ]
    }
};
export default star;

曲线

javascript 复制代码
const line2 = {
    "type": "VECTOR",
    "strokeWeight": 1,
    "strokeAlign": "CENTER",
    "strokeJoin": "MITER",
    "strokePaints": [
        {
            "type": "SOLID",
            "color": {
                "r": 0,
                "g": 0,
                "b": 0,
                "a": 1
            },
            "opacity": 1,
            "visible": true,
            "blendMode": "NORMAL"
        }
    ],
    "vectorNetwork": {
        "vertices": [
            {
                "styleID": 0,
                "x": 0,
                "y": 23.836986541748047
            },
            {
                "styleID": 0,
                "x": 100,
                "y": 22.8122615814209
            },
            {
                "styleID": 1,
                "x": 50,
                "y": 23.324623107910156
            }
        ],
        "segments": [
            {
                "styleID": 0,
                "start": {
                    "vertex": 0,
                    "dx": 19.28675651550293, // 起始点的控制点x坐标(相对于当前点偏移量)
                    "dy": -29.784029006958008 // 起始点的控制点y坐标(相对于当前点偏移量)
                },
                "end": {
                    "vertex": 2,
                    "dx": -22.682817459106445, // 结束点的控制点x坐标(相对于当前点偏移量)
                    "dy": -33.02580642700195 // 结束点的控制点y坐标(相对于当前点偏移量)
                }
            },
            {
                "styleID": 0,
                "start": {
                    "vertex": 2,
                    "dx": 22.682817459106445,
                    "dy": 33.02580642700195
                },
                "end": {
                    "vertex": 1,
                    "dx": -14.171252250671387,
                    "dy": 28.461177825927734
                }
            }
        ],
        "regions": []
    }
};
export default line2;

椭圆

javascript 复制代码
const ellipse = {
    "type": "ELLIPSE",
    "strokeWeight": 1,
    "strokeAlign": "INSIDE",
    "strokeJoin": "MITER",
    "fillPaints": [
        {
            "type": "SOLID",
            "color": {
                "r": 0.8509804010391235,
                "g": 0.8509804010391235,
                "b": 0.8509804010391235,
                "a": 1
            },
            "opacity": 1,
            "visible": true,
            "blendMode": "NORMAL"
        }
    ],
    "strokePaints": [
        {
            "type": "SOLID",
            "color": {
                "r": 0,
                "g": 0,
                "b": 0,
                "a": 1
            },
            "opacity": 1,
            "visible": true,
            "blendMode": "NORMAL"
        }
    ],
    "vectorNetwork": {
        "vertices": [
            {
                "styleID": 0,
                "x": 100,
                "y": 25
            },
            {
                "styleID": 0,
                "x": 50,
                "y": 50
            },
            {
                "styleID": 0,
                "x": 0,
                "y": 25
            },
            {
                "styleID": 0,
                "x": 50,
                "y": 0
            }
        ],
        "segments": [
            {
                "styleID": 0,
                "start": {
                    "vertex": 0,
                    "dx": 0,
                    "dy": 13.80711841583252
                },
                "end": {
                    "vertex": 1,
                    "dx": 27.61423683166504,
                    "dy": 0
                }
            },
            {
                "styleID": 0,
                "start": {
                    "vertex": 1,
                    "dx": -27.61423683166504,
                    "dy": 0
                },
                "end": {
                    "vertex": 2,
                    "dx": 0,
                    "dy": 13.80711841583252
                }
            },
            {
                "styleID": 0,
                "start": {
                    "vertex": 2,
                    "dx": 0,
                    "dy": -13.80711841583252
                },
                "end": {
                    "vertex": 3,
                    "dx": -27.61423683166504,
                    "dy": 0
                }
            },
            {
                "styleID": 0,
                "start": {
                    "vertex": 3,
                    "dx": 27.61423683166504,
                    "dy": 0
                },
                "end": {
                    "vertex": 0,
                    "dx": 0,
                    "dy": -13.80711841583252
                }
            }
        ],
        "regions": [
            {
                "styleID": 0,
                "windingRule": "NONZERO",
                "loops": [
                    {
                        "segments": [ // 当前环由四条曲线组成
                            0,
                            1,
                            2,
                            3
                        ]
                    }
                ]
            }
        ]
    }
};
export default ellipse;

总结

Figma Vector Networks 提供了一种更高维度的矢量图形描述方式 ,它不仅突破了传统 SVG Path 的编辑限制,还通过其独特的拓扑结构 实现了更灵活、高效的图形构建与渲染。本文通过多个实际图形示例,系统演示了如何使用 Vector Networks 的结构化数据(如 verticessegmentsregions)来定义图形的几何形状、描边样式与填充区域。无论是直线、多边形还是曲线图形,Vector Networks 都能以统一而强大的方式予以支持,极大地降低了矢量图形编辑的复杂度,为图形工具的开发与使用提供了新的可能性。

更多精彩内容可关注 风起的博客 ,微信公众号:听风说图

相关推荐
冻感糕人~4 分钟前
【珍藏必备】ReAct框架实战指南:从零开始构建AI智能体,让大模型学会思考与行动
java·前端·人工智能·react.js·大模型·就业·大模型学习
程序员agions8 分钟前
2026年,“配置工程师“终于死绝了
前端·程序人生
alice--小文子14 分钟前
cursor-mcp工具使用
java·服务器·前端
晚霞的不甘23 分钟前
揭秘 CANN 内存管理:如何让大模型在小设备上“轻装上阵”?
前端·数据库·经验分享·flutter·3d
小迷糊的学习记录32 分钟前
0.1 + 0.2 不等于 0.3
前端·javascript·面试
梦帮科技1 小时前
Node.js配置生成器CLI工具开发实战
前端·人工智能·windows·前端框架·node.js·json
VT.馒头2 小时前
【力扣】2695. 包装数组
前端·javascript·算法·leetcode·职场和发展·typescript
css趣多多2 小时前
一个UI内置组件el-scrollbar
前端·javascript·vue.js
C澒2 小时前
前端整洁架构(Clean Architecture)实战解析:从理论到 Todo 项目落地
前端·架构·系统架构·前端框架
C澒2 小时前
Remesh 框架详解:基于 CQRS 的前端领域驱动设计方案
前端·架构·前端框架·状态模式