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 都能以统一而强大的方式予以支持,极大地降低了矢量图形编辑的复杂度,为图形工具的开发与使用提供了新的可能性。

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

相关推荐
小信丶10 分钟前
解决 pnpm dev 报错:系统禁止运行脚本的问题
前端·vue.js·windows·npm
૮・ﻌ・17 分钟前
Vue3:组合式API、Vue3.3新特性、Pinia
前端·javascript·vue3
前端不太难18 分钟前
RN + TypeScript 项目越写越乱?如何规范架构?
前端·javascript·typescript
神算大模型APi--天枢64619 分钟前
全栈自主可控:国产算力平台重塑大模型后端开发与部署生态
大数据·前端·人工智能·架构·硬件架构
苏打水com19 分钟前
第十五篇:Day43-45 前端性能优化进阶——从“可用”到“极致”(对标职场“高并发场景优化”需求)
前端·css·vue·html·js
@大迁世界26 分钟前
08.CSS if() 函数
前端·css
Moment33 分钟前
小米不仅造车,还造模型?309B参数全开源,深度思考完胜DeepSeek 🐒🐒🐒
前端·人工智能·后端
苏打水com36 分钟前
第十六篇:Day46-48 前端安全进阶——从“漏洞防范”到“安全体系”(对标职场“攻防实战”需求)
前端·javascript·css·vue.js·html
5C2438 分钟前
从思想到实践:前端工程化体系与 Webpack 构建架构深度解析
前端·前端工程化
咕噜企业分发小米44 分钟前
如何平衡服务器内存使用率和系统稳定性?
java·服务器·前端