JavaScript is required

线条动画预设集

这个示例渲染一棵左到右 relation-graph 树,并将其变为 12 种动画边样式的展示集。它在运行时批量更新内置连线的 `className`,使用 SCSS 重绘原生线条表面,并注入 SVG 滤镜定义以支持基于滤镜的预设。

由预设驱动的动画图谱连线

这个示例构建了什么

这个示例在深色画布上构建了一个从左到右展开的 relation graph 树,并把连线变成一个动画效果展示集。节点保持小巧并聚焦于图标,而连线则通过十二种可选预设承担了大部分视觉表现。

用户可以在一个浮动控制窗口中切换基础、管线式以及基于 SVG 过滤器的连线样式。还可以拖动或最小化该窗口,打开一个共享设置浮层来修改滚轮和拖拽行为,并将图谱导出为图片。这个 demo 的重点在于,它通过重设 relation-graph 内置连线表面的样式来实现这一切,而不是替换连线渲染器。

数据是如何组织的

图谱数据在 initializeGraph() 内以内联方式组装为一个静态 RGJsonData 对象。它包含一个 rootId、一个带有 idtextdata.iconnodes 数组,以及一个显式包含 idfromtotext 字段的 lines 数组。

在调用 setJsonData() 之前没有做结构预处理。唯一有实际意义的转换是图标查找层:node.data.icon 会在 IconMapper 中映射为一个 Lucide 组件,而所选的连线预设会在加载后通过修改每一条已有连线来应用。在真实应用中,同样的数据形状可以表示服务依赖、生产阶段、供应路线或审批流,此时拓扑结构保持稳定,但视觉状态需要在运行时切换。

relation-graph 是如何使用的

index.tsxRGProvider 包裹整个示例,MyGraph.tsx 则以一个从左侧生长的树形布局渲染 RelationGraph。这些选项固定了视觉外壳:圆形节点、曲线连线、左右连接点、明确的树形横向和纵向间距、透明节点主体,以及一个位于右下角的横向工具栏。

该示例使用 RGHooks.useGraphInstance() 作为运行时控制接口。这个实例负责加载静态 JSON、将图谱居中、缩放以适配视口、通过 getLines() 读取当前连线、通过 updateLine() 重写它们,并通过 prepareForImageGeneration()restoreAfterImageGeneration() 支持导出。共享设置浮层使用 RGHooks.useGraphStore() 读取当前的滚轮和拖拽模式,然后调用 setOptions() 进行切换。

自定义渲染钩子是 RGSlotOnNode。这个示例没有替换边,而是保留 relation-graph 原生的连线渲染器,只用基于图标的圆形节点主体覆盖节点内容。样式工作放在 SCSS 中完成:.rg-map.rg-toolbar 设定深色外壳,.rg-node-peel.rg-node-checked.rg-line-peel.rg-line-checked 调整选中状态,而十二个 .my-line-class-xx 选择器会重新定义 .rg-line.rg-line-bg.rg-line-label.rg-line-text 的样式。MySvgFilters 注入了由过滤器驱动的预设所需的 rough-paperelectric-glitchgooey-plasma 过滤器定义,而像 current-animation-is-10 这样的包装类则让节点强调色与当前激活的连线样式保持一致。

关键交互

  • 在任意 SimpleUISelect 中选择一个预设,都会重写每条连线的 className 和可见文本,因此整个图谱会一次性切换样式。
  • 初始加载本身也是交互式的:在 setJsonData() 之后,图谱会先居中、适配视口,然后立即切换到预设 10,因此 demo 打开时就是完整样式状态。
  • 浮动控制窗口可以通过标题栏拖动、最小化,并在不影响图谱本身的情况下重新打开。
  • 设置浮层会在 scrollzoomnone 之间切换 wheelEventAction,并在 selectionmovenone 之间切换 dragEventAction
  • 导出操作会先准备图谱 DOM 以供捕获,然后通过 modern-screenshot 将其渲染为 Blob,下载图片,最后再恢复图谱状态。

关键代码片段

下面这段代码表明,该 demo 依赖的是 relation-graph 内置的布局和渲染器设置,而不是自定义几何绘制。

const graphOptions: RGOptions = {
    defaultLineColor: 'rgba(255, 255, 255, 0.6)',
    defaultNodeColor: 'transparent',
    defaultNodeShape: RGNodeShape.circle,
    defaultLineShape: RGLineShape.StandardCurve,
    defaultJunctionPoint: RGJunctionPoint.lr,
    layout: {
        layoutName: 'tree',
        from: 'left',
        treeNodeGapH: 310,
        treeNodeGapV: 70
    }
};

下面这段代码展示了:一个固定数据集只加载一次,然后在图谱已经显示之后再应用样式。

const myJsonData: RGJsonData = {
    rootId: 'a',
    nodes: [
        // node records omitted
    ],
    lines: [
        // line records omitted
    ]
};

await graphInstance.setJsonData(myJsonData);
graphInstance.moveToCenter();
graphInstance.zoomToFit();
changeAllLineClassName('10');

下面这段代码是核心技巧:它通过 graph instance API 批量更新所有现有连线。

const changeAllLineClassName = (newClassName: string) => {
    setLineAnimation(newClassName);
    const allLines = graphInstance.getLines();
    allLines.forEach(line => {
        graphInstance.updateLine(line.id, {
            className: `my-line-class-${newClassName}`,
            text: `className=${newClassName}`
        });
    });
};

下面这段代码展示了,该示例通过 slot 自定义节点,同时将边渲染继续交给 relation-graph 本身。

<RGSlotOnNode>
    {({ node }: RGNodeSlotProps) => {
        const iconName = node.data?.icon || 'default';
        const IconComponent = IconMapper[iconName] || CircleDot;
        return (
            <div className="my-icon-node rounded-full h-20 w-20 text-white rounded flex place-items-center justify-center hover:bg-white hover:bg-opacity-40">
                <IconComponent size={40} strokeWidth={1.5} />
            </div>
        );
    }}
</RGSlotOnNode>

下面这段代码证明,有些预设并不只是纯 CSS 装饰;它们依赖注入的 SVG 过滤器定义。

<filter id="electric-glitch">
    <feTurbulence type="turbulence" baseFrequency="0.05" numOctaves="2" result="turbulence">
        <animate attributeName="baseFrequency" dur="0.1s" values="0.01;0.5;0.02"
            repeatCount="indefinite"/>
    </feTurbulence>
    <feDisplacementMap in="SourceGraphic" in2="turbulence" scale="10" xChannelSelector="R"
        yChannelSelector="G"/>
</filter>

下面这段代码展示了其中一个预设如何把这些过滤器定义绑定到 relation-graph 原生的连线层上。

.rg-line-peel.my-line-class-10 {
    .rg-line-bg {
        stroke: #f43ce5;
        filter: url(#electric-glitch);
    }

    .rg-line {
        stroke: $glitch-color;
        animation: glitch-slide-10 0.2s steps(2) infinite;
        filter: url(#electric-glitch);
    }
}

这个示例的独特之处

从对比数据来看,这个 demo 的定位是一个用于展示内置动画边样式的预设画廊,而不是一个通用的连线样式定制示例。它少见之处在于:通过浮动面板批量更新每条连线的 className,把一棵固定的树切换成十二种可切换样式。

custom-line-style 相比,它在动态效果的多样性上走得更远,把预设分成基础、管线式和 SVG 过滤器三大家族,而不是停留在较小规模、以 CSS 为主的皮肤集合。与 line-style1 相比,它不依赖嵌入在 RGJsonData 中的静态 dashTypeanimation 值;它是通过 getLines()updateLine() 对已加载完成的图谱重新设定主题。与 line-style2 相比,它改变的是整个图谱,而不是只强调选中的那条边。与 customer-line1 相比,它保留了 relation-graph 原生的连线渲染器,而不是用 slot 渲染的丝带形几何来替换边的绘制方式。

另一个独特细节是它的协同展示层。SCSS 预设类负责处理连线、标签和文本,而像 current-animation-is-01101112 这样的包装类则会重新着色节点图标,以匹配当前选择的效果。这使得该示例成为一个紧凑的整图视觉主题化参考,而不是单一、孤立的动画技巧展示。

这种模式还适用于哪里

  • 网络运维界面可以保留一张依赖图,通过切换连线皮肤来表示空闲、退化、拥塞和故障中的流量状态,而无需重建数据。
  • 物流或制造业看板可以复用同一套拓扑,仅通过改变连线动态效果来表达油流、冷却液循环、能量传输或运输阻塞。
  • 设计系统演示环境可以把同样的技术用作边样式目录,让产品团队在一个稳定的示例图上比较多种品牌化连线处理方式。
  • 工作流或审批查看器可以将连线预设映射到正常吞吐、升级处理、审核积压或双向同步等运行时状态,同时保持节点布局不变。