使用自定义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标签,如图: