尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

X6 元素属性(Element Attributes)完全指南:ref 相对定位、沿边定位与箭头标记

X6 元素属性(Element Attributes)完全指南:ref 相对定位、沿边定位与箭头标记 X6 元素属性Element Attributes完全指南ref 相对定位、沿边定位与箭头标记【免费下载链接】X6 JavaScript diagramming library that uses SVG and HTML for rendering.项目地址: https://gitcode.com/GitHub_Trending/x6/X6X6 是基于 SVG 与 HTML 渲染的 JavaScript 图编辑引擎其强大的可定制性很大程度源于一套位于原生 SVG 属性之上的**特殊属性Special Attributes**体系。本文围绕 元素属性文档 展开系统讲解ref系列相对尺寸/位置属性、相对子元素定位、沿边长度的相对定位connection/atConnectionLength/atConnectionRatio以及sourceMarker/targetMarker箭头属性并结合 ref 属性实现、connection 属性实现、marker 属性实现 与对应单元测试帮助你在定制节点、边和标签时写出精确、可复用且高性能的属性配置。特殊属性与原生 SVG 属性的关系SVG 原生属性的参考资料很多例如 MDN 的 SVG 属性参考但 X6 的价值在于特殊属性它们在原生属性之上提供了更灵活、更强大的表达能力。在 X6 中应用属性时遵循两条路径原生属性被直接传递给对应的 SVG 元素例如fill、stroke、strokeWidth。特殊属性先经过 X6 的属性注册表Attribute Registry加工处理转换为浏览器能识别的原生属性后再传递给对应的元素。例如refWidth最终会被换算成原生widthatConnectionRatio最终会被换算成transform: translate(...) rotate(...)。从源码看全部内置特殊属性的注册入口位于 attr 注册表定义它把原生属性raw与特殊属性attrs即main.ts中导出的ref、fill、stroke、text、connection、marker等模块合并后注册进attrRegistry。因此凡是文档中出现的refX、atConnectionRatio等名称都可以在src/registry/attr/目录下找到对应的实现文件。相对大小和相对位置ref 系列属性在定制节点或边时让子元素相对宿主节点/边的大小进行排布是最高频的需求。X6 提供了一系列以ref为前缀的特殊属性核心优势在于这些属性的计算基于节点/边的数据大小Data Size完全不依赖浏览器对 SVG 元素的 bbox 测量因此不存在任何性能问题。X6 提供的相对属性清单属性作用refWidth/refHeight设置元素的宽度 / 高度refX/refY设置元素的位置左上角坐标refCx/refCy设置ellipse和circle的中心位置refRx/refRy设置ellipse的 x / y 轴半径refR设置circle的半径refRCircumscribed以参考 bbox 的对角线长度为基准计算半径refDx/refDy相对参考元素右下角corner的偏移refD/refPoints相对缩放并重置偏移的路径d/ 折线points相对属性的取值规则以 ref.ts 源码 中的注释为基准refX/refY的取值分三种语义百分比含%后缀按参考 bbox 的宽/高比例计算例如refX: 50%表示 x 坐标为参考宽度的 50%。[0, 1]区间的纯数字同样按比例处理例如refX: 0.5等价于refX: 50%。其他数值负数或大于 1按绝对像素偏移处理例如refX: 20表示在参考原点基础上右移 20pxrefX: -10表示左移 10px。refWidth/refHeight/refRx/refRy/refCx/refCy的换算逻辑由setWrapperref.ts实现规则为百分比或[0, 1]区间数值值 × 参考 bbox 的对应维度其他数值值 参考 bbox 的对应维度并保证不小于 0例如refWidth: -20表示宽度比参考宽度小 20px。这一点在单元测试 ref.spec.ts 中得到验证以Rectangle(100, 100, 200, 150)为参考 bboxrefX.position(50%)得到x 200refX.position(20)得到x 120refWidth.set(50%)得到width 100refWidth.set(-20)得到width 180。综合示例相对节点排布多个图形下面的例子定义了一个红色椭圆e、一个绿色矩形r、一个蓝色圆形c以及一个表示节点大小的矩形outline全部使用相对属性进行排布graph.addNode({ shape: custom-rect, x: 160, y: 100, width: 280, height: 120, attrs: { e: { refRx: 50%, // 椭圆 x 轴半径为宽度的一半 refRy: 25%, // 椭圆 y 轴半径为高度的 1/4 refCx: 50%, // 椭圆中心 x 坐标为宽度一半即位于节点宽度的中心 refCy: 0, // 椭圆中心 y 坐标为 0 refX: -50%, // 向左偏移宽度一半 refY: 25%, // 向下偏移高度的 1/4 }, r: { refX: 100%, // 矩形 x 轴坐标位于节点右下角 refY: 100%, // 矩形 y 轴坐标位于节点右下角 refWidth: 50%, // 矩形宽度为节点宽的一半 refHeight: 50%, // 矩形高度为节点高度的一半 x: -10, // 向左偏移 10px y: -10, // 向上偏移 10px }, c: { refRCircumscribed: 50%, // 圆半径为节点宽度/高度中较大的那个值的一半 refCx: 50%, // 圆中心 x 坐标位于节点中心 refCy: 50%, // 圆中心 y 坐标位于节点中心 }, }, })值得注意的细节refX: -50%与refCx: 50%组合使用可以让椭圆既“居中偏移一半宽度”又让中心落在指定位置实现灵活的对齐效果。refRCircumscribed的计算依据是参考 bbox 的对角线长度Math.sqrt(height² width²)详见 ref.ts 中的实现单元测试 ref.spec.ts 用Rectangle(100, 100, 200, 150)验证了50%会得到对角线的一半。与refRCircumscribed对应的是refRInscribed别名refR它取参考 bbox 宽高中较小的维度来计算内切圆半径见 ref.spec.ts。相对属性可以与原生属性如x: -10、y: -10混合使用实现“相对定位 绝对微调”的效果。相对子元素通过ref指定参考系默认情况下上述属性都相对于节点自身的大小进行计算。X6 还提供了ref属性传入一个子元素选择器后所有计算都将相对于ref指向的那个子元素进行从而实现相对于子元素的大小与位置。graph.addNode({ shape: custom-text, x: 320, y: 160, width: 280, height: 120, attrs: { label: { text: H, }, e: { ref: label, refRx: 50%, refRy: 25%, refCx: 50%, refCy: 0, refX: -50%, refY: 25%, }, r: { ref: label, refX: 100%, refY: 100%, x: -10, y: -10, refWidth: 50%, refHeight: 50%, }, c: { ref: label, refRCircumscribed: 50%, }, }, })::: warning 注意 设置ref后所有计算都会依赖子元素在浏览器中的 bbox 测量性能会比直接相对节点计算的方式要慢。因此在满足需求的前提下优先使用默认的“相对节点”方式仅在确实需要以某个子元素为参考时才使用ref。 :::从源码层面看ref本身是一个“空”属性定义ref.ts它不会直接设置到元素上而是作为上下文传入属性处理函数影响refBBox的取值。这一点在AttrOptions类型中体现为refBBox: Rectangle字段index.ts。沿边长度的相对位置connection 与 atConnection 系列对于边Edge及其子元素X6 提供了一组专门沿边进行定位的特殊属性。它们的共同点是最终会基于边的路径path计算位置并可能自动旋转元素使其贴合边的斜率。属性作用connection仅适用于边的path元素设为true时表示在该元素上渲染边本身atConnectionLengthatConnectionLengthKeepGradient的简称移动到指定偏移量px处并自动旋转以匹配该处边的斜率atConnectionRatioatConnectionRatioKeepGradient的简称移动到指定比例[0, 1]处并自动旋转以匹配该处边的斜率atConnectionLengthIgnoreGradient移动到指定偏移量处忽略边的斜率不自动旋转atConnectionRatioIgnoreGradient移动到指定比例[0, 1]处忽略边的斜率不自动旋转上述 KeepGradient 与 IgnoreGradient 的实现都在 connection.ts 中它们共用atConnectionWrapper工具函数通过view.getTangentAtLength()或view.getTangentAtRatio()取得边上的切点rotate: true时计算切向量与水平向量的夹角并拼装transform: translate(x,y) rotate(angle)rotate: false时仅生成translate详见 connection.ts 的实现。示例一在边上放置图形符号graph.addEdge({ shape: custom-edge, source: { x: 100, y: 60 }, target: { x: 500, y: 60 }, vertices: [{ x: 300, y: 160 }], attrs: { symbol: { atConnectionRatio: 0.75, // 沿边长度方向距离起点 75% 位置处 }, arrowhead: { atConnectionLength: 100, // 沿边长度方向距离起点 100px 位置处 }, }, })示例二沿边排布多种标签atConnectionRatio与atConnectionLength支持负数与混合偏移非常适合在边上同时放置相对比例标签、绝对距离标签和带偏移的标签。下面的例子展示了完整用法graph.addEdge({ shape: custom-edge, source: { x: 100, y: 60 }, target: { x: 500, y: 60 }, vertices: [{ x: 300, y: 160 }], attrs: { relativeLabel: { text: 0.25, atConnectionRatio: 0.25, }, relativeLabelBody: { atConnectionRatio: 0.25, }, absoluteLabel: { text: 150, atConnectionLength: 150, }, absoluteLabelBody: { atConnectionLength: 150, }, absoluteReverseLabel: { text: -100, atConnectionLength: -100, }, absoluteReverseLabelBody: { atConnectionLength: -100, }, offsetLabelPositive: { y: 40, text: keepGradient: 0,40, atConnectionRatio: 0.66, }, offsetLabelPositiveBody: { x: -60, // 0 -60 y: 30, // 40 -10 atConnectionRatio: 0.66, }, offsetLabelNegative: { y: -40, text: keepGradient: 0,-40, atConnectionRatio: 0.66, }, offsetLabelNegativeBody: { x: -60, // 0 -60 y: -50, // -40 -10 atConnectionRatio: 0.66, }, offsetLabelAbsolute: { x: -40, y: 80, text: ignoreGradient: -40,80, atConnectionRatioIgnoreGradient: 0.66, }, offsetLabelAbsoluteBody: { x: -110, // -40 -70 y: 70, // 80 -10 atConnectionRatioIgnoreGradient: 0.66, }, }, })这个示例揭示了几条关键规律标签与标签体Body共享同一个定位属性relativeLabel与relativeLabelBody使用相同的atConnectionRatio: 0.25保证背景与文字严格对齐。atConnectionLength支持负值-100表示沿边起点方向的反向 100px 处放置元素。定位属性与原生x/y偏移可叠加atConnectionRatio先把元素放到边上的 0.66 比例处随后x/y再基于该点做像素级偏移如offsetLabelAbsoluteBody中x: -40 -70 -110。KeepGradient 与 IgnoreGradient 的区别前者自动旋转贴合边斜率适合文字/图形沿边排布后者保持水平适合需要恒定方向的标注。connection 属性与边的渲染connection属性只对边的path元素生效其qualify函数要求view.cell.isEdge()见 connection.ts。当设为true时边的路径会渲染在该元素上。它还支持两个附加选项reverse布尔值是否反向截取路径stubs数值沿边的起始端与末端各截断指定像素负值表示按总长度反推用于实现“边两端留白”的效果。核心截断逻辑通过path.divideAtLength(offset)完成详见 connection.ts 中的实现。使用箭头sourceMarker 与 targetMarker边的起始与终止箭头分别通过sourceMarker与targetMarker两个特殊属性指定。其底层实现位于 marker.tsX6 会把配置解析为箭头定义通过view.graph.defineMarker(options)生成一个marker定义并最终把marker-start/marker-end的url(#...)引用写回path元素targetMarker额外附加transform: rotate(180)以修正方向。最简单的用法是直接使用内置箭头名称edge.attr({ line: { sourceMarker: block, // 内置实心箭头 targetMarker: { name: ellipse, rx: 10, // 椭圆箭头 x 轴半径 ry: 6, // 椭圆箭头 y 轴半径 }, }, })配置形式支持两种字符串等价于{ name: block }即使用内置箭头名。对象通过name指定箭头类型其余键值作为箭头参数fill与stroke默认继承自边但可显式覆盖。X6 内置的箭头类型包括block、classic、diamond、cross、async、path、circle、circlePlus、ellipse等各自的参数如size、width、height、offset、open、r、rx/ry、d、offsetX/offsetY等与默认值详见 箭头教程。例如path类型支持直接传入d作为path的路径数据并通过Util.normalizeMarker(d)标准化坐标以避免箭头位置偏移。自定义箭头元素除了内置箭头还可以通过tagName指定任意 SVG 元素来渲染箭头其余配置项会作为该元素的属性写入edge.attr({ line: { sourceMarker: { tagName: path, d: M 20 -10 0 0 20 10 Z, }, targetMarker: { tagName: path, fill: yellow, // 自定义填充色 stroke: green, // 自定义描边色 strokeWidth: 2, d: M 20 -10 0 0 20 10 Z, }, }, })自定义时只需定义一个**指向原点向左**的箭头X6 会自动计算箭头的朝向。对于circle/ellipse类型的箭头建议把cx设为半径值以避免箭头超出边的边界对于其他元素则可用y做垂直居中微调。注册自定义箭头当某个箭头需要多场景复用时可以通过Graph.registerMarker将其注册为“内置箭头”Graph.registerMarker(name: string, factory: Factory, overwrite?: boolean)name箭头名称factory箭头工厂方法接收配置参数args返回{ tagName, ...attrs, children? }结构overwrite遇到重名时是否覆盖默认false重名会抛错。例如注册一个语义化的图片箭头interface ImageMarkerArgs extends Attr.SimpleAttrs { imageUrl: string imageWidth?: number imageHeight?: number } Graph.registerMarker(image, (args: ImageMarkerArgs) { const { imageUrl, imageWidth, imageHeight, ...attrs } args return { ...attrs, // 非特殊参数原样透传 tagName: image, // 使用 image 元素渲染箭头 width: imageWidth, height: imageHeight, xlink:href: imageUrl, } })注册后即可像内置箭头一样使用edge.attr({ line: { sourceMarker: { name: image, imageUrl: http://cdn3.iconfinder.com/data/icons/49handdrawing/24x24/left.png, imageWidth: 24, imageHeight: 24, y: -12, }, targetMarker: { name: image, imageUrl: http://cdn3.iconfinder.com/data/icons/49handdrawing/24x24/left.png, imageWidth: 24, imageHeight: 24, y: -12, }, }, })工厂方法注册的完整说明、内置箭头参数表以及Util.normalizeMarker的使用方式请继续阅读 箭头Marker教程。小结如何选用合适的特殊属性场景推荐属性性能特征子元素相对节点排布大小、位置、圆心、半径refWidth/refHeight/refX/refY/refCx/refCy/refRx/refRy/refR基于数据大小计算无浏览器 bbox 测量性能最优相对某个子元素排布ref: selector 上述ref*属性依赖子元素 bbox 测量性能略慢在边上放置符号 / 标签按比例或按像素atConnectionRatio/atConnectionLength/atConnectionRatioIgnoreGradient/atConnectionLengthIgnoreGradient基于边路径切线计算边两端留白 / 指定渲染路径connection: { reverse, stubs }基于边路径分割边起始 / 终止箭头sourceMarker/targetMarker通过marker引用可复用这套特殊属性体系把“相对布局”从繁琐的手工坐标换算中解放出来基于数据大小的ref*属性保证了高频定制场景下的性能atConnection*系列让边标签的定位与朝向完全自动化sourceMarker/targetMarker则提供了从内置箭头到完全自定义箭头的完整梯度。理解它们的取值规则与底层实现可继续阅读 ref.ts、connection.ts、marker.ts 及其单元测试 ref.spec.ts你就能在 X6 中构建出布局精确、任意缩放、跨场景复用的高质量图形节点与边。【免费下载链接】X6 JavaScript diagramming library that uses SVG and HTML for rendering.项目地址: https://gitcode.com/GitHub_Trending/x6/X6创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表