我前一段时间写过一篇文章《「AntV」怎样用SVG & X6制作客户旅程时光轴》,分享了怎样通过使用SVG和X6制作客户旅程时光轴,得到了很多伙伴们的喜欢。
一些小伙伴找我要到了演示Demo的源代码,可是即使在有演示Demo源代码,在这基础之上再进行二次开发时却遇到了很多障碍。
软件/工具是知识和经验凝结的产物,在开发上遇到的很多障碍、往往是相关知识、开发经验或者能力方面有欠缺,所以我打算在演示Demo源代码基础之上,通过接下来的《「AntV X6」从0到1实现客户旅程时光轴组件》一系列文章,将我使用SVG和X6的相关知识和经验对大家进行分享,希望能帮到有同样需求的朋友。
《「AntV X6」从0到1实现客户旅程时光轴组件》会以系列文章的形式,有步骤、有重点地分享我使用AntV X6进行客户旅程时光轴组件实现相关的知识和经验,目前该系列文章会有如下6篇:
- 「AntV X6」从0到1实现客户旅程时光轴组件01-先导篇
- 「AntV X6」从0到1实现客户旅程时光轴组件02-画布篇
- 「AntV X6」从0到1实现客户旅程时光轴组件03-节点篇
- 「AntV X6」从0到1实现客户旅程时光轴组件04-连线篇
- 「AntV X6」从0到1实现客户旅程时光轴组件05-布局篇
- 「AntV X6」从0到1实现客户旅程时光轴组件06-动画篇
系列文章将会以“项目驱动”的方式进行技术知识和开发经验的分享,贯穿整个系列文章的客户旅程时光轴Demo如下:
回顾之前的文章,我们知道了一个客户旅程时光轴图由以下5个核心要素组成——
点(节点,Node):旅程图中那些一个个的圆圈,被称为“节点”;边(连线,Edge):两个节点中的连线,被称为“边”;标签(Label):节点下的说明文字,被称为“标签”;布局(Layout):各个节点位置坐标的排列,被称为“布局”;动画(Animation):在旅程时光轴图中,有一个从第一个节点一直到最后一个节点的描边动画。
以下是《「AntV X6」从0到1实现客户旅程时光轴组件03-节点篇》文章正文。
节点
节点是图里面的最基础的元素——接下来我们从节点定义和节点样式两个方面讲述在AntV X6中节点的使用:
节点定义:节点样式:
节点定义
首先来看下一个简单的矩形节点的基础配置:
// 一个简单的矩形节点的基础配置
graph.addNode({
shape: 'rect', // shape:定义节点的形状,X6 内置了 rect、circle、ellipse、polygon、polyline、image、html 等基础形状
x: 100, // x/y:定义节点的左上角坐标
y: 100,
width: 80, // width/height:定义节点的尺寸
height: 40,
attrs: { // attrs: 可以将 attrs 看做 css 样式集合
body: {
stroke: 'red'
}
}
});
shape:定义节点的形状,X6 内置了rect、circle、ellipse、polygon、polyline、image、html等基础形状;x/y:定义节点的左上角坐标;width/height:定义节点的尺寸 ;attrs:看到attrs大家可能比较奇怪,这是什么东西?其实可以将attrs看做css样式集合,其中body类似于css选择器,body的值是被选中元素的属性。那body又是哪来的呢?这里就要说的另一个重要的配置项markup,markup表示的是节点的DOM结构,内置的rect的默认markup为:
[
{
tagName: 'rect',
selector: 'body',
},
{
tagName: 'text',
selector: 'label',
},
]
渲染完成后,实际生效的 DOM 为:
<g data-cell-id="ca715562-8faf-4c88-a242-2b18d4ce47a6" data-shape="rect" class="x6-cell x6-node" transform="translate(100,100)">
<rect fill="#ffffff" stroke="red" stroke-width="2" width="80" height="40"></rect>
<text font-size="14" fill="#000000" text-anchor="middle" text-vertical-anchor="middle" font-family="Arial, helvetica, sans-serif" transform="matrix(1,0,0,1,40,20)"></text>
</g>
所以说一个节点是由 markup 和 attrs 来决定结构和样式,我们可以通过设置 markup 和 attrs 来定义自己业务中的节点,看下面的例子:
graph.addNode({
shape: 'rect',
x: 200,
y: 80,
width: 70,
height: 70,
markup: [
{
tagName: 'rect',
selector: 'r1'
},
{
tagName: 'circle',
selector: 'c1'
},
{
tagName: 'circle',
selector: 'c2'
},
{
tagName: 'circle',
selector: 'c3'
},
{
tagName: 'circle',
selector: 'c4'
}
],
attrs: {
r1: {
width: 70,
height: 70,
stroke: '#ccc',
rx: 12,
ry: 12,
},
c1: {
r: 10,
cx: 20,
cy: 20,
fill: '#000'
},
c2: {
r: 10,
cx: 50,
cy: 20,
fill: '#000'
},
c3: {
r: 10,
cx: 20,
cy: 50,
fill: '#000'
},
c4: {
r: 10,
cx: 50,
cy: 50,
fill: '#000'
},
}
})
上面可以看到定义节点的时候,属性是固定写死的,在业务场景中,经常需要动态修改节点的样式,X6 中也提供了非常便利的方法:
const node = graph.addNode({
shape: 'rect',
x: 290,
y: 100,
width: 80,
height: 40,
attrs: {
body: {
stroke: 'red'
}
}
})
node.attr('body/stroke', 'green')
node.attr('body/fill', 'yellow')
那么问题来了,如果多个节点的结构和样式相似度很高,每次定义节点都要写很多类似的代码,有没有一种方式可以将节点之间的公共属性抽象出来呢?X6 提供了很优雅的方式来解决这个问题,首先注册自定义的节点类型,在这里配置公共的属性,然后在添加节点的时候指定 shape 值为刚才注册的节点类型。
Graph.registerNode('custom-rect', {
inherit: 'rect', // 继承自 Shape.Rect
width: 300, // 默认宽度
height: 40, // 默认高度
attrs: {
body: {
rx: 10, // 圆角矩形
ry: 10,
strokeWidth: 1,
fill: '#5755a1',
stroke: '#5755a1',
},
label: {
fill: '#fff',
fontSize: 18,
refX: 10, // x 轴偏移,类似 css 中的 margin-left
textAnchor: 'left', // 左对齐
}
},
})
graph.addNode({
shape: 'custom-rect',
x: 100,
y: 200,
width: 100,
height: 50,
label: 'rect1'
})
graph.addNode({
shape: 'custom-rect',
x: 250,
y: 200,
width: 100,
height: 50,
label: 'rect2',
attrs: {
body: {
fill: '#ccc'
}
}
})
节点样式
把节点做进一步的拆解,适用于表达节点的视觉样式的有大小、颜色、描边、形状、图标、角标 这么几个视觉参数:
大小:通过width/height来定义节点的尺寸;颜色:通过attrs中的fill来定义填充颜色;描边:通过attrs中的stroke来定义描边颜色;形状:通过shape来定义节点形状,X6 内置了rect、circle、ellipse、polygon、polyline、image、html等基础形状,另外也支持React、Vue组件;图标:同上;角标:节点右上角的圆形徽标。
另外,X6 不仅支持上文提到的基础的 SVG 节点,还具备在节点中渲染 React、Vue 组件的能力。在实际业务场景中,如果对 SVG 不熟悉或者节点内容复杂,我们可以根据技术栈选择 React/Vue 渲染,这样在节点内部,我们可以用熟悉的方式绘制各种复杂的内容。
在接下来的系列文章中,我们继续从“节点”、“连线”、“标签”、“布局”、“动画”这5个要素出发,通过结合下面的客户旅程时光轴图Demo案例,进行更详细的分解和讲述,希望能帮助到有需要的朋友: