Graph Line Animation Presets
This example renders a left-to-right relation-graph tree and turns it into a gallery of twelve animated edge styles. It batch-updates each built-in line's className at runtime, uses SCSS to restyle the native line surfaces, and injects SVG filter definitions for the filter-backed presets.
Preset-Driven Animated Graph Lines
What This Example Builds
This example builds a left-to-right relation graph tree on a dark canvas and turns the links into an animation gallery. The nodes stay small and icon-focused, while the lines carry most of the visual behavior through twelve selectable presets.
Users can switch among basic, pipeline, and SVG-filter-based line styles from a floating control window. They can also drag or minimize that window, open a shared settings overlay to change wheel and drag behavior, and export the graph as an image. The main point is that the demo achieves all of this by restyling relation-graph’s built-in line surfaces instead of replacing the line renderer.
How the Data Is Organized
The graph data is assembled inline inside initializeGraph() as a static RGJsonData object. It has one rootId, a nodes array with id, text, and data.icon, and a lines array with explicit id, from, to, and text fields.
There is no structural preprocessing before setJsonData(). The only meaningful transformation is the icon lookup layer: node.data.icon is mapped to a Lucide component in IconMapper, and the selected line preset is applied after load by mutating every existing line. In a real application, the same shape could represent service dependencies, production stages, supply routes, or approval flows where the topology stays stable but the visual state needs to change at runtime.
How relation-graph Is Used
index.tsx wraps the example in RGProvider, and MyGraph.tsx renders RelationGraph with a tree layout that grows from the left. The options fix the visual shell: circular nodes, curved lines, left-right junction points, explicit horizontal and vertical tree gaps, a transparent node body, and a bottom-right horizontal toolbar.
The example uses RGHooks.useGraphInstance() as the runtime control surface. That instance loads the static JSON, centers the graph, fits it to the viewport, reads the current lines with getLines(), rewrites them with updateLine(), and supports export through prepareForImageGeneration() and restoreAfterImageGeneration(). The shared settings overlay uses RGHooks.useGraphStore() to read the current wheel and drag modes, then calls setOptions() to switch them.
The custom rendering hook is RGSlotOnNode. Instead of replacing edges, the example keeps the native relation-graph line renderer and overrides only the node content with icon-based circular bodies. Styling work happens in SCSS: .rg-map and .rg-toolbar set the dark shell, .rg-node-peel.rg-node-checked and .rg-line-peel.rg-line-checked adjust checked states, and twelve .my-line-class-xx selectors retheme .rg-line, .rg-line-bg, .rg-line-label, and .rg-line-text. MySvgFilters injects the rough-paper, electric-glitch, and gooey-plasma filter definitions required by the filter-backed presets, and wrapper classes such as current-animation-is-10 keep node accents aligned with the active line style.
Key Interactions
- Choosing a preset in any
SimpleUISelectrewrites every line’sclassNameand visible text, so the whole graph switches style at once. - Initial load is also interactive: after
setJsonData(), the graph is centered, fitted, and immediately switched to preset10, so the demo opens in a fully styled state. - The floating control window can be dragged by its title bar, minimized, and reopened without affecting the graph itself.
- The settings overlay changes
wheelEventActionbetweenscroll,zoom, andnone, and changesdragEventActionbetweenselection,move, andnone. - The export action prepares the graph DOM for capture, renders it to a Blob through
modern-screenshot, downloads the image, and restores the graph state afterward.
Key Code Fragments
This fragment shows that the demo relies on relation-graph’s built-in layout and renderer settings rather than custom geometry.
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
}
};
This fragment shows that one fixed dataset is loaded once, then styled after the graph is already on screen.
const myJsonData: RGJsonData = {
rootId: 'a',
nodes: [
// node records omitted
],
lines: [
// line records omitted
]
};
await graphInstance.setJsonData(myJsonData);
graphInstance.moveToCenter();
graphInstance.zoomToFit();
changeAllLineClassName('10');
This fragment is the core technique: it batch-updates every existing line through the 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}`
});
});
};
This fragment shows that the example customizes nodes through a slot while leaving edge rendering to relation-graph itself.
<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>
This fragment proves that some presets are not pure CSS decoration; they depend on injected SVG filter definitions.
<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>
This fragment shows how one preset binds those filter defs to relation-graph’s native line layers.
.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);
}
}
What Makes This Example Distinct
The comparison data positions this demo as a preset gallery for animated built-in edges, not as a generic line-customization sample. Its rare point is the way it turns one fixed tree into twelve switchable styles by batch-updating every line’s className from a floating panel.
Compared with custom-line-style, it pushes further on motion variety by grouping presets into basic, pipeline, and SVG-filter families instead of stopping at a smaller CSS-first skin set. Compared with line-style1, it does not rely on static dashType and animation values embedded in RGJsonData; it rethemes the already-loaded graph through getLines() and updateLine(). Compared with line-style2, it changes the entire graph at once instead of emphasizing only the checked edge. Compared with customer-line1, it stays on relation-graph’s native line renderer instead of replacing edge geometry with slot-rendered ribbons.
Another distinctive detail is the coordinated presentation layer. The SCSS preset classes handle the lines, labels, and text, while wrapper classes such as current-animation-is-01, 10, 11, and 12 recolor node icons to match the selected effect. That makes the example a compact reference for whole-graph visual theming rather than a single isolated animation trick.
Where Else This Pattern Applies
- A network operations screen could keep one dependency graph and switch line skins to represent idle, degraded, congested, and failing traffic modes without rebuilding the data.
- A logistics or manufacturing dashboard could reuse the same topology while changing line motion to suggest oil flow, coolant circulation, energy transfer, or blocked transport.
- A design-system playground could use the same technique as an edge-style catalog, where product teams compare several branded line treatments on one stable example graph.
- A workflow or approval viewer could map line presets to runtime states such as normal throughput, escalation, review backlog, or bidirectional synchronization, while keeping the node layout unchanged.