JavaScript is required

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 pathAnimationSpeed state 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: reverseLine indicates 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.