Canvas Interaction Events
A full-screen relation-graph demo that turns canvas click and drag callbacks into visible UI. It loads a small inline graph, clears checked state from `onCanvasClick`, converts drag-start browser coordinates into graph-view coordinates for a temporary arrow overlay, and reuses a floating settings panel for runtime mode switching and image export.
Canvas Drag Feedback and Selection Clearing in a Graph Workspace
What This Example Builds
This example builds a full-height relation-graph viewer that treats the canvas itself as the main subject. The graph data is intentionally small, but the page adds a draggable helper window, fixed toast messages, the built-in toolbar, and a temporary red arrow overlay that appears while the user drags the canvas.
Users can click empty canvas space to clear checked state, drag the canvas to see live feedback, open a settings panel inside the floating window, switch wheel and drag behavior at runtime, and export the current graph view as an image. The most useful part is how canvas callbacks become visible interface behavior instead of staying as console-only events.
How the Data Is Organized
The graph data is declared inline as one RGJsonData object inside initializeGraph() in MyGraph.tsx. It uses rootId: 'a', defines four nodes, and declares four line records with explicit IDs such as l1 and l4.
There is no preprocessing pipeline before setJsonData(...) beyond embedding presentation metadata directly into the dataset. Node colors, font color, shape, width, and height are assigned inline, and the lines also carry inline labels and colors. In a real application, the same structure could represent a compact device topology, a workflow checkpoint map, or any small graph where interaction handling matters more than custom data loading.
How relation-graph Is Used
index.tsx wraps the demo in RGProvider, and MyGraph.tsx gets the active graph instance through RGHooks.useGraphInstance(). The example does not define an explicit layout option, so it relies on relation-graph defaults after loading the inline data with setJsonData(...), then centers and fits the result with moveToCenter() and zoomToFit().
The graph options are short but important. showToolBar: true keeps the built-in toolbar visible, checkedItemBackgroundColor gives checked objects a translucent green highlight, defaultLineWidth: 1 keeps links thin, and defaultJunctionPoint: RGJunctionPoint.border makes line endpoints attach to node borders. RelationGraph binds onCanvasClick, onCanvasDragStart, onCanvasDragging, and onCanvasDragEnd, while node and line click handlers are present only for console logging.
Most of the reusable behavior comes from graph instance APIs. onCanvasClick reads getCheckedNode() and getCheckedLine(), shows a toast, and calls clearChecked(). onCanvasDragStart converts browser coordinates into graph-view coordinates with getViewXyByClientXy(...), and React state then drives both the helper-window readout and the MyArrowLayer SVG overlay. The floating DraggableWindow is a local React component rather than a relation-graph slot, but its settings panel still uses RGHooks.useGraphStore() to reflect wheelEventAction and dragEventAction, setOptions(...) to change those modes at runtime, and prepareForImageGeneration() plus restoreAfterImageGeneration() to support screenshot export. This example does not use node, line, canvas, or viewport slots; the drag indicator is a separate absolutely positioned overlay component.
Styling is intentionally light in my-relation-graph.scss, which mostly leaves relation-graph selectors open. The visible customization comes from the inline node and line presentation data, the shared floating window component, and the top-right toast UI styles.
Key Interactions
- Clicking empty canvas space checks whether a node or line is currently marked as checked, shows a success or warning toast, and clears that checked state.
- Starting a canvas drag stores a graph-relative origin, so the feedback overlay is anchored to relation-graph view coordinates instead of raw browser coordinates.
- Dragging updates live offset state and shows both a text readout in the helper window and a temporary red arrow over the graph.
- Ending the drag removes the active drag state and shows a completion toast.
- The helper window can be dragged, minimized, opened into a settings panel, switched between wheel and drag modes, and used to download an image of the current graph view.
Key Code Fragments
This fragment shows that the sample graph is declared inline and loaded during mount before the view is centered and fitted:
const myJsonData: RGJsonData = {
rootId: 'a',
nodes: [
{ id: 'a', text: 'A' },
{ id: 'b', text: 'B', color: '#43a2f1', fontColor: 'yellow' },
{ id: 'c', text: 'C', nodeShape: RGNodeShape.rect, width: 80, height: 60 },
{ id: 'e', text: 'E', nodeShape: RGNodeShape.circle, width: 150, height: 150 }
],
// ...
};
await graphInstance.setJsonData(myJsonData);
graphInstance.moveToCenter();
graphInstance.zoomToFit();
This fragment shows how empty-canvas clicks turn relation-graph checked state into visible feedback and then reset it:
const onCanvasClick = ($event: RGUserEvent) => {
console.log('onCanvasClick:', $event);
if (graphInstance.getCheckedNode() || graphInstance.getCheckedLine()) {
SimpleGlobalMessage.success('Clear Line/Node Checked Status');
} else {
SimpleGlobalMessage.warning('Please Check Node Or Line');
}
graphInstance.clearChecked();
};
This fragment shows the drag lifecycle capturing a graph-relative origin and the live drag offset:
const onCanvasDragStart = (canvasMoveStartPosition: RGPosition, eventClientStartPosition: RGPosition, e: RGUserEvent) => {
SimpleGlobalMessage.success('Canvas Drag Start');
setCanvasDragging(true);
const xyOnView = graphInstance.getViewXyByClientXy(eventClientStartPosition);
setCanvasDraggingStartPosition(xyOnView);
};
const onCanvasDragging = (canvasOffsetX: number, canvasOffsetY: number, buffX: number, buffY: number) => {
setCanvasDraggingInfo({ buffX, buffY });
};
This fragment shows that the visual drag indicator is a separate SVG layer that computes its end point from the stored offset and disappears at the origin:
const endX = startPos.x + offset.buffX;
const endY = startPos.y + offset.buffY;
if (offset.buffX === 0 && offset.buffY === 0) return null;
<line
x1={startPos.x}
y1={startPos.y}
x2={endX}
y2={endY}
stroke={color}
markerEnd="url(#arrowhead-solid)"
/>
This fragment shows the runtime canvas-mode switching pattern inside the floating settings panel:
<SettingRow
label="Wheel Event:"
options={[
{ label: 'Scroll', value: 'scroll' },
{ label: 'Zoom', value: 'zoom' },
{ label: 'None', value: 'none' },
]}
value={wheelMode}
onChange={(newValue: string) => { graphInstance.setOptions({ wheelEventAction: newValue }); }}
/>
This fragment shows the export flow that asks relation-graph to prepare the canvas DOM, renders it to an image blob, and then restores graph state:
const canvasDom = await graphInstance.prepareForImageGeneration();
let graphBackgroundColor = graphInstance.getOptions().backgroundColor;
if (!graphBackgroundColor || graphBackgroundColor === 'transparent') {
graphBackgroundColor = '#ffffff';
}
const imageBlob = await domToImageByModernScreenshot(canvasDom, {
backgroundColor: graphBackgroundColor
});
await graphInstance.restoreAfterImageGeneration();
What Makes This Example Distinct
The comparison data places drag-and-wheel-event closest to this example because both pages reuse the same floating helper window, runtime canvas settings, and image-export utilities. The difference is that canvas-event makes the drag lifecycle itself the main lesson. It binds onCanvasDragStart, onCanvasDragging, and onCanvasDragEnd, converts the drag-start browser position with getViewXyByClientXy(...), and turns that state into a live arrow overlay.
It is also notably different from canvas-selection and selections. Those examples use drag gestures for marquee selection or graph-authoring behavior, while this one never turns dragging into selection, node creation, or graph mutation. Here the drag gesture only produces transient feedback: readouts, toasts, and the red vector overlay.
Compared with node-line-tips-contentmenu, the emphasis is canvas-wide rather than object-specific. The unusual combination supported by the rarity and comparison data is empty-canvas checked-state clearing, graph-coordinate conversion, runtime wheel and drag mode switching, screenshot export, and a temporary drag-direction overlay on one compact mixed-shape sample graph. The floating helper window and export path are shared infrastructure, but the drag instrumentation pattern is what makes this example a better starting point for workspace interaction tooling.
Where Else This Pattern Applies
This pattern transfers well to graph workbenches where users need to understand canvas gestures, not just inspect graph content. Examples include topology review screens, operations dashboards, map-like graph viewers, onboarding sandboxes for pan and zoom rules, and debugging tools for custom interaction policies.
It is also useful when a team wants to layer instrumentation on top of an existing read-only graph without turning the page into a full editor. The same approach can be extended into drag guides, temporary measurement overlays, gesture training prompts, or QA surfaces that verify how different canvas modes behave under live interaction.