JavaScript is required

线条形态、锚点与自定义箭头

这个示例构建了一个中心布局参考图,一次性对比多种内置连线行为,包括线形、线宽、连接锚点、箭头显示、文字位置,以及基于 marker 的自定义箭头。它保持图数据静态,加入基于 CSS 的标签样式和暗色主题切换,并提供画布交互模式与图片导出的辅助控件。

在一个参考图中展示线条形状、连接点锚位与自定义箭头

这个示例构建了什么

该示例构建了一个全高的 relation-graph 画布,用作线条渲染的可视化参考板。查看者会看到一个预先排好的中心布局图,其中包含通用的字母节点,以及大量带注释的连接线,在同一画面中对比线宽、线条形状、连接点位置、箭头可见性、文本放置方式和颜色处理。

用户不会编辑图结构,但可以查看这个预设的对照面板、切换深色主题、打开悬浮设置面板、更改滚轮和拖拽行为,并将画布导出为图片。这个示例的重点在于覆盖面广:它会同时展示许多内置线条选项,然后再通过 CSS 标签样式和自定义 SVG 箭头标记对其进行扩展。

数据是如何组织的

图数据在 initializeGraph() 中以内联方式声明为一个 RGJsonData 对象,包含 rootId: 'a'、22 个节点和 28 条线。setJsonData() 之前没有预处理步骤。这个示例直接在代码中构造最终数据集,并按原样加载。

大部分语义都放在 lines 数组中。每条边记录都可以携带自己的 lineShapelineWidthcolorfontColorshowStartArrowshowEndArrowstartMarkerIdendMarkerIdfromJunctionPointtoJunctionPointuseTextOnPathclassName。节点数据大多只是用于承载对比布局的占位内容,不过也有少数节点会覆盖尺寸、形状或固定位置。在真实产品中,同样的数据结构可以表示工作流分支、集成路径、血缘边、依赖连接,或需要为每条连接单独指定渲染规则的带注释网络路径。

如何使用 relation-graph

index.tsxRGProvider 包裹这个 demo,MyGraph.tsx 则通过 RGHooks.useGraphInstance() 获取当前图实例。图使用内置的 center 布局,并显式设置了 levelGaps、圆形节点默认值、60x60 的默认节点尺寸,以及白色默认节点颜色。

图会在挂载时加载一次。initializeGraph() 会调用 setJsonData(),然后执行 moveToCenter()zoomToFit(),让这个预设画廊在初始状态下就完整可见。这个示例没有使用节点插槽、线条插槽、画布插槽或编辑 API,而是把大部分变化都放进内置的线条元数据字段中。

在内置渲染器之上,这个示例还叠加了两种非数据层面的自定义方式。MyLineArrowDefine 会注入 SVG <marker> 定义,让选中的线条可以通过 marker id 使用自定义的起始和结束箭头。局部 SCSS 文件则通过每条线的类名(如 .my-text-bg-001.my-text-bg-004)来样式化内置线条标签,而当勾选框启用时,外层包裹类会切换为 .my-graph-dark,从而重新定义画布、线条文本、节点和工具栏的样式。

悬浮帮助窗口来自一个共享的本地组件,但它也是示例渲染结果的一部分。这个辅助组件会使用 RGHooks.useGraphStore() 读取当前交互模式,使用 graphInstance.setOptions() 切换滚轮和拖拽行为,并通过 prepareForImageGeneration()restoreAfterImageGeneration() 将当前图视图导出为图片。

关键交互

最显眼的交互,是悬浮说明窗口中的本地深色主题切换。它不会更改图数据,但会改变同一组线条样式在浅色和深色背景下的视觉表现。

悬浮帮助窗口中的设置面板为图增加了操作层控制。用户可以把滚轮事件切换为滚动或缩放,把画布拖拽切换为框选或移动,也可以下载当前画布的图片。这些控件影响的是查看环境,而不是图结构本身。

说明窗口本身也可以通过其内置辅助控件进行拖拽、最小化和重新打开。节点点击和线条点击事件虽然已绑定,但它们只是在控制台打印对象,因此更像是调试或查看用的检查钩子,而不是有实际业务意义的终端用户流程。

关键代码片段

这段代码表明,整个场景使用了 relation-graph 的内置中心布局和默认节点选项。

const graphOptions: RGOptions = {
    defaultNodeShape: RGNodeShape.circle,
    defaultNodeBorderWidth: 1,
    defaultNodeWidth: 60,
    defaultNodeHeight: 60,
    defaultNodeColor: '#ffffff',
    layout: {
        layoutName: 'center',
        levelGaps: [300, 200, 200]
    }
};

这段代码表明,大部分可见差异都是直接在每条线记录中表达的。

{ id: 'line-2', from: 'a', to: 'c', text: 'useTextOnPath Line 1', useTextOnPath: true, fontSize: 10, color: 'rgba(30, 144, 255, 1)' },
{ id: 'line-5', from: 'a', to: 'd', text: 'Thick Line', lineWidth: 3 },
{ id: 'line-6', from: 'a', to: 'e', text: 'Hide Arrow', showEndArrow: false },
{ id: 'line-11', from: 'a', to: 'd', text: 'Polyline', lineShape: RGLineShape.StandardOrthogonal, fromJunctionPoint: RGJunctionPoint.left, toJunctionPoint: RGJunctionPoint.top, className: 'my-text-bg-001' },

这段代码表明,部分线条会附加自定义 marker id,而不只是依赖默认箭头。

{ id: 'line-18', from: 'c', to: 'm1', text: 'RGJunctionPoint.bottom', endMarkerId: 'my-arrow-001', startMarkerId: 'my-arrow-001-start', showStartArrow: true, lineShape: RGLineShape.StandardCurve, toJunctionPoint: RGJunctionPoint.bottom, lineWidth: 6, color: '#00ced1' },
{ id: 'line-19', from: 'c', to: 'm2', text: 'Custom Line Arrow', endMarkerId: 'my-arrow-001', startMarkerId: 'my-arrow-001-start', showStartArrow: true, lineShape: RGLineShape.StandardCurve, fromJunctionPoint: RGJunctionPoint.border, lineWidth: 7, color: '#00ced1' },

这段代码展示了隐藏的 SVG 定义,它们使这些自定义 marker id 可供图渲染器使用。

<svg className="absolute opacity-0" xmlns="http://www.w3.org/2000/svg">
    <defs>
        <marker id="my-arrow-001" markerWidth="80" markerHeight="80" refX="2" refY="3"
            markerUnits="userSpaceOnUse" orient="auto" viewBox="0 0 12 12">
            <path d="M 4 6, V 0, L 0 3, Z" style={{ fill: 'context-stroke' }}></path>
        </marker>
        <marker id="my-arrow-001-start" markerWidth="80" markerHeight="80" refX="2" refY="3"
            markerUnits="userSpaceOnUse" orient="auto-start-reverse" viewBox="0 0 12 12">
            <path d="M 4 6, V 0, L 0 3, Z" style={{ fill: 'context-stroke' }}></path>
        </marker>
    </defs>
</svg>

这段代码表明,标签展示也通过 CSS 类和深色主题包裹类进行了自定义。

.my-text-bg-003 {
    .rg-line-label {
        background-color: rgb(13, 179, 1);
        color: #ffffff;
        border-radius: 20px;
        padding: 3px 10px;
    }
}

.my-graph-dark {
    .relation-graph {
        .rg-map {
            background-color: #333;
        }
    }
}

这段代码表明,共享帮助窗口会通过 relation-graph API 切换图的交互模式。

<SettingRow
    label="Wheel Event:"
    options={[
        { label: 'Scroll', value: 'scroll' },
        { label: 'Zoom', value: 'zoom' },
        { label: 'None', value: 'none' },
    ]}
    value={wheelMode}
    onChange={(newValue: string) => { graphInstance.setOptions({ wheelEventAction: newValue }); }}
/>

这个示例的独特之处

从这组对比数据可以看出,这个示例的突出之处在于:一个静态的中心布局图把许多内置线条行为集中展示在同一个地方,包括混合线条形状、显式连接点锚位、宽度变化、隐藏或反向箭头、沿路径排布的文本标签、自定义 marker id,以及按线条分类的标签类。它的独特价值不在于某一项技巧是独有的,而在于它把这么多能力组合进了同一个预设参考板中。

advanced-line-usage 相比,这个示例更宽、更密。两个 demo 都使用内联数据,并且点击处理器都只输出到控制台,但 line 增加了更多可见变体、自定义 SVG marker 定义、深色主题切换,以及悬浮工具窗口。与 custom-line-style 相比,区别在于实现方式:custom-line-style 强调运行时的批量重设样式,而这个示例则从一开始就通过每条边的数据和 CSS 类,同时展示大部分变化。

line-style1layout-center 相比,这个示例较少强调某一种狭义的预设线条族或整图级展示控制,而更强调把一个预设数据集当作广义的边行为展示板来使用。当团队想先对比 relation-graph 的内置线条能力,再决定是否进入自定义线条插槽或完整编辑器时,它会是一个很强的起点。

这种模式还适用于哪里

这种模式很适合迁移到那些“连接本身承载多重含义”的产品场景中。团队可以把它改造成用于工作流分支、依赖方向、网络路由、集成连接或血缘路径的 QA 面板,让每条边通过不同的宽度、箭头、锚位或标签规则来表达状态或语义。

它也适合作为内部文档。在设计自定义渲染器之前,团队可以先构建一个像这样紧凑的参考图,用来判断哪些需求已经被 relation-graph 的内置线条字段、CSS 主题能力和注入式 SVG marker 所覆盖。在培训材料中,同样的结构也可以作为可复用的边配置选项可视化目录。