这是我参与更文挑战的第 4 天,活动详情查看:更文挑战
首先还是简单介绍一下 X6
X6 是图编辑引擎,特点是节点、边、等元素的定制能力非常强,经常用来构建流程图、ER 图、DAG 图、脑图等应用。
根据官网的示例,X6 可以实现以下的效果
具体示例,请看这里:x6.antv.vision/zh/examples…
什么是元件?
节点和边统称为元件。元件是节点和边的共同基类。
元件定义了节点和边共同属性和方法,如属性样式、可见性、业务数据等,并且在实例化、定制样式、配置默认选项等方面具有相同的行为。
看下面的继承关系:
graph LR
Cell --> Node
Cell --> Edge
Node --> Shape.Rect
Node --> Shape.Circle
Node --> Shape.Ellipse
Node --> Shape.Xxx...
Edge --> Shape.Edge
Edge --> Shape.DoubleEdge
Edge --> Shape.ShadowEdge
内置元件
官方内置了一批节点和边,具体看这里:内置节点
除了上面提到的内置元件之外,还有一个文档中未提到,但却非常有用的元件。
构造函数 | shape名称 | 描述 |
---|---|---|
Shape.Empty | empty | 无预设配置的节点。需要通过 markup 和 attr 指定图形和样式 |
自定义元件
虽然官方提供了许多内置组件,但可能无法满足丰富的业务需求。这时就需要创建自定义元件了。
自定义元件的官方说法:自定义节点
如何开发一个自定义元件?
继承官方提供的节点开发元件
官方给了一个继承的示例
但除了官方文档提到的,我们甚至可以直接写继承类,不用调用静态方法。这部分的实现受到了“根据图片大小缩放节点”的启发。
这里我们以自定义开关节点示例演示直接写继承类的方式创建自定义组件
首先,绘制一个这样的开关:
SVG 图形的绘制,需要使用 markup
和 attr
属性。其中
markup
负责用结构化的方式创建 SVG 中各元素的节点attr
负责设置节点的样式等属性
这两部分参照代码清单中的 markup
和 attr
部分。
const switchCenter = {
x: 35,
y: -2,
}
const switchOpen = `rotate(-30 ${switchCenter.x} ${switchCenter.y})`
const switchClose = `rotate(-12 ${switchCenter.x} ${switchCenter.y})`
const markup = [
{
tagName: 'g',
selector: 'left-group',
children: [
{
tagName: 'rect',
selector: 'left',
groupSelector: 'line',
attrs: {
x: 0,
y: 0,
},
},
{
tagName: 'circle',
selector: 'lco',
groupSelector: 'co',
attrs: {
cx: 30,
},
},
{
tagName: 'circle',
selector: 'lci',
groupSelector: 'ci',
attrs: {
cx: 30,
},
},
],
},
{
tagName: 'rect',
selector: 'switch',
groupSelector: 'line',
},
{
tagName: 'g',
selector: 'right-group',
children: [
{
tagName: 'rect',
selector: 'right',
groupSelector: 'line',
attrs: {
x: 70,
y: 0,
},
},
{
tagName: 'circle',
selector: 'rco',
groupSelector: 'co',
attrs: {
cx: 70,
},
},
{
tagName: 'circle',
selector: 'rci',
groupSelector: 'ci',
attrs: {
cx: 70,
},
},
],
},
]
const attrs = {
line: {
width: 30,
height: 2,
fill: '#000',
stroke: '#000',
},
co: {
r: 8,
fill: '#000',
},
ci: {
r: 4,
fill: '#fff',
},
switch: {
...switchCenter,
width: 35,
transform: switchOpen,
},
}
上述代码中的
switchCenter
指定了开关的旋转中心坐标,switchOpen
和switchClose
分别表示开关开启和关闭时的样式。
其次,创建自定义节点
import { Graph, Shape } from '@antv/x6'
class Switch extends Shape.Empty {
constructor(metadata) {
super(Object.assign({}, metadata, { markup, attrs }))
}
}
Graph.registerNode('switch', Switch, true)
注意到上面的构造函数部分了吗?
constructor(metadata) {
super(Object.assign({}, metadata, { markup, attrs }))
}
如果有构造函数出现,必须调用 super
方法,调用时传入 metadata
值。在调用 super
方法时,可以将上面指定的样式数据传入。
然后,加上开关动态效果
在上面创建的自定义节点里面,添加 toggleOpen 方法。该方法可切换开关状态。
class Switch extends Shape.Empty {
constructor(metadata) {
super(Object.assign({}, metadata, { markup, attrs }))
}
toggleOpen() {
const attrPath = 'attrs/switch/transform'
const current = this.prop(attrPath)
const target = current === switchOpen ? switchClose : switchOpen
this.transition(attrPath, target, {
interp: (a, b) => {
const reg = /-?\d+/g
const start = parseInt(a.match(reg)[0], 10)
const end = parseInt(b.match(reg)[0], 10)
const d = end - start
return (t) => {
return `rotate(${start + d * t} ${switchCenter.x} ${switchCenter.y})`
}
},
})
}
}
我们期望在点击时执行切换动作。
根据开发者的说法,需要创建一个自定义的 view,在 view 中注册绑定事件,然后在创建或声明元件时,指定为自定义的 view。
关于
view
相关信息,请查看 View
由于我们创建的是一个节点,因此对应的 view
需要继承 NodeView
。
具体实现过程:
首先创建一个 view
class SwitchView extends NodeView {
onClick() {
this.cell?.toggleOpen();
}
}
Graph.registerView('switch', SwitchView, true)
然后修改自定义元件,指定为对应的 view
view
属性的值和 Graph.registerView
中第一个参数的值一致
class Switch extends Shape.Empty {
constructor(metadata) {
- super(Object.assign({}, metadata, { markup, attrs }))
+ super(Object.assign({}, metadata, { markup, attrs, view: 'switch' }))
}
...
}
完成以上步骤,一个自定义的开关组件就完成了。
使用方式和内置组件一致:
graph.addNode({
x: 320,
y: 120,
shape: 'switch'
})
完整的代码清单参照:Github Gist
使用 HTML/Vue/React 渲染内容
按照官方的说法
在 SVG 中有一个特殊的
<foreignObject>
元素,在该元素中可以内嵌任何 XHTML 元素,所以我们可以借助该元素来渲染 HTML 元素和 React 组件到需要位置。
<svg xmlns="http://www.w3.org/2000/svg">
<foreignObject width="120" height="50">
<body xmlns="http://www.w3.org/1999/xhtml">
<p>Hello World</p>
</body>
</foreignObject>
</svg>
具体操作看文档吧:使用 HTML/React/Vue 渲染
根据文档,我们可以得到以下结论:
HTML
渲染内容,是通过DOM API
进行操作Vue/React
渲染内容,本质上是执行了$mount
操作