Simulated data flow path
Animated Data Flow Path - Shortest path calculation with sequential data flow animation
Animated Data Flow Path Visualization
Functional Overview
This example demonstrates an advanced path visualization with animated data flow between nodes. When two nodes are selected, the system calculates the shortest path between them using Dijkstra’s algorithm, then animates data packets traveling along that path. It features custom line rendering with animated particles, bidirectional flow indicators, and interactive path selection. Users can also switch between tree and center layouts dynamically.
Implementation of Key Features
Shortest Path Calculation
Uses Dijkstra’s algorithm via calcShortestPath utility:
const showDataFlow = (fromNodeId: string, toNodeId: string) => {
const { lineIdsOnPath, nodeIdsOnPath } = calcShortestPath(fromNodeId, toNodeId, graphInstance, '');
console.log('lineIdsOnPath:', lineIdsOnPath);
allLineIdsOnPathRef.current = lineIdsOnPath;
allNodeIdsOnPathRef.current = nodeIdsOnPath;
restartTask();
};
Key aspects:
- Path calculation: Returns arrays of line IDs and node IDs forming the shortest route
- Reference storage: Uses refs to maintain path state across animation cycles
- Task restart: Triggers animation when new path is calculated
Animated Data Flow Implementation
The playDataFlowAnimationTask function orchestrates sequential line animations:
const playDataFlowAnimationTask = async () => {
clearInterval(playTimerRef.current);
const stepTime = pathAnimationSpeed;
if (allLineIdsOnPathRef.current.length === 0) {
return;
}
let currentLineId = allLineIdsOnPathRef.current[currentLineIndexOfPathRef.current];
let currentNodeId = allNodeIdsOnPathRef.current[currentLineIndexOfPathRef.current];
if (currentLineIndexOfPathRef.current === 0) {
// Reset all lines and nodes at animation start
for (const line of graphInstance.getLines()) {
graphInstance.updateLineData(line, {
played: false,
reverseLine: false
});
}
graphInstance.getNodes().forEach(node => {
graphInstance.updateNode(node, {
className: ''
});
});
await graphInstance.sleep(200);
}
playDataFlowAnimationOnLine(currentLineId, currentNodeId, stepTime);
currentLineIndexOfPathRef.current++;
playTimerRef.current = setTimeout(() => {
playDataFlowAnimationTask();
}, stepTime);
};
Key aspects:
- Sequential animation: Animates one line at a time along the path
- State reset: Clears previous animation states at cycle start
- Configurable timing: Uses
pathAnimationSpeedstate for duration control - Recursive scheduling: Uses setTimeout for next animation step
Per-Line Animation Control
Activates animation on individual lines with directional flow:
const playDataFlowAnimationOnLine = (currentLineId: string, currentNodeId: string, stepTime: number) => {
// Stop animations on other lines
graphInstance.getLines().forEach(line => {
if (currentLineId !== line.id && line.data?.playMyDataFlowAnimation) {
graphInstance.updateLineData(line, {
playMyDataFlowAnimation: false
});
}
});
const currentLine = graphInstance.getLineById(currentLineId);
graphInstance.updateLineData(currentLineId, {
playMyDataFlowAnimation: true,
played: true,
reverseLine: currentLine?.from !== currentNodeId,
myStepTime: stepTime
});
graphInstance.updateNode(currentNodeId, {
className: 'my-draw-path-animation'
});
};
Key aspects:
- Single active line: Only one line shows animation at a time
- Bidirectional support:
reverseLineindicates flow direction relative to line definition - Node highlighting: Adds CSS class to nodes as data passes through
- Custom timing: Passes animation duration to line component via data property
Two-Click Node Selection
Implements sequential node selection for path endpoints:
const onNodeClick = (node: RGNode, $event?: RGUserEvent) => {
console.log('onNodeClick:', node.id, graphInstance.getCheckedNode()?.id);
if (checkedNodeIdRef.current) {
if (checkedNodeIdRef.current === node.id) {
return;
}
// Second click: calculate and show path
showDataFlow(checkedNodeIdRef.current, node.id);
checkedNodeIdRef.current = '';
} else {
// First click: store as start node
checkedNodeIdRef.current = node.id;
}
};
Key aspects:
- State tracking: Uses ref to track first selected node
- Same-node check: Ignores clicks on already selected node
- Auto-clear: Resets after path calculation
Custom Line Component with Animation
The MyLineContent component renders animated data flow:
// MyLineContent.tsx (assumed implementation)
const MyLineContent: React.FC<RGLineSlotProps & { onMyLineDetailClick: (line: RGLine) => void }> = ({
line,
onMyLineDetailClick
}) => {
const isAnimating = line.data?.playMyDataFlowAnimation;
const isReverse = line.data?.reverseLine;
const stepTime = line.data?.myStepTime || 2000;
return (
<g className={`my-line-content ${isAnimating ? 'animating' : ''}`}>
<line
x1={line.x} y1={line.y}
x2={line.x2} y2={line.y2}
stroke={line.data?.played ? '#4ade80' : '#94a3b8'}
strokeWidth={2}
/>
{isAnimating && (
<circle r={4} fill="#22c55e">
<animateMotion
dur={`${stepTime}ms`}
path={`M${line.x},${line.y} L${line.x2},${line.y2}`}
rotate="auto"
/>
</circle>
)}
{line.text && (
<text onClick={() => onMyLineDetailClick(line)}>
{line.text}
</text>
)}
</g>
);
};
Custom SVG Markers
Defines arrow markers for line endpoints:
// MySvgDefs.tsx (assumed implementation)
const MySvgDefs = () => (
<defs>
<marker
id="my-arrow-001"
markerWidth="10"
markerHeight="10"
refX="9"
refY="3"
orient="auto"
markerUnits="strokeWidth"
>
<path d="M0,0 L0,6 L9,3 z" fill="#94a3b8" />
</marker>
<marker
id="my-arrow-001-start"
markerWidth="10"
markerHeight="10"
refX="0"
refY="3"
orient="auto"
markerUnits="strokeWidth"
>
<path d="M9,0 L9,6 L0,3 z" fill="#94a3b8" />
</marker>
</defs>
);
Arrow Configuration on Lines
Sets markers for bidirectional flow indication:
myJsonData.lines.forEach(line => {
line.endMarkerId = 'my-arrow-001';
line.startMarkerId = 'my-arrow-001-start';
line.showStartArrow = true;
line.showEndArrow = false;
});
Tree Layout with Gap Configuration
const graphOptions: RGOptions = {
defaultJunctionPoint: RGJunctionPoint.ltrb,
defaultNodeShape: RGNodeShape.circle,
defaultLineShape: RGLineShape.StandardCurve,
layout: {
layoutName: 'tree',
from: 'left',
treeNodeGapH: 100,
treeNodeGapV: 20
}
};
Speed Control UI
Allows users to adjust animation timing:
<SimpleUISelect
currentValue={pathAnimationSpeed}
data={[
{ value: 500, text: '0.5s' },
{ value: 1000, text: '1s' },
{ value: 2000, text: '2s' },
{ value: 3000, text: '3s' },
{ value: 5000, text: '5s' }
]}
onChange={(newValue: number) => {
setPathAnimationSpeed(newValue);
}}
/>
Layout Switching
Dynamic layout change support:
<SimpleUISelect
currentValue={''}
data={[
{ value: 'center', text: 'Center Layout' },
{ value: 'tree', text: 'Tree Layout' }
]}
onChange={(newValue: string) => {
graphInstance.updateOptions({
layout: {
...graphOptions.layout,
layoutName: newValue
}
});
initializeGraph();
}}
/>
Creative Use Cases
Network Traffic Monitoring: Visualize data packet flow through network infrastructure. Select source and destination servers to see the actual route packets take, with animation showing congestion points.
Supply Chain Tracking: Show product movement through distribution centers. Click factory and retailer to see distribution path with animated tracking of shipment progress.
Process Workflow Analysis: Display business process flows with animated task progression. Select start and end steps to visualize the complete workflow path with current status indicators.
Financial Transaction Tracing: Trace money flow through banking networks. Select sender and receiver accounts to visualize the transaction path through intermediaries.
Disease Spread Modeling: Animate infection transmission through contact networks. Select patient zero and current case to visualize transmission chain with timing information.