[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:en:canvas-interaction-events":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# Canvas Drag Feedback and Selection Clearing in a Graph Workspace\n\n## What This Example Builds\n\nThis 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.\n\nUsers 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.\n\n## How the Data Is Organized\n\nThe 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`.\n\nThere 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.\n\n## How relation-graph Is Used\n\n`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()`.\n\nThe 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.\n\nMost 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.\n\nStyling 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.\n\n## Key Interactions\n\n- 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.\n- 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.\n- Dragging updates live offset state and shows both a text readout in the helper window and a temporary red arrow over the graph.\n- Ending the drag removes the active drag state and shows a completion toast.\n- 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.\n\n## Key Code Fragments\n\nThis fragment shows that the sample graph is declared inline and loaded during mount before the view is centered and fitted:\n\n```tsx\nconst myJsonData: RGJsonData = {\n    rootId: 'a',\n    nodes: [\n        { id: 'a', text: 'A' },\n        { id: 'b', text: 'B', color: '#43a2f1', fontColor: 'yellow' },\n        { id: 'c', text: 'C', nodeShape: RGNodeShape.rect, width: 80, height: 60 },\n        { id: 'e', text: 'E', nodeShape: RGNodeShape.circle, width: 150, height: 150 }\n    ],\n    // ...\n};\nawait graphInstance.setJsonData(myJsonData);\ngraphInstance.moveToCenter();\ngraphInstance.zoomToFit();\n```\n\nThis fragment shows how empty-canvas clicks turn relation-graph checked state into visible feedback and then reset it:\n\n```tsx\nconst onCanvasClick = ($event: RGUserEvent) => {\n    console.log('onCanvasClick:', $event);\n    if (graphInstance.getCheckedNode() || graphInstance.getCheckedLine()) {\n        SimpleGlobalMessage.success('Clear Line/Node Checked Status');\n    } else {\n        SimpleGlobalMessage.warning('Please Check Node Or Line');\n    }\n    graphInstance.clearChecked();\n};\n```\n\nThis fragment shows the drag lifecycle capturing a graph-relative origin and the live drag offset:\n\n```tsx\nconst onCanvasDragStart = (canvasMoveStartPosition: RGPosition, eventClientStartPosition: RGPosition, e: RGUserEvent) => {\n    SimpleGlobalMessage.success('Canvas Drag Start');\n    setCanvasDragging(true);\n    const xyOnView = graphInstance.getViewXyByClientXy(eventClientStartPosition);\n    setCanvasDraggingStartPosition(xyOnView);\n};\nconst onCanvasDragging = (canvasOffsetX: number, canvasOffsetY: number, buffX: number, buffY: number) => {\n    setCanvasDraggingInfo({ buffX, buffY });\n};\n```\n\nThis 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:\n\n```tsx\nconst endX = startPos.x + offset.buffX;\nconst endY = startPos.y + offset.buffY;\nif (offset.buffX === 0 && offset.buffY === 0) return null;\n\n\u003Cline\n    x1={startPos.x}\n    y1={startPos.y}\n    x2={endX}\n    y2={endY}\n    stroke={color}\n    markerEnd=\"url(#arrowhead-solid)\"\n/>\n```\n\nThis fragment shows the runtime canvas-mode switching pattern inside the floating settings panel:\n\n```tsx\n\u003CSettingRow\n    label=\"Wheel Event:\"\n    options={[\n        { label: 'Scroll', value: 'scroll' },\n        { label: 'Zoom', value: 'zoom' },\n        { label: 'None', value: 'none' },\n    ]}\n    value={wheelMode}\n    onChange={(newValue: string) => { graphInstance.setOptions({ wheelEventAction: newValue }); }}\n/>\n```\n\nThis 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:\n\n```tsx\nconst canvasDom = await graphInstance.prepareForImageGeneration();\nlet graphBackgroundColor = graphInstance.getOptions().backgroundColor;\nif (!graphBackgroundColor || graphBackgroundColor === 'transparent') {\n    graphBackgroundColor = '#ffffff';\n}\nconst imageBlob = await domToImageByModernScreenshot(canvasDom, {\n    backgroundColor: graphBackgroundColor\n});\nawait graphInstance.restoreAfterImageGeneration();\n```\n\n## What Makes This Example Distinct\n\nThe 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.\n\nIt 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.\n\nCompared 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.\n\n## Where Else This Pattern Applies\n\nThis 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.\n\nIt 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.\n",false,500,1782615423947]