[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:zh:center-layout-style-controls":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# 在 center 布局关系图上实时切换样式\n\n## 这个示例构建了什么\n\n这个示例构建了一个全视口关系图，使用内置的 `center` 布局，并在画布上方放置了一个悬浮的白色控制窗口。图本身是一个固定的径向网络，中间有一个根节点，配有橙色节点和带标签的关系线。\n\n用户可以在已经显示的实时图上切换节点形状、连线形状，以及连线标签是否沿着连线路径显示。同一个悬浮窗口还可以拖动、最小化、展开为画布设置面板，并用于将当前图视图导出为图片。\n\n这个示例的重点在于图结构不会改变。它是一个紧凑的参考示例，用来展示如何在一个居中的布局之上原位修改图的呈现方式。\n\n## 数据是如何组织的\n\n数据以内联方式声明为一个 `RGJsonData` 对象，其中包含 `rootId: '2'`、一个 `nodes` 数组和一个 `lines` 数组。节点 ID 和标签使用诸如 `Node-2` 这样的通用占位符，而连线标签则使用 `Investment` 和 `Executive` 这一类少量关系文本。\n\n在 `setJsonData()` 执行之前，没有任何预处理步骤会重写这份数据集。初始化时会先应用图选项，再一次性加载静态 JSON。之后，运行时控制只会通过实例 API 对现有节点和连线重新设置样式，而不是重建数据。\n\n在实际项目中，这种数据形态可以表示以公司为中心的股权图谱、围绕某个关键人物的利益相关者网络、系统依赖中心，或者任何需要让某个中心实体在视觉上保持主导地位的小型关系图。\n\n## relation-graph 是如何使用的\n\n页面外层包裹了 `RGProvider`，图逻辑通过 `RGHooks.useGraphInstance()` 读取当前活跃实例。共享的 `DraggableWindow` 中的悬浮设置层同样会读取当前实例，并使用 `RGHooks.useGraphStore()` 反映当前的滚轮和拖拽模式。\n\n图是通过实例 API 初始化的，而不是通过 `RelationGraph` 的 `options` 属性。组件会调用 `setOptions(graphOptions)`，然后调用 `setJsonData(myJsonData)`，再通过 `moveToCenter()` 和 `zoomToFit()` 将视口重新居中。初始选项开启了 `layoutName: 'center'`，设置了无边框的橙色节点，并以连接到节点边框的直线作为初始连线样式。\n\n实时样式行为由 React 状态加上对当前图元素的批量更新来处理。当 `nodeShape`、`lineShape` 或 `textOnPath` 变化时，组件会遍历 `getNodes()` 和 `getLines()`，并对每个元素应用 `updateNode()` 或 `updateLine()`。圆形节点会被强制设置为 `70x70`，而矩形节点则通过 `width: 0` 和 `height: 0` 切换回自动尺寸。直线会继续使用边框连接点，而正交线和曲线则会切换到 `RGJunctionPoint.ltrb`。\n\n这个示例没有使用节点插槽、连线插槽、自定义渲染器或编辑 API。本地样式表主要对内置的选中状态做了细化：节点文本保持白色，而被选中的连线和标签则复用了与节点配色相同的橙色强调色。\n\n## 关键交互\n\n三个 `SimpleUISelect` 控件是主要的交互界面。一个用于在矩形节点和圆形节点之间切换，一个用于在直线、正交线和曲线之间切换，另一个用于在普通标签位置和沿路径文字模式之间切换连线标签。\n\n悬浮说明窗口支持拖动和最小化，因此可以在不离开页面的情况下把控件移到不遮挡内容的位置。它的设置视图还暴露了另外两个工作区级别的控制项：滚轮行为可以在滚动、缩放和无操作之间切换，画布拖拽可以在框选、移动和无操作之间切换。\n\n设置面板还支持导出图片。它会先请求 relation-graph 为图片生成准备画布，再使用 `modern-screenshot` 将 DOM 渲染为 blob，下载文件，然后恢复图状态。\n\n## 关键代码片段\n\n这个片段展示了该示例从内置的 `center` 布局开始，并在加载任何数据之前就在代码中设置了初始视觉默认值。\n\n```tsx\nconst graphOptions: RGOptions = {\n  debug: true,\n  defaultNodeBorderWidth: 0,\n  defaultNodeColor: 'rgba(238, 178, 94, 1)',\n  defaultLineShape: RGLineShape.StandardStraight,\n  layout: {\n    layoutName: 'center'\n  },\n  defaultJunctionPoint: RGJunctionPoint.border\n};\n```\n\n这个片段展示了图的数据载荷是一个内联数据集，并显式指定了根节点，因此布局始终拥有一个稳定的中心点。\n\n```tsx\nconst myJsonData: RGJsonData = {\n  rootId: '2',\n  nodes: [\n    { id: '1', text: 'Node-1' },\n    { id: '2', text: 'Node-2' },\n    { id: '3', text: 'Node-3' },\n    { id: '4', text: 'Node-4' },\n    { id: '6', text: 'Node-6' }\n  ],\n  // ...\n};\n```\n\n这个片段证明了初始化流程会先应用选项，再加载数据集，然后围绕结果规范化视口。\n\n```tsx\nconst initializeGraph = async () => {\n  graphInstance.setOptions(graphOptions);\n  await graphInstance.setJsonData(myJsonData);\n  graphInstance.moveToCenter();\n  graphInstance.zoomToFit();\n};\n```\n\n这个片段展示了运行时的核心模式：当呈现模式变化时，直接原位更新当前节点，而不是重建 `RGJsonData`。\n\n```tsx\ngraphInstance.getNodes().forEach((node) => {\n  if (nodeShape === RGNodeShape.circle) {\n    graphInstance.updateNode(node, {\n      nodeShape,\n      width: 70,\n      height: 70\n    });\n  } else {\n    graphInstance.updateNode(node, {\n      nodeShape,\n      width: 0,\n      height: 0\n    });\n  }\n});\n```\n\n这个片段展示了对应的连线更新流程，包括全局的沿路径文字开关，以及非直线情况下连接点的切换。\n\n```tsx\ngraphInstance.getLines().forEach((line) => {\n  if (lineShape === RGLineShape.StandardStraight) {\n    graphInstance.updateLine(line, {\n      lineShape,\n      fromJunctionPoint: RGJunctionPoint.border,\n      toJunctionPoint: RGJunctionPoint.border,\n      useTextOnPath: textOnPath\n    });\n  } else {\n    graphInstance.updateLine(line, {\n      lineShape,\n      fromJunctionPoint: RGJunctionPoint.ltrb,\n      toJunctionPoint: RGJunctionPoint.ltrb,\n      useTextOnPath: textOnPath\n    });\n  }\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```\n\n## 这个示例的独特之处\n\n与附近的其他 `center` 布局示例相比，这个示例更关注呈现方式切换，而不是布局间距或布局计算。对比记录明确将它与 `center-layout-options` 区分开来：两者都使用相同的悬浮窗口模式和同一内置布局家族，但 `layout-center` 的运行时逻辑关注的是节点形状、连线形状、连接点行为以及连线标签位置，而不是 `distanceCoefficient` 或 `levelGaps`。\n\n与 `line` 相比，这个示例更像一个紧凑的整图切换器，而不是一个覆盖面很广的线条样式展示集。它通过少量选择器一起修改所有可见节点和边，而不是将多种边样式直接编码进数据集、CSS 类或 marker 定义中。\n\n与 `deep-each` 和 `layout-diy` 相比，这里的交互更轻量，也更偏向展示层。它不会隔离子树，也不会计算自定义坐标。它把内置的 `center` 布局作为一个稳定的径向画布，并通过批量调用 `updateNode()` 和 `updateLine()` 来比较已经加载好的同一张图在不同渲染模式下的效果。\n\n这种组合正是它突出的主要原因：居中的径向构图、无边框的橙色节点、悬浮的白色覆盖层，以及三个实时选择器，它们可以在不替换数据集的前提下对现有图进行重新样式化。\n\n## 这种模式还适用于哪里\n\n这种模式很适合用于图工作台，在团队最终确定默认样式之前，先基于一份具有代表性的关系数据集比较不同的呈现方案。适用场景包括客户关系图、投资人或股权概览、服务依赖中心，以及围绕某个主要实体展开的利益相关者图。\n\n它也适用于文档或设计评审工具。在固定一张图结构的同时切换节点几何、连线路由和标签位置，可以更容易评估可读性上的取舍，而不会把样式实验与布局变化或数据重载混在一起。\n",false,500,1782615384781]