[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:en:canvas-center-offset":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# Recenter the Canvas with a Slot-Mounted Coordinate Grid\n\n## What This Example Builds\n\nThis example builds a canvas-positioning playground rather than a graph-data showcase. The page renders a full-height `RelationGraph` canvas, mounts a large labeled SVG coordinate grid inside the canvas layer, and places a floating utility window above it.\n\nUsers can drag that window, minimize it, open a shared settings panel, export an image, and most importantly move two sliders that shift the canvas center on the X and Y axes. The main point is that the example isolates canvas recentering itself, so the effect is easy to study without node data, line routing, or automatic layout changes getting in the way.\n\n## How the Data Is Organized\n\nThere is no graph dataset in this demo. `MyGraph.tsx` imports `RGJsonData`, but it never calls `setJsonData`, never provides nodes or lines to `RelationGraph`, and leaves `initializeGraph()` empty. The live state is just two numbers, `canvasOffsetX` and `canvasOffsetY`, which are used as the target canvas center.\n\nThe visual reference comes from `CoordinateGrid.tsx`. That component defines a fixed graph-space range from `-1000` to `1000` on both axes, uses a `50` unit step, and memoizes text-label positions for each grid segment before rendering the SVG. In a production tool, the same structure could represent workshop coordinates, warehouse aisles, floor-plan guides, CAD rulers, seat maps, or any other measured overlay that needs to stay aligned with graph space.\n\n## How relation-graph Is Used\n\n`index.tsx` wraps the demo in `RGProvider`, so relation-graph hooks can resolve the active graph context. Inside `MyGraph.tsx`, `RelationGraph` runs with `layoutName: 'fixed'`, a horizontal toolbar positioned at the bottom-right, and predefined node and line defaults. Because no graph data is loaded, those node and line defaults are mostly latent configuration rather than the visible lesson of the example.\n\nThe key runtime integration is `RGHooks.useGraphInstance()`. A React effect watches `canvasOffsetX` and `canvasOffsetY`, then calls `graphInstance.setCanvasCenter(...)` whenever either value changes. That turns a pair of ordinary React controls into an external camera-control surface for relation-graph.\n\nThe other important integration point is `RGSlotOnCanvas`. Instead of drawing the grid outside the graph container with CSS, the example mounts the SVG directly into the canvas layer and offsets its wrapper to `left: -1000px` and `top: -1000px`. That placement lines the grid's viewBox up with graph-space coordinates, so moving the canvas center produces a measurable visual result.\n\nThe floating helper window comes from a shared `DraggableWindow` component. In this example it hosts the X/Y sliders, and its built-in settings overlay uses `RGHooks.useGraphStore()` plus `graphInstance.setOptions(...)` to switch wheel and drag behavior at runtime. The same helper also uses `prepareForImageGeneration()`, `getOptions()`, and `restoreAfterImageGeneration()` to export the current canvas through `modern-screenshot`.\n\nStyling is intentionally light. `my-relation-graph.scss` contains selector scaffolding for toolbar, nodes, and lines, but it does not add concrete overrides, so the graph surface stays close to the library defaults while the coordinate grid and floating panel carry most of the visual meaning.\n\n## Key Interactions\n\nThe primary interaction is slider-driven recentering. Each slider updates local React state in steps of `10`, and the effect hook immediately pushes the new values into `setCanvasCenter(...)`. The sliders do not move nodes or alter a layout; they move the canvas center itself.\n\nThe floating window is also interactive. Users can drag it by the title bar, minimize it, and open the settings overlay. That overlay is not unique to this example, but here it matters because it lets users change wheel behavior between scroll, zoom, and none, and switch drag behavior between selection, move, and none while testing the empty workspace.\n\nThe last meaningful interaction is image export. The helper panel can prepare the current graph canvas for capture, render it to a blob, and download it, which makes the demo useful as a small debugging or documentation aid when checking coordinate alignment.\n\n## Key Code Fragments\n\nThis effect is the core proof that the demo controls canvas position through the graph instance rather than through layout data.\n\n```tsx\nconst [canvasOffsetX, setCanvasOffsetX] = useState(0);\nconst [canvasOffsetY, setCanvasOffsetY] = useState(0);\nconst graphInstance = RGHooks.useGraphInstance();\n\nuseEffect(() => {\n    graphInstance.setCanvasCenter(canvasOffsetX, canvasOffsetY);\n}, [canvasOffsetX, canvasOffsetY]);\n```\n\nThis JSX shows that the example-specific UI is only two sliders, while the graph surface itself receives a canvas-layer overlay.\n\n```tsx\n\u003CDraggableWindow>\n    \u003Cdiv className=\"py-1 text-sm\">canvas center:\u003C/div>\n    \u003Cdiv className=\"c-option-name\">canvas X: {canvasOffsetX}\u003C/div>\n    \u003CSimpleUISlider min={-1000} max={1000} step={10} currentValue={canvasOffsetX} onChange={(newValue: number) => { setCanvasOffsetX(newValue); }} />\n    \u003Cdiv className=\"c-option-name\">canvas Y: {canvasOffsetY}\u003C/div>\n    \u003CSimpleUISlider min={-1000} max={1000} step={10} currentValue={canvasOffsetY} onChange={(newValue: number) => { setCanvasOffsetY(newValue); }} />\n\u003C/DraggableWindow>\n```\n\nThis memoized block proves that the coordinate labels are precomputed from a fixed graph-space range before the SVG is rendered.\n\n```tsx\nconst labels = useMemo(() => {\n  const xLabels = [];\n  const yLabels = [];\n\n  for (let x = minX; x \u003C= maxX; x += step) {\n    for (let y = minY; y \u003C maxY; y += step) {\n      xLabels.push({ x, y: y + step / 2, val: x });\n    }\n  }\n  // ... matching yLabels loop omitted\n  return { xLabels, yLabels };\n}, [minX, maxX, minY, maxY, step]);\n```\n\nThis canvas slot wiring is what keeps the grid aligned with graph space instead of with the page viewport.\n\n```tsx\n\u003CRelationGraph options={graphOptions}>\n    \u003CRGSlotOnCanvas>\n        \u003Cdiv className=\"absolute left-[-1000px] top-[-1000px]\">\n            \u003CCoordinateGrid />\n        \u003C/div>\n    \u003C/RGSlotOnCanvas>\n\u003C/RelationGraph>\n```\n\nThis shared settings row shows how the helper panel changes relation-graph input behavior at runtime without reloading the scene.\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 export helper uses relation-graph's image-preparation APIs before handing the canvas DOM to `modern-screenshot`.\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 makes this example stand out for one narrow reason: it turns `setCanvasCenter(...)` into the main lesson. External sliders drive X and Y recentering directly, so the demo isolates canvas movement itself instead of combining it with data loading or layout transitions.\n\nIt is also more measurement-oriented than nearby examples. Compared with `canvas-caliper`, this demo moves the canvas center across a fixed graph-space grid instead of annotating the currently visible viewport with rulers. Compared with `area-set`, the canvas slot is analytical rather than semantic: it exists to reveal coordinates and offsets, not to partition a populated scene. Compared with `zoom` and `gee-thumbnail-diagram`, the surrounding controls change graph-space position itself rather than scale or minimap configuration.\n\nThe most distinctive combination is an otherwise empty fixed-layout graph, a labeled `RGSlotOnCanvas` grid that spans negative and positive coordinates, and slider-driven `setCanvasCenter(...)`. That makes this example a stronger starting point for debugging graph-space alignment than data-heavy demos that happen to include viewport controls.\n\n## Where Else This Pattern Applies\n\nThis pattern transfers well to tools that need precise external camera control. Typical follow-on uses include recenter buttons that jump to a selected work area, synchronized inspectors that move the canvas to a chosen coordinate, and guided tutorials that step through graph-space landmarks.\n\nIt also fits measurement-heavy overlays. A team could replace the demo grid with floor-plan axes, warehouse coordinates, engineering guides, game-map sectors, or large-image annotation guides while keeping the same `RGSlotOnCanvas` plus `setCanvasCenter(...)` structure.\n\nFinally, it is a practical template for troubleshooting. When a project needs to verify whether slot content, graph-space coordinates, screenshots, and interaction modes stay aligned, an empty scene with a labeled overlay is often easier to reason about than a populated business graph.\n",false,500,1782615432771]