[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:zh:graph-canvas-theme-switcher":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# 通过一个包装类切换 Relation Graph Canvas 主题\n\n## 这个示例构建了什么\n\n这个示例会渲染一棵从左到右展开的小型树，并允许用户在运行时在三种视觉主题之间切换整个图的展示效果。图数据和 `RelationGraph` 渲染器保持不变，但最外层的包装类会从 `my-graph-theme-1` 切换到 `my-graph-theme-3`，从而重新设置地图背景、工具栏、节点以及选中状态表面的样式。\n\n界面是一个全高度查看器，画布上方悬浮着一个工具窗口。用户可以拖动该窗口、将其最小化、打开设置浮层、切换当前主题、通过共享设置面板调整滚轮和画布拖拽行为，并将当前图下载为图片。\n\n最重要的一点是，这是一种 CSS 换肤模式，而不是自定义渲染模式。这个示例保持标准树图不变，通过针对 relation-graph 内置 DOM 结构编写 SCSS 选择器，营造出截然不同的视觉氛围。\n\n## 数据是如何组织的\n\n数据在 `initializeGraph()` 内以内联方式声明为一个 `RGJsonData` 对象。它使用一个 `rootId`、一个包含 `id` 和 `text` 的扁平 `nodes` 数组，以及一个带有明确 `id`、`from` 和 `to` 字段的扁平 `lines` 数组。\n\n布局前没有预处理步骤。代码会直接把 JSON 传给 `graphInstance.setJsonData()`，然后通过 `moveToCenter()` 和 `zoomToFit()` 将结果重新居中并适配视图。\n\n这种结构很容易替换为真实业务数据。相同的数据形状可以表示组织树、服务依赖层级、分类树、团队归属关系图，或者审批链，在这些场景里每个节点只需要一个标签，每条边只需要一个源和目标。\n\n## relation-graph 是如何使用的\n\n`index.tsx` 使用 `RGProvider` 包裹整个示例，因此主图组件和共享辅助窗口都可以解析当前激活的图上下文。在 `MyGraph.tsx` 中，`RelationGraph` 只被渲染一次，使用的是一个紧凑的 `graphOptions` 对象，并且没有自定义节点插槽。\n\n布局使用内置的树形布局，从左向右生长，配置为 `treeNodeGapH: 200` 和 `treeNodeGapV: 10`。基础选项刻意让节点和连线样式保持中性：节点是矩形、透明、带轻微边框；连线使用标准曲线形状和左右连接点；工具栏则水平放置在底部居中位置。这样的组合为导入的主题文件留出了空间，让它们定义大部分可见的界面外观。\n\n这个示例在两个地方使用了 `RGHooks.useGraphInstance()`。在 `MyGraph` 中，它在组件挂载时加载静态 JSON 数据，然后让图重新居中并适配视图。在共享的 `CanvasSettingsPanel` 中，它会更新 `wheelEventAction` 和 `dragEventAction` 之类的运行时选项，并通过 `prepareForImageGeneration()` 和 `restoreAfterImageGeneration()` 执行图片导出流程。\n\n真正的重点在于样式。每个导入的 SCSS 文件都会针对 relation-graph 的选择器编写规则，例如 `.rg-map`、`.rg-toolbar`、`.rg-node`、`.rg-node-peel.rg-node-checked` 和 `.rg-line`。由于包装类是在顶层切换的，因此同一个图实例可以在径向渐变玻璃主题、动态多彩主题和动态对角渐变主题之间切换，而不需要修改数据集或重建节点内容。\n\n## 关键交互\n\n核心交互是主题切换。点击三个 `SimpleUISelect` 项中的任意一个，都会更新 React 状态，从而替换包装类，并立即为图重新换肤。\n\n悬浮辅助窗口本身也支持交互。它可以在页面上拖动、最小化，并再次展开，这样既能保持图本身界面整洁，又能让控制项随时可用。\n\n设置按钮会打开共享的 `CanvasSettingsPanel`。在这里，用户可以将鼠标滚轮行为切换为滚动、缩放或无；将画布拖拽行为切换为框选、移动或无；还可以将当前已套用样式的画布下载为图片。这些控件是跨示例复用的通用辅助功能，但在本示例中同样可用，并且会影响当前实时图实例。\n\n主题样式还为节点和连线定义了选中状态下的覆盖样式。这个示例没有添加自定义选择逻辑，但当 relation-graph 应用这些类时，内置的选中视觉效果仍然会继承当前激活主题的样式。\n\n## 关键代码片段\n\n这段配置展示了图如何保持简单的树形配置，并把最终外观的大部分工作交给 CSS。\n\n```tsx\nconst graphOptions: RGOptions = {\n    defaultLineColor: 'rgba(255, 255, 255, 0.6)',\n    defaultNodeColor: 'transparent',\n    defaultNodeBorderWidth: 1,\n    defaultNodeBorderColor: 'rgba(255, 255, 255, 0.3)',\n    defaultNodeShape: RGNodeShape.rect,\n    defaultNodeWidth: 170,\n    defaultNodeHeight: 40,\n    toolBarDirection: 'h',\n    toolBarPositionH: 'center',\n    toolBarPositionV: 'bottom',\n    defaultPolyLineRadius: 10,\n```\n\n这段初始化流程证明，这个示例使用的是普通 `RGJsonData` 和图实例 API，而不是自定义加载器。\n\n```tsx\nconst myJsonData: RGJsonData = {\n    rootId: 'a',\n    nodes: [\n        { id: 'a', text: 'a' }, { id: 'b', text: 'b' }, { id: 'b1', text: 'b1' },\n        // ...\n    ],\n    lines: [\n        { id: 'l1', from: 'a', to: 'b' }, { id: 'l2', from: 'b', to: 'b1' },\n        // ...\n    ]\n};\n\nawait graphInstance.setJsonData(myJsonData);\ngraphInstance.moveToCenter();\ngraphInstance.zoomToFit();\n```\n\n这段渲染代码说明，实时主题切换本质上只是包装类上的一次状态变化，再加上一个小型选择器 UI。\n\n```tsx\n\u003Cdiv className={myGraphClassName} style={{ height: '100vh' }}>\n    \u003CDraggableWindow>\n        \u003CSimpleUISelect\n            data={[\n                { value: 'my-graph-theme-1', text: 'Canvas Theme 1' },\n                { value: 'my-graph-theme-2', text: 'Canvas Theme 2' },\n                { value: 'my-graph-theme-3', text: 'Canvas Theme 3' }\n            ]}\n            currentValue={myGraphClassName}\n            onChange={(newValue: string) => {\n                setMyGraphClassName(newValue);\n            }}\n        />\n    \u003C/DraggableWindow>\n```\n\n这段主题代码片段展示了包装类如何深入 relation-graph 的内置表面进行样式设置，而不是用自定义插槽替换它们。\n\n```scss\n.my-graph-theme-2 {\n    .relation-graph {\n        .rg-map {\n            background: radial-gradient(circle at center, #aff69e 0%, #9ed8f6 10%, #f69ee5 20%, #d4980e 30%, #daa93a 40%, #3eda3a 50%, #b9ed94 60%);\n            background-size: 1000% 1000%;\n            animation: backgroundGradient 30s ease infinite;\n        }\n\n        .rg-toolbar {\n            background-color: #ffffff;\n        }\n```\n\n这段辅助代码片段展示了设置浮层如何直接更新图的交互模式，并通过共享图实例触发图片导出。\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\u003CSettingRow\n    label=\"Canvas Drag Event:\"\n```\n\n## 这个示例的独特之处\n\n比较数据将这个示例与 `css-theme`、`generate-image-with-background`、`node-style3` 和 `node-drag-handle` 放得较近，但它的关注点比这些相邻示例都更集中。它的独特价值在于展示：同一棵 relation-graph 树只需要切换一个外层包装类，就能在三套导入的全画布皮肤之间切换，同时保持相同的数据、布局和内置渲染器不变。\n\n与 `css-theme` 相比，这个示例是更聚焦的包装类换肤参考。它没有加入 minimap、自定义节点图标插槽或更大的主题画廊，而是专注于营造氛围感渐变画布、半透明矩形文本节点以及工具栏换肤。\n\n与 `generate-image-with-background` 相比，这里的主要经验是实时展示切换，而不是捕获预览工作流。与 `node-style3` 和 `node-drag-handle` 相比，它更接近 relation-graph 的默认查看器行为，并把 CSS 表面主题化作为示例核心，而不是基于插槽驱动的节点内容或手动拖拽交互。\n\n## 这种模式还能用在哪里\n\n这种模式非常适合品牌化仪表盘场景，在这些场景中，同一张图需要针对不同客户、活动或工作区模式提供多套视觉皮肤。它也很适合白标产品，因为这类产品通常需要保持同一个图实现，只在 CSS 层交换展示效果。\n\n它也适用于需要临时展示模式的内部工具，例如白天与夜间运行视图、按状态着色的层级仪表盘，或者面向演示和截图的图界面。在这些场景中，数据模型和图行为可以保持稳定，而包装类则负责控制可见的视觉氛围。\n",false,500,1782615434404]