JavaScript is required

Line-Following HTML DIV Overlay

This example shows how to convert a selected relation-graph edge into CSS motion-path data and attach real HTML overlays above the canvas. It highlights the chosen line, refreshes the path after clicks and node drags, and lets users switch between animated travel and fixed positions along the line.

Line-Following HTML Overlay on a Graph Edge

What This Example Builds

This example renders a compact center-layout graph on a dark, neon-styled canvas and keeps one edge selected by default. The selected edge receives a brighter animated highlight, custom arrow markers at both ends, and two HTML ornaments rendered above the canvas: a main DIV that can move along the edge or stop at a fixed percentage, and a smaller particle that continuously travels the same path.

Users can click another line to retarget the overlay, drag nodes and watch the overlay realign to the new geometry, switch the shape used by all lines, and open the shared settings panel to change wheel or drag behavior and export an image. The main lesson is not custom SVG edge rendering. It is how to reuse relation-graph’s built-in line geometry as CSS motion-path data for ordinary DOM content.

How the Data Is Organized

The graph data is assembled inline in initializeGraph() as a static RGJsonData object with a rootId, a nodes array, and a lines array. Each line has a stable id, from, to, and label, and a short preprocessing pass adds startMarkerId, endMarkerId, and showStartArrow before setJsonData(...) loads the graph.

There is no remote fetch and no heavy transformation before layout. In a production application, the same data shape could represent process flows, device links, routing segments, dependency edges, or approval transitions, where the line id becomes the handle used to attach a DOM overlay or status badge to one selected relationship.

How relation-graph Is Used

index.tsx wraps the example in RGProvider, which lets RGHooks.useGraphInstance and the shared CanvasSettingsPanel resolve the active graph context. The graph options use layoutName: 'center', disableAsForceLayout: true, defaultJunctionPoint: RGJunctionPoint.lr, and a state-backed defaultLineShape, so the example starts from a stable centered viewer and can restyle current lines at runtime.

The graph instance APIs drive the main behavior. setJsonData(...), moveToCenter(), and zoomToFit() initialize the view; getLineById(...) chooses line-1 as the initial tracked edge; getLines() and updateLine(...) manage selected-line highlighting and shape switching; and generateLineConfig(...) plus generateLinePath(...) convert the current line into a CSS path(...) string. That path is passed into a wrapper inside RGSlotOnCanvasAbove, so the built-in relation-graph edge renderer stays visible while the HTML overlays move above it.

The rest of the customization is local. MySvgDefs injects the gooey filter and custom marker definitions, my-relation-graph.scss overrides node and line styling and binds both overlays to offset-path, and the shared DraggableWindow and CanvasSettingsPanel utilities add the floating controls, wheel and drag mode switches, and image export flow. The example remains viewer-oriented rather than editor-oriented: users change geometry and presentation, not graph structure.

Key Interactions

  • Clicking a line makes it the tracked path, highlights only that line, and regenerates the CSS path string used by the overlays.
  • Dragging a node triggers onNodeDragEnd, which recalculates the tracked path so the overlays stay attached to the updated geometry.
  • The line-shape selector switches all current edges between straight, curve, polyline, and bezier styles, then refreshes the tracked path.
  • The position selector switches the main DIV between continuous animation and fixed stops at 10%, 25%, 50%, 75%, or 90% of the selected path.
  • The floating window is draggable, and its settings overlay changes wheel and canvas-drag behavior and can export the current graph as an image.

Key Code Fragments

This initialization block shows the inline dataset, the per-line marker assignment, and the default selection of line-1.

myJsonData.lines.forEach(line => {
  line.endMarkerId = 'my-arrow-001';
  line.startMarkerId = 'my-arrow-001-start';
  line.showStartArrow = true;
});
await graphInstance.setJsonData(myJsonData);
graphInstance.moveToCenter();
graphInstance.zoomToFit();
const line = graphInstance.getLineById('line-1');
if (line) {
  handleLineSelection(line);
}

This fragment is the core reusable technique: it highlights one built-in line and converts that line into a CSS path(...) string for DOM overlays.

currentLineObject.current = line;
for (const l of graphInstance.getLines()) {
  graphInstance.updateLine(l, {
    className: l.id === line.id ? 'my-moving-line' : ''
  });
}
const lineConfig = graphInstance.generateLineConfig(line);
if (lineConfig) {
  const pathInfo = graphInstance.generateLinePath(lineConfig);
  setDivOffsetPath(`path('${pathInfo.pathData}')`);
}

This update path proves that line-shape changes are runtime changes on the current graph, followed by path regeneration for the selected overlay target.

const setNewLineShape = async (newShape: number) => {
  setLineShape(newShape);
  const lines = graphInstance.getLines();
  lines.forEach(line => {
    graphInstance.updateLine(line, {
      lineShape: newShape
    });
  });
  if (currentLineObject.current) {
    handleLineSelection(currentLineObject.current!);
  }
};

This CSS fragment shows how both DOM elements are bound to the generated path instead of fixed screen coordinates.

.div-on-line {
  animation: my-line-move 3s linear infinite;
  position: absolute;
  z-index: 999;
  offset-path: var(--checked-line-path);
  pointer-events: none;
}

.effect-particle {
  offset-path: var(--checked-line-path);
  offset-rotate: auto;
  animation: fly-along-path 3s cubic-bezier(0.4, 0.0, 0.2, 1) infinite;
}

What Makes This Example Distinct

Prepared comparison data places this example near custom-line-animation, custom-line-style, customer-line1, diy-line-arrow, and adv-line-slot, but it emphasizes a different layer of customization. Compared with adv-line-slot and customer-line1, this example keeps relation-graph’s native visible line renderer and adds DOM decoration above the canvas instead of replacing edge rendering with a custom line slot.

Its more distinctive combination is the geometry-aware attachment flow. The selected line is turned into CSS motion-path data through generateLineConfig(...) and generateLinePath(...), and that path is regenerated after line clicks, node drag end, and line-shape changes. The comparison record marks this combination of selected-line tracking, path refresh, and animated-or-fixed HTML overlays as rare, especially when paired with custom start and end markers, a gooey highlighted edge, and a draggable utility window.

Compared with custom-line-animation and custom-line-style, the runtime behavior is narrower but more geometry-aware: the example is not mainly a whole-graph style gallery. Compared with diy-line-arrow, the custom markers are supporting detail rather than the main lesson. This makes div-on-line a stronger starting point when the requirement is “attach a real DOM element to one live relation-graph edge and keep it aligned as the graph changes.”

Where Else This Pattern Applies

This pattern transfers well to monitoring and workflow interfaces where one relationship needs a richer DOM treatment than standard SVG can provide. A service map could place a live status chip on the selected dependency edge, a logistics view could park a vehicle badge at a fixed stop percentage or animate it across the active route segment, and a manufacturing graph could keep a warning label attached to the currently inspected connection.

It also applies to guided explanation layers. A workflow or approval graph can place a DOM tooltip, badge, or action control on the currently focused transition, while a knowledge or topology view can animate a token along the chosen relationship without replacing the built-in line renderer for every edge.