弄清楚了桩的配置信息,可以开始着手学习shape的内容了。
shape是博客里的名称,官网的名称是model,有三种类型:Cell、Node、Edge。
Cell
官网内容:
Cell 是 Node 和 Edge 的基类,包含节点和边的通用属性和方法定义,如属性样式、可见性、业务数据等,并且在实例化、样式定制、默认选项、自定义选项等方面具有相同的行为。
可以知道的是,cell是最基础的节点,cell的各项配置node和edge都可以修改。
构造函数
选项 | 类型 | 默认值 | 必选 | 描述 |
---|---|---|---|---|
id | string | 节点/边的唯一标识,推荐使用具备业务意义的 ID,默认使用自动生成的 UUID。 | ||
markup | Markup | 节点/边的 SVG/HTML 片段。 | ||
attrs | Attr.CellAttrs | 节点/边属性样式。 | ||
shape | string | 渲染节点/边的图形。节点对应的默认值为 ‘rect’,边对应的默认值为 ‘edge’。 | ||
view | string | 渲染节点/边的视图。 | ||
zIndex | number | 节点/边在画布中的层级,默认根据节点/边添加顺序自动确定。 | ||
visible | boolean | true | 节点/边是否可见。 | |
parent | string | 父节点。 | ||
children | string[] | 子节点/边。 | ||
tools | ToolItem | ToolItem[] | Tools | 工具选项。 | ||
data | any | 节点/边关联的业务数据。 |
构造函数中的内容就是初始化cell时可选的配置,很多都可以望文生义,下面挑一些重要的仔细学习一下。
markup
markup应该是最复杂的一项,markup也很关键,关系到节点的形状和样式。
先贴一些原文:
markup 指定了渲染节点/边时使用的 SVG/HTML 片段,使用 JSON 格式描述。例如内置节点 Shape.Rect 的 markup 定义如下:
{
markup: [
{
tagName: 'rect',
selector: 'body',
},
{
tagName: 'text',
selector: 'label',
},
],
}
表示该节点内部包含
<g data-cell-id="c2e1dd06-15c6-43a4-987a-712a664b8f85" class="x6-cell x6-node" transform="translate(40,40)">
<rect fill="#fff" stroke="#000" stroke-width="2" fill-opacity="0.5" width="100" height="40"></rect>
<text font-size="14" xml:space="preserve" fill="#333" text-anchor="middle" font-family="Arial, helvetica, sans-serif" transform="matrix(1,0,0,1,50,20)">
<tspan dy="0.3em" class="v-line">rect</tspan>
</text>
</g>
通过上面的介绍,我们大致了解了 Markup 的结构,下面我们将详细介绍 Markup 定义。
interface Markup {
tagName: string
ns?: string
selector?: string
groupSelector?: string | string[]
attrs?: { [key: string]: string | number }
style?: { [key: string]: string | number }
className?: string | string[]
textContent?: string
children?: Markup[]
}
可配置参数:
选项 | 类型 | 默认值 | 必选 | 描述 |
---|---|---|---|---|
tagName | string | ✓ | SVG/HTML 元素标签名。 | |
ns | string | “http://www.w3.org/2000/svg” | SVG/HTML 元素命名空间。 | |
selector | string | - | 该元素的选择器(唯一),通过选择器来定位该元素或为该元素指定属性样式。 | |
groupSelector | string | - | 该元素的群组选择器,可以同时为该群组对应的多个元素指定样式。 | |
attrs | Attr.SimpleAttrs | - | 该元素的默认属性键值对。 | |
style | JQuery.PlainObject<string | number> | - | 该元素的行内样式键值对。 | |
className | string | - | 该元素的 CSS 样式名。 | |
textContent | string | - | 该元素的文本内容。 | |
children | Markup[] | - | 嵌套的子元素。 |
tagname
通过 tagName 指定需要创建哪种 SVG/HTML 元素。
ns
该元素的命名空间。需要与 tagName 指定的的元素类型对应,默认使用 SVG 元素命名空间 “http://www.w3.org/2000/svg”。
- SVG 元素命名空间为 “http://www.w3.org/2000/svg”
- HTML 元素的命名空间为 “http://www.w3.org/1999/xhtml”
这里需要暂停一下,个人理解,tagname是指创建的元素类型是什么,ns是元素里的内容。
按我目前的理解,应该是这样:
<div id="tagname">
<input id="ns">
</input>
</div>
selector
该元素的唯一选择器,通过选择器为该元素指定属性样式。例如,为内置节点 Shape.Rect 指定
const rect = new Shape.Rect({
x: 40,
y: 40,
width: 100,
height: 40,
attrs: {
// 指定 rect 元素的样式
body: {
stroke: '#000', // 边框颜色
fill: '#fff', // 填充颜色
},
// 指定 text 元素的样式
label: {
text: 'rect', // 文字
fill: '#333', // 文字颜色
},
},
})
这块不是很理解。
后补充,这里理解了,selector就是markup下的一项配置,可以在attr中指定其样式。说简单点就是markup中元素的id,需要唯一。
groupSelector
该元素的群组选择器,通过群组选择器可以为该群组关联的多个元素指定样式。例如,下面 Markup 中两个
{
markup: [
{
tagName: 'rect',
selector: 'body',
groupSelector: 'group1',
},
{
tagName: 'rect',
selector: 'wrap',
groupSelector: 'group1',
},
{
tagName: 'text',
selector: 'label',
},
],
}
创建节点时,我们可以像下面这样来指定群组样式:
new SomeNode({
attrs: {
group1: {
fill: '#2ECC71',
},
},
})
个人理解:group1相当于是存储了的通用样式。
attrs
注:这块感觉是关键,内容最多的部分,决定了整个shape的样式
该元素的默认属性键值对,通常用于定义那些不变的通用属性,这些默认属性也可以在实例化节点时被覆盖。需要注意的是 markup 中的 attrs 属性只支持原生的 SVG 属性,也就是说 X6 的自定义属性在这里不可用。
例如,我们为内置节点 Shape.Rect 的
{
markup: [
{
tagName: 'rect',
selector: 'body',
attrs: {
fill: '#fff',
stroke: '#000',
strokeWidth: 2,
}
},
{
tagName: 'text',
selector: 'label',
attrs: {
fill: '#333',
textAnchor: 'middle',
textVerticalAnchor: 'middle',
}
},
],
}
style
该元素的行内样式键值对。
这个跟attr里的内容有什么区别?存疑
className
该元素的 CSS 样式名。
同style的疑问。
textContent
该元素的文本内容。
children
嵌套的子元素。
attrs
属性选项 attrs 是一个复杂对象,该对象的 Key 是节点 Markup 定义中元素的选择器(selector),对应的值是应用到该 SVG 元素的 SVG 属性值(如 fill 和 stroke),如果你对 SVG 属性还不熟悉,可以参考 MDN 提供的填充和边框入门教程。
例如,内置节点 Shape.Rect 的 Markup 定义了 ‘body’(代表
const rect = new Shape.Rect({
x: 40,
y: 40,
width: 100,
height: 40,
attrs: {
body: {
fill: '#2ECC71',
stroke: '#000',
},
label: {
text: 'rect',
fill: '#333',
fontSize: 13,
},
},
})
节点渲染到画布后的 DOM 结构看起来像下面这样:
<g data-cell-id="3ee1452c-6d75-478d-af22-88e03c6d513b" class="x6-cell x6-node" transform="translate(40,40)">
<rect fill="#2ECC71" stroke="#000" stroke-width="2" width="100" height="40"></rect>
<text font-size="13" xml:space="preserve" fill="#333" text-anchor="middle" font-family="Arial, helvetica, sans-serif" transform="matrix(1,0,0,1,50,20)">
<tspan dy="0.3em" class="v-line">
rect
</tspan>
</text>
</g>
另外,我们还可以使用 CSS 选择器来指定节点样式,这样我们就不用记住预定义的选择器名称,只需要根据渲染后的 DOM 结构来定义样式即可。使用 CSS 选择器时需要注意,指定的 CSS 选择器可能选中多个元素,这时对应的属性样式将同时应用到多个元素上。
const rect = new Shape.Rect({
x: 40,
y: 40,
width: 100,
height: 40,
attrs: {
rect: { // 使用 rect 这个 css 选择器替代预定义的 body 选择器
fill: '#2ECC71',
stroke: '#000',
},
text: { // 使用 text 这个 css 选择器替代预定义的 label 选择器
text: 'rect',
fill: '#333',
fontSize: 13,
},
},
})
注:也就是说这里两段代码是一个含义。
值得一提的是,支持使用小驼峰(camelCase)格式的属性名,如 ‘fontSize’,这就避免了 ‘font-size’ 这种属性名作为对象 Key 时需要加引号的麻烦。
除了标准的 SVG 属性,我们在 X6 中还定义了一系列特殊属性,详情请参考如何使用特殊属性和如何自定义属性。另外,我们还可以使用 CSS 来定制样式,节点和边渲染到画布后分别有 ‘x6-node’ 和 ‘x6-edge’ 两个样式名,默认的样式定义参考这里。例如,我们可以像下面这样来指定节点中
.x6-node rect {
fill: #2ECC71;
stroke: #000;
}
创建节点/边后,我们可以调用实例上的 attr() 方法来修改节点属性样式。看下面代码,通过 / 分割的路径修改样式,label 选择器对应到
shape
节点/边的图形,图形类似 MVC 模式中的 Model,决定了节点/边的结构化数据。该选项通常在使用 graph.addNode 和 graph.addEdge 两个方法添加节点和边时使用。
const rect = graph.addNode({
shape: 'rect',
x: 100,
y: 200,
width: 80,
height: 40,
label: 'rect',
})
const circle = graph.addNode({
shape: 'circle',
x: 280,
y: 200,
width: 60,
height: 60,
label: 'circle',
})
const edge = graph.addEdge({
shape: 'edge',
source: rect,
target: circle,
})
在 X6 内部实现中,我们通过 shape 指定的图形找到对应的构造函数来初始化节点/边,并将其添加到画布。
该选项的默认值为:
- graph.addNode 方法中 shape 的默认值为 ‘rect’
- graph.addEdge 方法中 shape 的默认值为 ‘edge’
同时,我们在 X6 中内置了一系列节点和边。
内置节点构造函数与 shape 名称对应关系如下表。
构造函数 | shape 名称 | 描述 |
---|---|---|
Shape.Rect | rect | 矩形。 |
Shape.Circle | circle | 圆形。 |
Shape.Ellipse | ellipse | 椭圆。 |
Shape.Polygon | polygon | 多边形。 |
Shape.Polyline | polyline | 多段线。 |
Shape.Path | path | 路径。 |
Shape.Image | image | 图片。 |
Shape.HTML | html | HTML 节点,使用 foreignObject 渲染 HTML 片段。 |
Shape.TextBlock | text-block | 文本节点,使用 foreignObject 渲染文本。 |
Shape.BorderedImage | image-bordered | 带边框的图片。 |
Shape.EmbeddedImage | image-embedded | 内嵌入矩形的图片。 |
Shape.InscribedImage | image-inscribed | 内嵌入椭圆的图片。 |
Shape.Cylinder | cylinder | 圆柱。 |
注:shape可选项有以上几种,html的部分还不清楚是怎么做的。
内置边构造函数与 shape 名称对应关系如下表。
构造函数 | shape 名称 | 描述 |
---|---|---|
Shape.Edge | edge | 边。 |
Shape.DoubleEdge | double-edge | 双线边。 |
Shape.ShadowEdge | shadow-edge | 阴影边。 |
tools
节点/边的小工具。小工具可以增强节点/边的交互能力,我们分别为节点和边提供了以下内置工具:
节点
- button 在指定位置处渲染一个按钮,支持自定义按钮的点击交互。
- button-remove 在指定的位置处,渲染一个删除按钮,点击时删除对应的节点。
- boundary 根据节点的包围盒渲染一个包围节点的矩形。注意,该工具仅仅渲染一个矩形,不带任何交互。
边
- vertices 路径点工具,在路径点位置渲染一个小圆点,拖动小圆点修改路径点位置,双击小圆点删除路径点,在边上单击添加路径点。
- segments 线段工具。在边的每条线段的中心渲染一个工具条,可以拖动工具条调整线段两端的路径点的位置。
- boundary 根据边的包围盒渲染一个包围边的矩形。注意,该工具仅仅渲染一个矩形,不带任何交互。
- button 在指定位置处渲染一个按钮,支持自定义按钮的点击交互。
- button-remove 在指定的位置处,渲染一个删除按钮,点击时删除对应的边。
- source-arrowhead-和-target-arrowhead 在边的起点或终点渲染一个图形(默认是箭头),拖动该图形来修改边的起点或终点。
示例:
graph.addNode({
x: 40,
y: 40,
width: 100,
height: 40,
tools: 'button-remove' // or { name: 'button-remove' }
})
graph.addNode({
x: 40,
y: 40,
width: 100,
height: 40,
tools: {
name: 'button-remove',
args: {
x: 10, // 按钮的 x 坐标,相对于节点的左上角
y: 10, // 按钮的 y 坐标,相对于节点的左上角
},
},
})
graph.addNode({
x: 40,
y: 40,
width: 100,
height: 40,
tools: [
'button-remove',
{
name: 'boundary',
args: {
padding: 5,
},
},
],
})
data
与节点/边关联的业务数据。例如,我们在实际使用时通常会将某些业务数据存在节点/边的 data 上。
const rect = new Shape.Rect({
x: 40,
y: 40,
width: 100,
height: 40,
data: {
bizID: 125,
date: '20200630',
price: 89.00,
}
})
相关函数:
setData()
设置关联的业务数据。
默认情况触发 ‘change:data’ 事件和画布重绘,当 options.silent 为 true 时不触发 ‘change:data’ 事件和画布重绘。
参数:
名称 | 类型 | 必选 | 默认值 | 描述 |
---|---|---|---|---|
data | any | ✓ | ||
options.overwrite | boolean | false | 为 true 时替换现有值,否则根据 options.deep 选项进行深度或浅度 merge。 | |
options.deep | boolean | true | 当 options.overwrite 为 false 时有效, 为 true 时进行深度 merge,否则进行浅 merge。 | |
options.silent | boolean | false | 为 true 时不触发 ‘change:data’ 事件和画布重绘。 | |
options…others | object | 其他自定义键值对,可以在事件回调中使用。 |
默认与原数据进行深度 merge,并触发 ‘change:data’ 事件和画布重绘:
cell.setData(data)
当 options.overwrite 为 true 时,替换旧数据:
cell.setData(data, { overwrite: true })
当 options.deep 为 false 时,与原数据进行浅 merge:
cell.setData(data, { overwrite: true })
当 options.silent 为 true 时,不触发 ‘change:data’ 事件和画布重绘:
cell.setData(data, { silent: true })
在选项中支持其他自定义键值对,可以在事件回调用使用:
cell.setData(data, { otherKey: 'otherValue', ... })
replaceData(…)
替换数据
replaceData(data: any, options: Cell.SetOptions = {}): this
用指定的数据替换原数据,相当于调用 setData(data, { …options, overwrite: true })。
名称 | 类型 | 必选 | 默认值 | 描述 |
---|---|---|---|---|
data | any | ✓ | ||
options.silent | boolean | false | 为 true 时不触发 ‘change:data’ 事件和画布重绘。 | |
options…others | object | 其他自定义键值对,可以在事件回调中使用。 |
Node
Node 是所有节点的基类,继承自 Cell,并定义了节点的通用属性和方法。
其他内容跟Cell差不多,多了一些额外的配置项。
其中 Node.Metadata 是创建节点的选项,除了从 Cell 继承的 markup、attrs、zIndex 等选项外,还支持以下选项。
选项 | 类型 | 默认值 | 必选 | 描述 |
---|---|---|---|---|
size | { width: number; height: number } | { width: 1, height: 1 } | 节点大小。 | |
position | { x: number; y: number } | - | 节点位置。 | |
angle | number | - | 节点的旋转角度。 | |
ports | object | - | 链接桩。 | |
portMarkup | Markup | object | 链接桩的 DOM 结构。 | |
portLabelMarkup | Markup | object | 链接桩标签的 DOM 结构。 |
可以看到,主要多的是size和桩的配置。
桩的信息在第二章捋了一遍,这边重点看一下其他的内容。
使用vue组件作为节点
官网有个例子,可以按照例子来。
但是,使用的时候遇到了一些问题。
问题
这里的getNode提示没有,问题类似于:https://github.com/antvis/X6/issues/3544
inject: ["getGraph", "getNode"],
尝试过更改版本等方法,不能避免这个问题。
如果不能解决getNode的问题,没有办法动态变更node节点内参数。毕竟后期肯定是要做复杂的节点的,需要用到vue的组件功能来制作复杂节点。
辅助文档:
这位大佬的demo我也跑不通,会出现版本异常。但是看他的代码,如果复杂节点,数据传递会很绕很复杂。
https://www.jianshu.com/p/cb6070193cbf
antv x6-v2的文档在这儿,也是这位大佬的。
https://www.jianshu.com/p/f8c8bfbba9e5
如果有相关的解决方案的话可以在下方讨论。