线条动画预设集
这个示例渲染一棵左到右 relation-graph 树,并将其变为 12 种动画边样式的展示集。它在运行时批量更新内置连线的 `className`,使用 SCSS 重绘原生线条表面,并注入 SVG 滤镜定义以支持基于滤镜的预设。
由预设驱动的动画图谱连线
这个示例构建了什么
这个示例在深色画布上构建了一个从左到右展开的 relation graph 树,并把连线变成一个动画效果展示集。节点保持小巧并聚焦于图标,而连线则通过十二种可选预设承担了大部分视觉表现。
用户可以在一个浮动控制窗口中切换基础、管线式以及基于 SVG 过滤器的连线样式。还可以拖动或最小化该窗口,打开一个共享设置浮层来修改滚轮和拖拽行为,并将图谱导出为图片。这个 demo 的重点在于,它通过重设 relation-graph 内置连线表面的样式来实现这一切,而不是替换连线渲染器。
数据是如何组织的
图谱数据在 initializeGraph() 内以内联方式组装为一个静态 RGJsonData 对象。它包含一个 rootId、一个带有 id、text 和 data.icon 的 nodes 数组,以及一个显式包含 id、from、to 和 text 字段的 lines 数组。
在调用 setJsonData() 之前没有做结构预处理。唯一有实际意义的转换是图标查找层:node.data.icon 会在 IconMapper 中映射为一个 Lucide 组件,而所选的连线预设会在加载后通过修改每一条已有连线来应用。在真实应用中,同样的数据形状可以表示服务依赖、生产阶段、供应路线或审批流,此时拓扑结构保持稳定,但视觉状态需要在运行时切换。
relation-graph 是如何使用的
index.tsx 用 RGProvider 包裹整个示例,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-paper、electric-glitch 和 gooey-plasma 过滤器定义,而像 current-animation-is-10 这样的包装类则让节点强调色与当前激活的连线样式保持一致。
关键交互
- 在任意
SimpleUISelect中选择一个预设,都会重写每条连线的className和可见文本,因此整个图谱会一次性切换样式。 - 初始加载本身也是交互式的:在
setJsonData()之后,图谱会先居中、适配视口,然后立即切换到预设10,因此 demo 打开时就是完整样式状态。 - 浮动控制窗口可以通过标题栏拖动、最小化,并在不影响图谱本身的情况下重新打开。
- 设置浮层会在
scroll、zoom和none之间切换wheelEventAction,并在selection、move和none之间切换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 中的静态 dashType 和 animation 值;它是通过 getLines() 和 updateLine() 对已加载完成的图谱重新设定主题。与 line-style2 相比,它改变的是整个图谱,而不是只强调选中的那条边。与 customer-line1 相比,它保留了 relation-graph 原生的连线渲染器,而不是用 slot 渲染的丝带形几何来替换边的绘制方式。
另一个独特细节是它的协同展示层。SCSS 预设类负责处理连线、标签和文本,而像 current-animation-is-01、10、11 和 12 这样的包装类则会重新着色节点图标,以匹配当前选择的效果。这使得该示例成为一个紧凑的整图视觉主题化参考,而不是单一、孤立的动画技巧展示。
这种模式还适用于哪里
- 网络运维界面可以保留一张依赖图,通过切换连线皮肤来表示空闲、退化、拥塞和故障中的流量状态,而无需重建数据。
- 物流或制造业看板可以复用同一套拓扑,仅通过改变连线动态效果来表达油流、冷却液循环、能量传输或运输阻塞。
- 设计系统演示环境可以把同样的技术用作边样式目录,让产品团队在一个稳定的示例图上比较多种品牌化连线处理方式。
- 工作流或审批查看器可以将连线预设映射到正常吞吐、升级处理、审核积压或双向同步等运行时状态,同时保持节点布局不变。