X6 内置 Attributes 与自定义 Attribute 注册完全指南
【免费下载链接】X6🚀 JavaScript diagramming library that uses SVG and HTML for rendering.项目地址: https://gitcode.com/GitHub_Trending/x6/X6
X6 是基于 SVG 与 HTML 渲染的 JavaScript 图编辑引擎,其样式与几何布局体系建立在attrs之上。attrs不仅支持原生 SVG 属性,还内置了一整套"特殊属性"(Attributes),用于实现相对定位、尺寸同步、文本换行、路径沿边等高频能力。本文以 site/docs/api/registry/attr.en.md 为主线,结合仓库源码(src/registry/attr)与测试用例(tests/registry/attr/ref.spec.ts)逐项讲解内置属性的语义、取值规则与适用元素,并深入registry注册机制,演示如何通过Graph.registerAttr编写自定义属性,实现完全可控的渲染行为。
一、Attributes 概述
在 X6 中,节点的外观由markup(DOM 结构)与attrs(样式与几何)共同描述。markup为每个结构元素指定selector,attrs则通过selector精确命中元素并施加属性:
graph.addNode({ markup: [ { tagName: 'rect', selector: 'body' }, { tagName: 'rect', selector: 'custom' }, ], attrs: { body: { width: 100, height: 50, fill: '#f5f5f5' }, custom: { fill: 'red' }, }, })attrs的值可以是浏览器原生 SVG 属性(如fill、stroke、x、y),也可以是 X6 定义的"特殊属性"。特殊属性会被 X6 在渲染阶段转换成浏览器可识别的属性(src/registry/attr/index.ts 中的attrPresets将 raw.ts 的原生属性别名与特殊属性合并后注册进attrRegistry)。下文所有内置属性均来自 src/registry/attr 目录下的实现。
二、相对定位属性(ref 系列)
相对定位是 X6 最有价值的特性之一:子元素可以相对于另一个"参考元素"(reference element)定位与缩放。参考元素由ref指定,其选择器与markup中定义的selector一致。渲染时,参考元素的包围盒(refBBox,即 Rectangle)作为所有计算的基准。
1. ref:指定参考元素
ref本身不产生渲染效果,它只是声明"以哪个元素为参考"。下面代码中,custom元素的宽高分别取body的一半:
graph.addNode({ markup: [ { tagName: 'rect', selector: 'body' }, { tagName: 'rect', selector: 'custom' }, ], attrs: { body: { width: 100, height: 50 }, // custom 元素的长宽是 body 的一半 custom: { ref: 'body', refWidth: 0.5, refHeight: 0.5 }, }, })ref的实现位于 src/registry/attr/ref.ts:它故意不设置任何属性值,仅作为占位,真正的计算交给refX/refY/refWidth等属性完成。若未指定ref,则参考元素默认为节点本身,refBBox即节点位置与尺寸构成的矩形。
2. refX / refY:相对左上角定位
refX设置元素的x坐标,基准是参考元素左上角的x坐标;refY对应y坐标,基准是左上角的y坐标。取值规则:
- 值在
[0, 1]之间,或是百分比(如'50%'):按参考元素宽(高)的比例偏移。refX: 0.5表示目标x坐标在参考x坐标右侧偏移参考宽度的 50%。 - 值
<0或>1:按绝对像素偏移。refX: 20表示目标x坐标在参考x坐标右侧偏移 20px。
源码positionWrapper('x', 'width', 'origin')(src/registry/attr/ref.ts)正是这一规则的实现:百分比或0 < v < 1时delta = refBBox[axis] + refBBox[dimension] * value,否则delta = refOrigin[axis] + value。测试tests/registry/attr/ref.spec.ts 验证了在refBBox = Rectangle(100, 100, 200, 150)时,refX('50%')得到x = 200,refX('20')得到x = 120。
3. refX2 / refY2:同时叠加相对与绝对偏移
当需要"相对偏移 + 固定像素"同时生效时,使用refX2/refY2:
{ refX: '50%', // 参考宽度的一半 refX2: 20, // 再额外偏移 20px }即目标x坐标 = 参考x坐标 + 参考宽度的 50% + 20px。源码中refX2 = refX、refY2 = refY(src/registry/attr/ref.ts)是简单别名,真正"叠加"的效果来自渲染管线分别执行两次定位计算。
4. refDx / refDy:相对右下角定位
与refX/refY不同,refDx/refDy以参考元素右下角坐标为基准(源码positionWrapper('x', 'width', 'corner'),src/registry/attr/ref.ts)。取值规则与上面完全一致:[0, 1]或百分比表示按参考宽(高)的比例,<0或>1表示绝对像素偏移。测试tests/registry/attr/ref.spec.ts 中refDx('-10')在refBBox = Rectangle(100, 100, 200, 150)下得到x = 290(即右下角 300 减 10),refDy('-15')得到y = 235。
5. refWidth / refHeight:尺寸跟随参考元素
refWidth设置元素宽度,refHeight设置元素高度,基准是参考元素的宽(高):
[0, 1]或百分比(如'75%'):按参考宽(高)的比例。refWidth: 0.75表示宽度为参考宽度的 75%。<0或>1:在参考宽(高)基础上增减。refWidth: 20表示比参考元素宽 20px。
源码setWrapper('width', 'width')(src/registry/attr/ref.ts)中,超出部分用Math.max(value + refBBox[dimension], 0)兜底,避免出现负尺寸。测试验证refWidth('50%')在参考宽 200 时得到 100,refWidth('-20')得到 180(tests/registry/attr/ref.spec.ts)。
⚠️ 注意:
refWidth/refHeight仅适用于支持宽高属性的元素,如<rect>。
6. refWidth2 / refHeight2:相对与绝对尺寸叠加
{ refWidth: '75%', // 参考宽度的 75% refWidth2: 20, // 再增加 20px }最终宽度 = 参考宽度的 75% + 20px。源码同样以别名方式实现(src/registry/attr/ref.ts)。
7. refCx / refCy:圆心跟随参考元素
refCx设置原生cx属性,基准是参考元素的宽度;refCy设置原生cy,基准是参考元素的高度。取值规则同前:[0, 1]/百分比为比例,<0/>1为绝对偏移(源码 src/registry/attr/ref.ts)。仅适用于支持cx/cy的元素,如<ellipse>。
8. refRx / refRy:圆角半径跟随参考元素
refRx设置rx,refRy设置ry,分别以参考元素宽度、高度为基准(源码 src/registry/attr/ref.ts)。仅适用于支持rx/ry的元素,如<rect>。
9. refRInscribed(别名 refR):内切圆半径
refRInscribed设置r,以参考元素宽高较小者为基准长度。源码 src/registry/attr/ref.ts 会动态比较refBBox.height与refBBox.width,谁小就用谁的setWrapper计算。测试tests/registry/attr/ref.spec.ts 验证:宽 200 高 100 时取宽度(50% 得 50),宽 100 高 200 时取高度(50% 得 50)。别名refR = refRInscribed定义在 src/registry/attr/ref.ts。
10. refRCircumscribed:外接圆半径
refRCircumscribed同样设置r,但以参考元素的对角线长度为基准(源码 src/registry/attr/ref.ts,对角线计算为sqrt(h² + w²))。测试验证refRCircumscribed('50%')得到对角线的一半(tests/registry/attr/ref.spec.ts)。两者配合可让圆形元素精确内切或外接参考矩形。
三、path 与 points 属性:让形状"适配"参考元素
X6 提供四个特殊属性,将<path>的d与<polygon>/<polyline>的points等比缩放并对齐到参考元素。它们的实现核心是shapeWrapper(src/registry/attr/ref.ts):解析路径/点集得到形状及其包围盒,用refBBox.getMaxScaleToFit计算等比缩放系数,再按resetOffset决定是否把原点平移到0,0。Keep系列保留原数据的偏移,Reset系列消除偏移。
1. refDKeepOffset
缩放 pathData 使其与参考元素同尺寸,并将目标<path>起点与参考元素起点对齐;保留pathData 自带偏移:
import { Graph, Node } from '@antv/x6' const Path = Node.define({ markup: [{ tagName: 'path' }], attrs: { path: { refDKeepOffset: 'M 10 10 30 10 30 30 z', // path 偏移为 10,10 fill: 'red', stroke: 'black', }, }, }) const container = document.getElementById('container')! const graph = new Graph({ container, width: 800, height: 80, grid: true }) const path = new Path().resize(40, 40).addTo(graph) const view = graph.findView(path) console.log(view.findOne('path').getAttribute('d')) // 'M 10 10 50 10 50 50 z'2. refDResetOffset(别名 refD)
行为与refDKeepOffset相同,但会移除pathData 的偏移,将左上角平移到原点,与参考元素严格对齐:
// 同样配置,但使用 refDResetOffset console.log(view.findOne('path').getAttribute('d')) // 'M 0 0 40 0 40 40 z'别名refD = refDResetOffset(src/registry/attr/ref.ts)。
3. refPointsKeepOffset
针对<polygon>/<polyline>的points属性,语义与refDKeepOffset一致:等比缩放 + 对齐起点 + 保留偏移:
const Polygon = Node.define({ markup: [{ tagName: 'polygon' }], attrs: { polygon: { refPointsKeepOffset: '10,10 30,10 30,30', // points 偏移为 10,10 fill: 'red', stroke: 'black', }, }, }) // resize(40, 40) 后 // '10,10 50,10 50,50'4. refPointsResetOffset(别名 refPoints)
缩放并对齐,同时移除偏移,与参考元素严格对齐:
// 同样配置,但使用 refPointsResetOffset // '0,0 40,0 40,40'别名refPoints = refPointsResetOffset(src/registry/attr/ref.ts)。
5. resetOffset:独立的偏移归零
若只想消除某个已有d的偏移而不缩放,可使用resetOffset:
path.attr({ path: { d: 'M 10 10 20 20', resetOffset: true, // 平移后 d 的值变为 "M 0 0 10 10" }, })其实现是返回{ x: -refBBox.x, y: -refBBox.y }的偏移定义(src/registry/attr/align.ts)。
四、对齐属性:xAlign 与 yAlign
xAlign控制元素与自身x坐标的水平对齐方式:
'left':元素左边与x对齐'middle':元素中心与x对齐'right':元素右边与x对齐
yAlign控制垂直对齐:
'top':元素顶边与y对齐'middle':元素中心与y对齐'bottom':元素底边与y对齐
实现为offsetWrapper('x', 'width', 'right')/offsetWrapper('y', 'height', 'bottom')(src/registry/attr/align.ts),通过返回偏移量将元素中心/边线对齐到指定坐标。可交互示例见仓库演示 examples/src/pages/shape 下的相关页面。
五、视觉属性:fill / stroke / filter / style / html / title
1. fill / stroke:支持渐变填充
当fill值为对象时表示渐变填充,字符串则按普通颜色处理:
rect.attr('body/fill', { type: 'linearGradient', stops: [ { offset: '0%', color: '#E67E22' }, { offset: '20%', color: '#D35400' }, { offset: '40%', color: '#E74C3C' }, { offset: '60%', color: '#C0392B' }, { offset: '80%', color: '#F39C12' }, ], })实现(src/registry/attr/fill.ts)通过qualify: ObjectExt.isPlainObject判断对象值,set调用view.graph.defineGradient生成<defs>中的渐变并把fill替换为url(#id)。stroke用法相同,源码见 src/registry/attr/stroke.ts。
2. filter:SVG 滤镜
filter值为对象时使用 X6 内置滤镜定义,字符串则使用原生形式(如"url(#myfilter)"):
rect.attr('body/filter', { name: 'dropShadow', args: { dx: 2, dy: 2, blur: 3 }, })实现(src/registry/attr/filter.ts)同样以isPlainObject限定,set调用view.graph.defineFilter并返回url(#id)。可用的滤镜名称(dropShadow、blur、outline等)及参数见 src/registry/filter 目录及文档 site/docs/api/registry/filter.zh.md。
3. style:内联 CSS
style将对象形式的键值对以内联样式写入元素:
rect.attr('body/style', { fill: 'red', cursor: 'pointer' })实现(src/registry/attr/style.ts)调用Dom.css(elem, styles),同样以isPlainObject限定。
4. html:设置 innerHTML
html直接设置元素内部 HTML:
rect.attr('body/html', '<div class="inner">Hello</div>')实现(src/registry/attr/html.ts)为elem.innerHTML = \${html}``。
5. title:SVG 描述
title为目标元素追加<title>子元素,不影响渲染,只提供描述(悬停提示):
rect.attr('body/title', 'Description of the rectangle')实现(src/registry/attr/title.ts)只对 SVG 元素生效(qualify判断elem instanceof SVGElement),会复用已存在的<title>子元素并保持其为第一个子节点。
六、文本属性:text / textWrap / textPath / lineHeight / textVerticalAnchor
1. text:设置文本内容
仅适用于<text>元素。单行文本(不含'\n')直接写入<text>;多行文本则每行生成一个<tspan>:
rect.attr('label/text', 'Hello X6')实现(src/registry/attr/text.ts)还会读取textPath、textVerticalAnchor、lineHeight、annotations、eol、displayEmpty等关联属性,并基于JSON.stringify([text, options])做变更缓存,避免无谓的 DOM 重写。注意qualify逻辑:当textWrap为对象时,text属性不单独生效(交给textWrap统一处理)。
2. textWrap:自动换行文本
仅适用于<text>元素。与text不同,textWrap会自动断行,使文本完整包围在参考元素的包围盒内。属性值为对象:
text:文本内容width/height:调整可用空间。负值表示缩小(相当于给文本留内边距),正值表示放大,百分比表示参考元素宽(高)的比例ellipsis:设为true时,文本超出显示范围自动追加...breakWord:默认true(单词会被截断);设为false时不截断完整单词,文本可能超出宽度范围
textWrap: { text: 'lorem ipsum dolor sit amet consectetur adipiscing elit', width: -10, // 宽度减少 10px height: '50%', // 高度为参考元素高度的一半 ellipsis: true, // 文本超出显示范围时自动添加省略号 breakWord: true, // 是否截断单词 }源码(src/registry/attr/text.ts)先按width/height调整refBBox(百分比按比例、非正数在原尺寸上增减),再调用Dom.breakText依据字号、字重、字体、行高计算换行与省略号,最后复用text属性的set写入 DOM。可交互演示见仓库 examples/src/pages/shape 相关页面。
3. textPath:沿路径文本
仅适用于<text>元素:
- 值为字符串时,表示沿该 pathData 渲染文本
- 值为对象时,可通过
d指定路径,或通过selector引用节点/边中已定义的 SVGPathElement(支持 CSS 选择器与markup中定义的 selector);startOffset控制文本在路径上的位置,如'50%'表示位于路径 50% 处,20表示距路径起点 20px
rect.attr('label/textPath', { d: 'M 10 10 100 10', startOffset: '50%', })实现中selector会通过view.find(selector)查找路径节点,Dom.ensureId为其分配 id 后转为xlink:href引用(src/registry/attr/text.ts)。
4. lineHeight:行高
仅适用于<text>元素,指定文本行高(见 W3C 行高规范),实现会将其透传给文本渲染逻辑(src/registry/attr/text.ts)。
5. textVerticalAnchor:垂直锚点
仅适用于<text>元素,控制文本与y坐标的垂直对齐:
'top':文本顶部与y对齐'middle':文本中心与y对齐'bottom':文本底部与y对齐
七、边的属性:connection 与 atConnection 系列
以下属性仅对边(Edge)的<path>元素或边上的子元素生效,其qualify统一判断view.cell.isEdge()(src/registry/attr/connection.ts)。
1. connection:指定边路径渲染元素
仅适用于边的<path>元素。设为true时,边的 pathData 会写入该元素的d:
edge.attr('pathSelector', { connection: true, stroke: 'red', fill: 'none', })也支持带stubs与reverse的对象(src/registry/attr/connection.ts):
reverse: false(默认):stubs为正数,表示只渲染首尾两段。{ stubs: 20 }只渲染连接线最前与最后各 20pxstubs为负数,表示中间缺失段的长度。{ stubs: -20 }中间留 20px 缺口
reverse: true:stubs为正数,表示首尾不渲染的长度。{ stubs: 20 }首尾各 20px 不渲染stubs为负数,表示只渲染中间段。{ stubs: -20 }只渲染中间 20px
edge.attr('pathSelector', { connection: { stubs: -20, reverse: true }, })实现通过view.getConnection()获取连接路径,用divideAtLength按长度切割并拼接片段。
2. atConnectionLengthKeepGradient(别名 atConnectionLength)
将边中的指定元素移动到距边起点指定长度处,并自动旋转使其方向与边在该点的切线一致:
edge.attr('rectSelector', { atConnectionLengthKeepGradient: 30, // 也可写 atConnectionLength: 30 width: 10, height: 10, fill: 'red', })- 正值表示距边起点的偏移
- 负值表示距边终点的偏移
3. atConnectionLengthIgnoreGradient
同上移动到指定长度处,但忽略边的方向,不自动旋转:
edge.attr('rectSelector', { atConnectionLengthIgnoreGradient: 30, width: 10, height: 10, fill: 'red', })4. atConnectionRatioKeepGradient(别名 atConnectionRatio)
移动到边的比例位置[0, 1]处,并自动旋转对齐切线:
edge.attr('rectSelector', { atConnectionRatioKeepGradient: 0.5, // 也可写 atConnectionRatio: 0.5 width: 10, height: 10, fill: 'red', })5. atConnectionRatioIgnoreGradient
移动到比例位置[0, 1]处,忽略边的方向、不旋转:
edge.attr('rectSelector', { atConnectionRatioIgnoreGradient: 0.5, width: 10, height: 10, fill: 'red', })四个属性的底层实现共享atConnectionWrapper(src/registry/attr/connection.ts):通过view.getTangentAtLength/view.getTangentAtRatio获取切线,rotate: true时计算切线与水平轴的夹角并写入transform: translate(...) rotate(...),rotate: false时仅平移。别名atConnectionLength、atConnectionRatio分别指向 KeepGradient 版本(src/registry/attr/connection.ts)。
八、标记属性:sourceMarker / targetMarker / vertexMarker
三个属性都适用于所有<path>元素,用于在路径起点、终点、顶点处附加 SVG 标记(如箭头),并自动旋转使其与路径方向一致。
edge.attr('connection/sourceMarker', { tagName: 'circle', fill: '#666', stroke: '#333', r: 5, cx: 5, })实现(src/registry/attr/marker.ts)将标记定义写入marker-start/marker-end/marker-mid,值可以是字符串(预设标记名,如'block')或对象(name+args),通过markerRegistry解析后调用view.graph.defineMarker生成<defs>引用。targetMarker会额外叠加transform: 'rotate(180)'(src/registry/attr/marker.ts),因此对水平直线,起点向左的箭头在终点会自动向右。预设标记类型见 src/registry/marker 目录及文档 site/docs/api/registry/marker.en.md。
九、交互属性:magnet / port / event
1. magnet:可连接标记
magnet: true表示该元素可被连接,即连线过程中可作为起点或终点(类似连接桩):
rect.attr('body/magnet', true)2. port:连接桩 ID
为标记了magnet的元素指定端口 ID,边连接后该 ID 会保存到边的source/target中:
- 字符串:直接作为端口 ID
- 对象:使用对象
id属性的值作为端口 ID
实现(src/registry/attr/port.ts)在对象含id时返回port.id,否则原样返回字符串。完整的端口定义方式见 site/docs/tutorial/intermediate/port.zh.md。
3. event:自定义元素事件
为指定元素注册自定义事件名,之后可在 Graph 上绑定回调:
node.attr({ // 表示一个删除按钮,点击后删除节点 image: { event: 'node:delete', xlinkHref: 'trash.png', width: 20, height: 20, }, }) // 绑定事件回调,触发时删除节点 graph.on('node:delete', ({ view, e }) => { e.stopPropagation() view.cell.remove() })十、XML/XLink 属性别名
X6 将带命名空间的原生属性包装为易于书写的别名(定义见 src/registry/attr/raw.ts):
| 别名 | 原生属性 |
|---|---|
xlinkHref | xlink:href |
xlinkShow | xlink:show |
xlinkType | xlink:type |
xlinkTitle | xlink:title |
xlinkArcrole | xlink:arcrole |
xmlSpace | xml:space |
xmlBase | xml:base |
xmlLang | xml:lang |
例如:
node.attr({ image: { xlinkHref: 'xxx.png', }, })十一、自定义 Attribute:registry 注册机制
除内置属性外,X6 通过registry对象提供register与unregister两个方法,用于注册和删除特殊属性,同时挂载为Graph.registerAttr/Graph.unregisterAttr两个静态方法(src/graph/graph.ts),推荐使用静态方法。
1. 注册与注销 API
register(entities: { [name: string]: Definition }, force?: boolean): void register(name: string, entity: Definition, force?: boolean): Definition unregister(name: string): Definition | nullforce为true时可覆盖已注册的同名属性- 底层仓库为
attrRegistry(src/registry/attr/index.ts),内置属性在模块加载时即注册完成
2. Definition 的多种形式
内部实现会把特殊属性转换为浏览器可识别的属性,因此属性值理论上可为任意类型,属性定义也支持多种形式:
字符串:原生属性别名
例如xlinkHref的定义实质是为xlink:href起一个更易写的别名:
// 定义 Graph.registerAttr('xlinkHref', 'xlink:href') // 使用 node.attr({ image: { xlinkHref: 'xxx.png', }, })qualify:资格函数
在介绍set/offset/position前,需要先理解资格函数qualify——只有通过它判断的属性值才会被特殊属性处理。例如stroke只在值为Object类型时(使用渐变色填充边框)才作为特殊属性:
type QualifyFunction = ( this: CellView, // 节点/边的视图 val: ComplexAttrValue, // 当前属性值 options: { elem: Element // 应用当前属性的元素 attrs: ComplexAttrs // 应用在该元素上的属性键值对 cell: Cell // 节点/边 view: CellView // 节点/边的视图 }, ) => boolean // 返回 true 表示通过资格判断对应类型定义见 src/registry/attr/index.ts。内置stroke的实现即依赖qualify:
export const stroke: Attr.Definition = { qualify(val) { // 仅当属性值为对象时才触发特殊属性处理逻辑 return ObjectExt.isPlainObject(val) }, set(stroke, { view }) { return `url(#${view.graph.defineGradient(stroke as any)})` }, }set:属性设置定义(最常用)
export interface SetDefinition { qualify?: QualifyFunction set: ( this: CellView, val: ComplexAttrValue, options: { refBBox: Rectangle // 参考元素的包围盒;无参考元素时为节点位置与尺寸构成的矩形 elem: Element // 应用当前属性的元素 attrs: ComplexAttrs cell: Cell view: CellView }, ) => SimpleAttrValue | SimpleAttrs | void }set的三种返回方式分别对应三种使用场景:
(1)返回string或number:作为该特殊属性的最终值,通常用于扩展原生属性定义。如stroke/fill支持渐变填充:
export const stroke: Attr.SetDefinition = { qualify: ObjectExt.isPlainObject, set(stroke, { view }) { // 返回字符串,返回值作为 stroke 属性的值 return `url(#${view.graph.defineGradient(stroke as any)})` }, }(2)返回简单对象:作为属性键值对应用到对应元素。如sourceMarker/targetMarker:
export const sourceMarker: Attr.SetDefinition = { qualify: ObjectExt.isPlainObject, set(marker: string | JSONObject, { view, attrs }) { // 返回简单对象,返回值作为属性键值对应用到对应元素 return { 'marker-start': createMarker(marker, view, attrs), } }, }(3)无返回值:在方法内部自行完成属性赋值。如html:
export const html: Attr.Definition = { set(html, { view, elem }) { // 无返回值,属性赋值在方法内部完成 view.$(elem).html(`${html}`) }, }对应源码见 src/registry/attr/html.ts(当前实现为elem.innerHTML)。
offset:偏移定义
export interface OffsetDefinition { qualify?: QualifyFunction offset: ( this: CellView, val: ComplexAttrValue, options: { refBBox: Rectangle elem: Element attrs: ComplexAttrs cell: Cell view: CellView }, ) => Point.PointLike // 返回绝对偏移 }返回Point.PointLike表示x/y方向的绝对偏移,如xAlign:
export const xAlign: Attr.OffsetDefinition = { offset(alignment, { refBBox }) { // ...计算逻辑 // 返回 x 轴上的绝对偏移 return { x, y: 0 } }, }对应源码见 src/registry/attr/align.ts,resetOffset也是典型的offset型定义。
position:定位定义
export interface PositionDefinition { qualify?: QualifyFunction offset: ( this: CellView, val: ComplexAttrValue, options: { refBBox: Rectangle elem: Element attrs: ComplexAttrs cell: Cell view: CellView }, ) => Point.PointLike // 返回相对于节点的绝对定位坐标 }返回相对于节点的绝对定位坐标,如refX、refDx:
export const refX: Attr.PositionDefinition = { position(val, { refBBox }) { // ...计算逻辑 // 返回 x 轴上的绝对定位 return { x, y: 0 } }, }对应源码见 src/registry/attr/ref.ts。类型定义(AttrPositionFunction、AttrOffsetFunction、AttrSetFunction)统一位于 src/registry/attr/index.ts。
3. 完整示例:注册一个自定义属性
综合上述概念,注册一个"将元素宽度设为参考元素宽度的一半"的自定义属性:
import { Graph } from '@antv/x6' Graph.registerAttr( 'halfWidth', { set(val, { refBBox }) { return { width: refBBox.width / 2 } }, }, true, // force:允许覆盖 ) // 使用 graph.addNode({ markup: [ { tagName: 'rect', selector: 'body' }, { tagName: 'rect', selector: 'child' }, ], attrs: { body: { width: 200, height: 100 }, child: { ref: 'body', halfWidth: true, height: 40, fill: 'orange' }, }, })十二、小结
X6 的 Attributes 体系由三层构成:
- 原生属性层:
raw中定义的大量 SVG/XML 原生属性别名(src/registry/attr/raw.ts),提供更简洁的书写方式; - 内置特殊属性层:
ref系列定位、path/points适配、text/textWrap文本排版、connection/atConnection*边路径、sourceMarker/targetMarker标记、magnet/port/event交互等,全部注册在attrRegistry中(src/registry/attr/index.ts); - 自定义属性层:通过
Graph.registerAttr结合qualify+set/offset/position三种定义形式,将任意计算逻辑接入渲染管线。
掌握refBBox与qualify两个核心概念,即可理解绝大多数内置属性的行为,并能自由扩展出符合业务需求的自定义属性。相关测试(tests/registry/attr/ref.spec.ts)对refX/refDx/refWidth/refR*的计算规则做了精确验证,可作为自定义属性开发时的参考样板。
【免费下载链接】X6🚀 JavaScript diagramming library that uses SVG and HTML for rendering.项目地址: https://gitcode.com/GitHub_Trending/x6/X6
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考