基于AntV的X6-Stencil实现流程图svg自定义图标库

3,804 阅读5分钟

1. 前言

使用X6最常用的场景,或者说最基础的场景,就是官网给出的第一个demo案例——流程图。 X6官网图表示例

image.png

左侧的这个侧边栏提供基础的UI组件,通过拖拽这些组件,可以在右侧的画布上进行绘制。该功能是通过插件 @antv/x6-plugin-stencil来实现的。 官网上提供了完整的案例代码。这里,改写下用cdn方式导入资源包,将ts改为js写法。最终要实现的效果如下图所示: image.png

2.熟悉X6插件之Stencil

2.1 认识Stencil

从上图可以看到,在系统设计图的group下,有6个节点,该节点是携带了label文字和图标两个元素。在拓展左侧工具面板的功能之前,先要了解下x6-plugin-stencil的基础功能。已经使用过该插件或者了解过该功能的可以跳转到添加svg图标

2.2 创建分组

左侧面板中基本流程图和系统流程图通过分组区分开。如下代码所示:

const stencil = new Stencil({
    title: '流程图',
    target: graph,
    stencilGraphWidth: 200,
    stencilGraphHeight: 180,
    collapsable: true,
    groups: [
        {
            title: '基础流程图',
            name: 'group1',
        },
        {
            title: '系统设计图',
            name: 'group2',
            graphHeight: 250,
            layoutOptions: {
                rowHeight: 70,
            },
        },
    ],
    layoutOptions: {
        columns: 2,
        columnWidth: 80,
        rowHeight: 55,
    },
})

其中有几个参数的设置注意下:

  1. collapsable: 是否显示全局折叠/展开按钮,默认为false,这里设置为true,显示折叠按钮。(注意:这里并不是默认折叠或展开的开关,而是是否显示按钮的开关。)
  2. groups:分组信息,数组类型,在分组下面定义单个分组的信息,包括展示的标题title,指定的分组名称name,如果分组标题title缺少的时候,会默认用name作为分组标题。
  3. collapsed :初始状态是否为折叠状态,在group内部使用,试了在外部使用没效果。为true表示折叠,false为展开。默认值是false,即展开。
  4. columns:网格布局的列数,默认为 2。行数根据节点数自动计算。
  5. columnWidth:列宽。
  6. rowHeight:行高。

分组内的一些配置和外层的配置有重合,比如 layoutOptions,分组内的配置优先级比较高。

2.3 注册节点

调用 Graph 的静态方法 registerNode 来注册节点,注册以后就可以像使用内置节点那样来使用节点。这个方法在1.34.6的官网文档中有说明,在2.x文档中搜索不到该方法,但是使用2.x版本的X6,也是可以调用registerNode方法的。1.x版本的自定义节点-官网(疑惑!!为啥最新的文档上就找不到了呢?)

Graph.registerNode(
    'custom-image',
    {
        inherit: 'rect',
        width: 52,
        height: 52,
        markup: [
            {
                tagName: 'rect',
                selector: 'body',
            },
            {
                tagName: 'image',
            },
            {
                tagName: 'text',
                selector: 'label',
            },
        ],
        attrs: {
            body: {
                stroke: '#5F95FF',
                fill: '#5F95FF',
            },
            image: {
                width: 26,
                height: 26,
                refX: 13,
                refY: 16,
            },
            label: {
                refX: 3,
                refY: 2,
                textAnchor: 'left',
                textVerticalAnchor: 'top',
                fontSize: 12,
                fill: '#fff',
            },
        },
        ports: { ...ports },
    },
    true,
)

这里自定义了一个名为 'custom-image' 的节点。里面的参数的含义就不一一写出来了,字面意就很明显。

2.4 创建节点

注册以后,我们可以像下面这样来创建。

   graph.createNode({
        shape: 'custom-image',//已经注册好的自定义节点
        label: "Client",
        attrs: {
         image: {
                'xlink:href': 'https://gw.alipayobjects.com/zos/bmw-prod/687b6cb9-4b97-42a6-96d0-34b3099133ac.svg',
            },
            body: {
                strokeWidth: 1,
                stroke: '#5F95FF',
                fill: '#5F95FF',
            },
        },
    }),

案例中通过SVG图标链接的方式来展示。存在多个svg的时候,将label和image提取到数组中。

const imageShapes = [
    {
        label: 'Client',
        image:
            'https://gw.alipayobjects.com/zos/bmw-prod/687b6cb9-4b97-42a6-96d0-34b3099133ac.svg',
    },
    {
        label: 'Http',
        image:
            'https://gw.alipayobjects.com/zos/bmw-prod/dc1ced06-417d-466f-927b-b4a4d3265791.svg',
    },
    {
        label: 'Api',
        image:
            'https://gw.alipayobjects.com/zos/bmw-prod/c55d7ae1-8d20-4585-bd8f-ca23653a4489.svg',
    },
    {
        label: 'Sql',
        image:
            'https://gw.alipayobjects.com/zos/bmw-prod/6eb71764-18ed-4149-b868-53ad1542c405.svg',
    },
    {
        label: 'Clound',
        image:
            'https://gw.alipayobjects.com/zos/bmw-prod/c36fe7cb-dc24-4854-aeb5-88d8dc36d52e.svg',
    },
    {
        label: 'Mq',
        image:
            'https://gw.alipayobjects.com/zos/bmw-prod/2010ac9f-40e7-49d4-8c4a-4fcf2f83033b.svg',
    },
]
const imageNodes = imageShapes.map((item) =>
    graph.createNode({
        shape: 'custom-image',
        label: item.label,
        attrs: {
            image: {
                'xlink:href': item.image,
            },
            body: {
                strokeWidth: 1,
                stroke: '#5F95FF',
                fill: '#5F95FF',
            },
        },
    }),
)

2.5 加载节点

最后,将节点进行加载,绑定到对应的分组上去。

stencil.load(imageNodes,'group2')

3. 添加svg图标

3.1 加载本地svg格式文件

是否可以基于这个改写下,去掉label文字描述,去掉背景元素,更改图标的颜色呢?

虽然通过链接的方式可以加载svg图标,达到目的效果,但是我们在查找图标的时候,有时候看到的并不是在线的无鉴权的链接。这里,我们访问【ant design的图标库】,打开控制台,定位到指定的图标。

image.png

可以看到这是一段svg的路径。很多的加载svg图标的都是svg直接加载。

image.png

如果能将复制下来的svg的路径直接使用,就可太方便了,很多的icon都可以通过控制台拿到。

3.2 将svg的数据转为base64

参照demo中上述的注册节点、创建节点和使用节点的方式,来新建一个group,注册一个名为icon-svg的节点。创建如下方法

function svgToDataURL(svgPath) {
    const svgCode = `
    <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" >
      <path d="${svgPath}"></path>
    </svg>
  `;
    const encoded = window.btoa(svgCode);
    return `data:image/svg+xml;base64,${encoded}`;
}

划重点了,将svg的格式图片转为base64,进行加载,这个即为本次文章的中心思想。但是这样直接转过来之后,不能更改颜色了,所以在svg这步,就要将颜色渲染进去。

function svgToDataURL(svgPath,fillColor = "#f00") {
    const svgCode = `
    <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" style="fill:${fillColor}">
      <path d="${svgPath}"></path>
    </svg>
  `;
    const encoded = window.btoa(svgCode);
    return `data:image/svg+xml;base64,${encoded}`;
}

3.3 复制svg路径的时候注意事项

存在很多的svg图标,不止一个path路径,会存在多个。如下:

image.png 这里可以进一步改写下上述的方式。

方式一:<path d="${svgPath}"></path>改为如上图加载的,svg标签下的字内容全部复制。

方式二:将两个路径合并成一个。合并的时候需注意,两段路径之间并不需要标点符号进行区分,就直接拼接在一起就可以了。

八青妹这里采用的方式二。完整代码:

4. 总结

icon图标在流程图中是为了让流程图的显示更加丰满一些。既然官网的案例给了svg链接加载的方式,那么肯定也是支持base64图片加载的,将svg的格式文件转为base64,仿照demo完成图标库的基本搭建。最后就是需要什么图标,在线查找最后复制粘贴进项目代码中就可以啦!