使用自定义svg标签时,发现示例并没有指明如何构建复杂的svg标签,“点到为止”。同样也是需要查看源码才能熟悉如何配置复杂场景下的svg标签。以下是我为了满足业务场景配合源码学习到的关于自定义svg标签的碎片。当然写博客的时候才发现原来有详细的文档,😓
x6 自定义 SVG 标签使用碎片
定义边
官网的示例如下:官网示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 Graph.registerEdge( "custom-edge-label" , { inherit: "edge" , defaultLabel: { markup: [ { tagName: "rect" , selector: "body" , }, { tagName: "text" , selector: "label" , }, ], attrs: { label: { fill: "#000" , fontSize: 14 , textAnchor: "middle" , textVerticalAnchor: "middle" , pointerEvents: "none" , }, body: { ref: "label" , fill: "#ffd591" , stroke: "#ffa940" , strokeWidth: 2 , rx: 4 , ry: 4 , refWidth: "140%" , refHeight: "140%" , refX: "-20%" , refY: "-20%" , }, }, position: { distance: 200 , options: { absoluteDistance: true , reverseDistance: true , }, }, }, }, true );
这次一点点来拆解说明
‘custom-edge-label’
定义当前特定边的名字,可以随意取名,有实际意义即可
inherit
定义当前边的继承类型,一般边定义从 edge 继承即可
defaultLabel
markup
定义了标签的渲染层次结构。如果 markup 指定为一个对象,则 x6 会将其转换为数组,所以本质这里需要一个数组渲染。其每一项的结构类似:
1 2 3 4 5 6 7 8 9 10 11 12 13 { tagName: String , selector: String , ns: String , attrs: String , className: String , textContent: String , groupSelector: String | Array , style: String , chilren: Array (Markup) },
markup中最核心的就是children属性,它使得markup约等于一个伪dom树,源码中的parseJSONMarkup()方法会BFS逐层解析并生成dom结构,并使用renderMarkup()方法渲染该dom。
其中可选的ns值有:
svg: http://www.w3.org/2000/svg
xmlns: http://www.w3.org/2000/xmlns/
xml: http://www.w3.org/XML/1998/namespace
xlink: http://www.w3.org/1999/xlink
xhtml: http://www.w3.org/1999/xhtml
attrs
attrs是一个属性选择器组,其中的对象都是之前在markup中定义好的选择器的名字,可以选中相应的节点,其属性可以定义对应节点的属性,除了节点本身可以有的属性外,还可以定义
ref:定义关联的节点,用来设置相对属性
refWidth:根据ref关联的节点的宽度,定义宽度,百分比值
refHeight:根据ref关联的节点的高度,定义高度,百分比值
refX:根据ref关联的节点的X,定义X方向的偏移,可以是百分比值,也可以是绝对值
refY:根据ref关联的节点的Y,定义Y方向的偏移,可以是百分比值,也可以是绝对值
textAnchor:定义文本节点的水平基准点,可选start/end/middle
textVerticalAnchor:定义文本节点的垂直基准点,可选 top/bottom/middle
…
详见官网文档
position
position定义标签整体的位置,已知的结构如下
1 2 3 4 5 6 7 8 9 10 { distance: Number | String , options: Object , offset: Object { x: Number | String , y: Number | String }, ... }
更详尽的文档见官网API
添加边
到此,已经可以构建复杂的标签,但是这些定义都是静态的,如何与业务数据连接起来呢?看一下官网示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 graph.addEdge({ source: [170 , 160 ], target: [550 , 160 ], shape: 'custom-edge-label' , attrs: { line: { stroke: '#ccc' , }, }, labels: [ { attrs: { line: { stroke: '#73d13d' , }, text: { text: 'Custom Label' , }, }, }, ], })
其中的labels类似定义边时的defaultLabel.attrs一样可以选择到对应的节点,不知是不是有意为之,这里官网的代码中的line选择器并不能选中任何节点,如果要改变图中矩形的边框,需要把line 变成body。
业务相关
我们的项目需要引入自定义的svg图标,平时在项目中使用的是类似如下结构的vue组件
1 2 3 4 5 6 7 <template > <i class ="svg-icon-wrapper" > <svg class ="svg-icon" > <use :xlink:href ="iconName" > </use > </svg > </i > </template >
出于业务需要,我们构建的标签需要添加对应的svg。由此使用上述的定义边 ,构造类似上述组件的伪dom层次结构,代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 Graph.registerEdge( 'custom-edge-label' , { inherit: 'edge' , defaultLabel: { markup: [ { tagName: 'foreignObject' , selector: 'foreign' , className: 'relation-line-label' , children: [ { tagName: 'body' , selector: 'body' , ns: 'http://www.w3.org/1999/xhtml' , children: [ { tagName: 'i' , selector: 'i' , ns: 'http://www.w3.org/1999/xhtml' , className: 'svg-icon-wrapper' , children: [ { tagName: 'svg' , selector: 'svg' , ns: 'http://www.w3.org/2000/svg' , className: 'svg-icon' , children: [ { tagName: 'use' , ns: 'http://www.w3.org/2000/svg' , selector: 'use' } ] } ] } ] } ] }, { tagName: 'text' , selector: 'label' } ], attrs: { label: { fill: '#303133' , fontSize: 12 , textAnchor: 'middle' , textVerticalAnchor: 'middle' , pointerEvents: 'none' }, body: { style: 'width: 24px; height: 24px; background-color: transparent;' }, foreign: { width: 24 , height: 24 , ref: 'label' , refX: '-30' , refY: '-30%' } }, position: { distance: 0.5 , offset: { y: -10 } } } }, true )
其中有以下几点值得注意:
svg需要使用foreignObject标签包裹html部分,因此需要多一层foreignObject
ns命名空间必须定义清楚,如svg的ns必须定义为http://www.w3.org/2000/svg
,否则实际生成的svg标签实际是一个HtmlUnknownElement元素,同理use的ns也必须定义为该命名空间
use可以全局调用已经解析好的svg,只需要指定xlink:href属性即可,因此可以将需要的svg的名字使用addEgde参数里的labels.attrs属性指定xlink:href,如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 { source: [124 , 260 ], target: [224 , 260 ], shape: 'custom-edge-label' , labels: [ { attrs: { text: { text: '标签文本' }, use: { 'xlink:href' : '#svg-loading' } } } ], ... }
于是完成了自定义SVG标签,如图: