内置节点和自定义节点
一、节点渲染方式
X6 是基于 SVG 的渲染引擎,可以使用不同的 SVG 元素渲染节点和边,非常适合节点内容比较简单的场景。面对复杂的节点, SVG 中有一个特殊的 foreignObject 元素,在该元素中可以内嵌任何 XHTML 元素,可以借助该元素来渲染 HTML、React/Vue/Angular 组件到需要位置,这会给项目开发带来非常大的便利。
- 在选择渲染方式时推荐:
- 如果节点内容比较简单,而且需求比较固定,使用 SVG 节点
- 其他场景,都推荐使用当前项目所使用的框架来渲染节点
注意:
- React/Vue/Angular/HTML 渲染方式也存在一些限制,因为浏览器的兼容性问题,有时会出现一些异常的渲染行为。主要表现形式为节点内容展示不全或者节点内容闪烁。可以通过一些方法规避,比如在节点内部元素的 css 样式中不要使用 position:absolute、position:relative、tranform、opacity。
- 使用 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 版本。
- 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",
},
}
});
以下是效果图
三、定制节点
实际上是从基础节点派生(继承)出我们自己的节点,并覆盖基类的某些选项和方法。
可以通过 markup 和 attrs 来定制节点的形状和样式,markup 可以类比 HTML,attrs 类比 CSS。具体可以仔细阅读 markup 和 attrs 文档。
以 Rect 为例:
- 继承
import { Node } from '@antv/x6'
class Rect extends Node {
......
}
-
配置
调用继承的静态方法 config(options) 来配置节点选项的默认值、自定义选项和自定义属性,最常用选项的是通过 markup 来指定节点默认的 SVG/HTML 结构,通过 attrs 来指定节点的默认属性样式
名称 类型 是否必选 默认值 说明 propHooks Function 否 undefined 自定义选项钩子 attrHooks Object 否 undefined 自定义属性钩子 …others Object 节点选项
Rect.config({
width: 100,
height: 40,
markup: [ // 定义了节点由 <rect> 和 <text> 两个 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
},
})
-
注册
调用 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节点
- 渲染节点
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,
})
- 节点更新
在注册节点的时候,提供一个 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节点