antv/x6_2.0学习使用(三、内置节点和自定义节点)

内置节点和自定义节点

一、节点渲染方式

X6 是基于 SVG 的渲染引擎,可以使用不同的 SVG 元素渲染节点和边,非常适合节点内容比较简单的场景。面对复杂的节点, SVG 中有一个特殊的 foreignObject 元素,在该元素中可以内嵌任何 XHTML 元素,可以借助该元素来渲染 HTML、React/Vue/Angular 组件到需要位置,这会给项目开发带来非常大的便利。

  • 在选择渲染方式时推荐:
    • 如果节点内容比较简单,而且需求比较固定,使用 SVG 节点
    • 其他场景,都推荐使用当前项目所使用的框架来渲染节点

注意:

  1. React/Vue/Angular/HTML 渲染方式也存在一些限制,因为浏览器的兼容性问题,有时会出现一些异常的渲染行为。主要表现形式为节点内容展示不全或者节点内容闪烁。可以通过一些方法规避,比如在节点内部元素的 css 样式中不要使用 position:absolute、position:relative、tranform、opacity。
  2. 使用 React/Vue/Angular 渲染时,x6 的版本要和 x6-react-shape/x6-vue-shape/x6-angular-shape 的版本匹配,也就是两个包需要用同一个大版本。比如如果 X6 使用的是 2.x 版本,那么 x6-react-shape/x6-vue-shape/x6-angular-shape 也需要使用 2.x 版本。
  3. x6-react-shape 从 2.0.8 版本以后只支持 react18,如果项目是低于 react18 的版本,就要把 x6-react-shape 的版本锁定在 2.0.8。
  • 节点和边都有共同的基类 Cell,除了从 Cell 继承属性外,还支持以下选项。
属性名 类型 默认值 描述
x number 0 节点位置 x 坐标,单位为 px
y number 0 节点位置 y 坐标,单位为 px
width number 1 节点宽度,单位为 px
height number 1 节点高度,单位为 px
angle number 0 节点旋转角度
graph.addNode({
  shape: 'rect',
  x: 100,
  y: 40,
  width: 100,
  height: 40,
})
二、内置节点

使用 shape 指定节点的图形,shape 的默认值为 rect。X6 内置节点与 shape 名称对应关系如下表

shape 名称 描述
rect 矩形
circle 圆形
ellipse 椭圆
polygon 多边形
polyline 折线
path 路径
image 图片
html HTML 节点,使用 foreignObject 渲染 HTML 片段。
// rect
graph.addNode({
    shape: "rect",
    x: 40,
    y: 40,
    width: 80,
    height: 40,
    label: "rect",
});

// circle
graph.addNode({
    shape: "circle",
    x: 180,
    y: 40,
    width: 40,
    height: 40,
    label: "circle",
});

// ellipse
graph.addNode({
    shape: "ellipse",
    x: 280,
    y: 40,
    width: 80,
    height: 40,
    label: "ellipse",
});

// path
graph.addNode({
    shape: "path",
    x: 420,
    y: 40,
    width: 40,
    height: 40,
    // https://www.svgrepo.com/svg/13653/like
    path: "M24.85,10.126c2.018-4.783,6.628-8.125,11.99-8.125c7.223,0,12.425,6.179,13.079,13.543c0,0,0.353,1.828-0.424,5.119c-1.058,4.482-3.545,8.464-6.898,11.503L24.85,48L7.402,32.165c-3.353-3.038-5.84-7.021-6.898-11.503c-0.777-3.291-0.424-5.119-0.424-5.119C0.734,8.179,5.936,2,13.159,2C18.522,2,22.832,5.343,24.85,10.126z",
    label: "path",
});

// polygon
graph.addNode({
    shape: "polygon",
    x: 60,
    y: 150,
    width: 40,
    height: 40,
    points: "100,10 40,198 190,78 10,78 160,198",
    label: "polygon",
    attrs: {
        body: {
            fill: "#fff",
            stroke: "#8f8f8f",
            strokeWidth: 1,
        },
        label: {
            refX: 0.5,
            refY: "100%",
            refY2: 4,
            textAnchor: "middle",
            textVerticalAnchor: "top",
        },
    },
});

// polyline
graph.addNode({
    shape: "polyline",
    x: 180,
    y: 150,
    width: 40,
    height: 40,
    label: "polyline",
    attrs: {
        body: {
            refPoints: "0,0 0,10 10,10 10,0",
        },
        label: {
            refX: 0.5,
            refY: "100%",
            refY2: 4,
            textAnchor: "middle",
            textVerticalAnchor: "top",
        },
    },
});

// image
graph.addNode({
    shape: "image",
    x: 290,
    y: 150,
    width: 60,
    height: 40,
    imageUrl:
        "https://gw.alipayobjects.com/os/s/prod/antv/assets/image/logo-with-text-73b8a.svg",
    label: "image",
    attrs: {
        label: {
            refX: 0.5,
            refY: "100%",
            refY2: 4,
            textAnchor: "middle",
            textVerticalAnchor: "top",
        },
    }
});

以下是效果图
antv/x6_2.0学习使用(三、内置节点和自定义节点)_第1张图片

三、定制节点

实际上是从基础节点派生(继承)出我们自己的节点,并覆盖基类的某些选项和方法。
可以通过 markup 和 attrs 来定制节点的形状和样式,markup 可以类比 HTML,attrs 类比 CSS。具体可以仔细阅读 markup 和 attrs 文档。

以 Rect 为例:

  1. 继承
import { Node } from '@antv/x6'

class Rect extends Node {
  ......
}
  1. 配置

    调用继承的静态方法 config(options) 来配置节点选项的默认值、自定义选项和自定义属性,最常用选项的是通过 markup 来指定节点默认的 SVG/HTML 结构,通过 attrs 来指定节点的默认属性样式

    名称 类型 是否必选 默认值 说明
    propHooks Function undefined 自定义选项钩子
    attrHooks Object undefined 自定义属性钩子
    …others Object 节点选项
Rect.config({
  width: 100,
  height: 40,
  markup: [  //  定义了节点由  两个 SVG 元素构成,并分别指定了 body 和 label 两个选择器,接着就可以在 attrs 中通过这两个选择器来指定节点的默认样式。
    {
      tagName: 'rect',
      selector: 'body',
    },
    {
      tagName: 'text',
      selector: 'label',
    },
  ],
  attrs: {
    body: {
      fill: '#ffffff',
      stroke: '#333333',
      strokeWidth: 2,
    },
    label: {
      fontSize: 14,
      fill: '#333333',
      refX: '50%',
      refY: '50%',
      textAnchor: 'middle',
      textVerticalAnchor: 'middle',
    },
  },
  // 通过钩子将自定义选项 label 应用到 'attrs/text/text' 属性上
  propHooks(metadata) {
    const { label, ...others } = metadata
    if (label) {
      ObjectExt.setByPath(others, 'attrs/text/text', label)
    }
    return others
  },
})
  1. 注册

    调用 Graph 的静态方法 registerNode 来注册节点,注册以后就可以像使用内置节点那样来使用节点。

Graph.registerNode(name: string, cls: typeof Node, overwrite?: boolean)
参数名 类型 是否必选 默认值 说明
name String 注册的节点名
cls typeof Node 节点类,直接或间接继承 Node 的类
overwrite Boolean false 重名时是否覆盖,默认为 false 不覆盖(重名时报错)

例如,注册名为 ‘rect’ 的节点。

Graph.registerNode('rect', Rect)

注册以后,可以像下面这样来使用。

graph.addNode({
  shape: 'rect',
  x: 30,
  y: 40,
})
  • 便捷方法:

同时实现定义和注册节点。

Graph.registerNode(name: string, options: Object, overwrite?: boolean)
参数名 类型 是否必选 默认值 说明
name String 注册的节点名
options Object 选项
overwrite Boolean false 重名时是否覆盖,默认为 false 不覆盖(重名时报错)

通过 options.inherit 来指定继承的基类,默认值为 Node 类,支持字符串或节点类,当 options.inherit 为字符串时将自动从已注册的节点中找到对应的节点作为基类,options 的其他选项与 define 方法一致。当 options.constructorName 类名缺省时,第一个参数 name 的大驼峰形式(CamelCase)也将作为自定义节点的类名。

const ports = {
  groups: {
    top: {
      position: "top",
      attrs: {
        circle: {
          r: 4,
          magnet: true,
          stroke: "#5F95FF",
          strokeWidth: 1,
          fill: "#fff",
          style: {
            visibility: "hidden",
          },
        },
      },
    },
    right: {
      position: "right",
      attrs: {
        circle: {
          r: 4,
          magnet: true,
          stroke: "#5F95FF",
          strokeWidth: 1,
          fill: "#fff",
          style: {
            visibility: "hidden",
          },
        },
      },
    },
    bottom: {
      position: "bottom",
      attrs: {
        circle: {
          r: 4,
          magnet: true,
          stroke: "#5F95FF",
          strokeWidth: 1,
          fill: "#fff",
          style: {
            visibility: "hidden",
          },
        },
      },
    },
    left: {
      position: "left",
      attrs: {
        circle: {
          r: 4,
          magnet: true,
          stroke: "#5F95FF",
          strokeWidth: 1,
          fill: "#fff",
          style: {
            visibility: "hidden",
          },
        },
      },
    },
  },
  items: [
    {
      group: "top",
    },
    {
      group: "right",
    },
    {
      group: "bottom",
    },
    {
      group: "left",
    },
  ],
};

Graph.registerNode(
  "custom-rect",
  {
    inherit: "rect",
    width: 66,
    height: 36,
    attrs: {
      body: {
        strokeWidth: 1,
        stroke: "#5F95FF",
        fill: "#EFF4FF",
      },
      text: {
        fontSize: 12,
        fill: "#262626",
      },
    },
    ports: { ...ports },
  },
  true
);

Graph.registerNode(
  "custom-polygon",
  {
    inherit: "polygon",
    width: 66,
    height: 36,
    attrs: {
      body: {
        strokeWidth: 1,
        stroke: "#5F95FF",
        fill: "#EFF4FF",
      },
      text: {
        fontSize: 12,
        fill: "#262626",
      },
    },
    ports: {
      ...ports,
      items: [
        {
          group: "top",
        },
        {
          group: "bottom",
        },
      ],
    },
  },
  true
);

Graph.registerNode(
  "custom-circle",
  {
    inherit: "circle",
    width: 45,
    height: 45,
    attrs: {
      body: {
        strokeWidth: 1,
        stroke: "#5F95FF",
        fill: "#EFF4FF",
      },
      text: {
        fontSize: 12,
        fill: "#262626",
      },
    },
    ports: { ...ports },
  },
  true
);

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
);

除此方法外还可以使用另外一种静态方法 define 来自定义节点,具体可查看自定义节点

四、自定义HTML节点
  1. 渲染节点

X6 默认内置 HTML 渲染能力

import {  Shape } from '@antv/x6'

Shape.HTML.register({
  shape: 'custom-html',
  width: 160,
  height: 80,
  html() {
    const div = document.createElement('div')
    div.className = 'custom-html'
    return div
  },
})

graph.addNode({
  shape: 'custom-html',
  x: 60,
  y: 100,
})
  1. 节点更新

在注册节点的时候,提供一个 effect,字段,是当前节点的 prop 数组,当 effect 包含的 prop 有变动时,会重新执行 html 方法,返回新的 dom,更新节点内容。

Shape.HTML.register({
  shape: 'custom-html',
  width: 160,
  height: 80,
  effect: ['data'],
  html(cell) {
    const { color } = cell.getData()
    const div = document.createElement('div')
    div.className = 'custom-html'
    div.style.background = color
    return div
  },
})
五、 自定义React/Vue/Angular节点

可阅读官方文档 自定义React节点、自定义Vue节点、自定义Angular节点

你可能感兴趣的:(antv/x6,学习,前端,typescript,vue.js,antv/x6)