JavaScript is required

中心布局样式控制

这个示例构建了一个全视口关系图,使用内置的 `center` 布局,并在画布上方放置了一个悬浮的白色控制窗口。图本身是一个固定的径向网络,中间有一个根节点,配有橙色节点和带标签的关系线。

在 center 布局关系图上实时切换样式

这个示例构建了什么

这个示例构建了一个全视口关系图,使用内置的 center 布局,并在画布上方放置了一个悬浮的白色控制窗口。图本身是一个固定的径向网络,中间有一个根节点,配有橙色节点和带标签的关系线。

用户可以在已经显示的实时图上切换节点形状、连线形状,以及连线标签是否沿着连线路径显示。同一个悬浮窗口还可以拖动、最小化、展开为画布设置面板,并用于将当前图视图导出为图片。

这个示例的重点在于图结构不会改变。它是一个紧凑的参考示例,用来展示如何在一个居中的布局之上原位修改图的呈现方式。

数据是如何组织的

数据以内联方式声明为一个 RGJsonData 对象,其中包含 rootId: '2'、一个 nodes 数组和一个 lines 数组。节点 ID 和标签使用诸如 Node-2 这样的通用占位符,而连线标签则使用 InvestmentExecutive 这一类少量关系文本。

setJsonData() 执行之前,没有任何预处理步骤会重写这份数据集。初始化时会先应用图选项,再一次性加载静态 JSON。之后,运行时控制只会通过实例 API 对现有节点和连线重新设置样式,而不是重建数据。

在实际项目中,这种数据形态可以表示以公司为中心的股权图谱、围绕某个关键人物的利益相关者网络、系统依赖中心,或者任何需要让某个中心实体在视觉上保持主导地位的小型关系图。

relation-graph 是如何使用的

页面外层包裹了 RGProvider,图逻辑通过 RGHooks.useGraphInstance() 读取当前活跃实例。共享的 DraggableWindow 中的悬浮设置层同样会读取当前实例,并使用 RGHooks.useGraphStore() 反映当前的滚轮和拖拽模式。

图是通过实例 API 初始化的,而不是通过 RelationGraphoptions 属性。组件会调用 setOptions(graphOptions),然后调用 setJsonData(myJsonData),再通过 moveToCenter()zoomToFit() 将视口重新居中。初始选项开启了 layoutName: 'center',设置了无边框的橙色节点,并以连接到节点边框的直线作为初始连线样式。

实时样式行为由 React 状态加上对当前图元素的批量更新来处理。当 nodeShapelineShapetextOnPath 变化时,组件会遍历 getNodes()getLines(),并对每个元素应用 updateNode()updateLine()。圆形节点会被强制设置为 70x70,而矩形节点则通过 width: 0height: 0 切换回自动尺寸。直线会继续使用边框连接点,而正交线和曲线则会切换到 RGJunctionPoint.ltrb

这个示例没有使用节点插槽、连线插槽、自定义渲染器或编辑 API。本地样式表主要对内置的选中状态做了细化:节点文本保持白色,而被选中的连线和标签则复用了与节点配色相同的橙色强调色。

关键交互

三个 SimpleUISelect 控件是主要的交互界面。一个用于在矩形节点和圆形节点之间切换,一个用于在直线、正交线和曲线之间切换,另一个用于在普通标签位置和沿路径文字模式之间切换连线标签。

悬浮说明窗口支持拖动和最小化,因此可以在不离开页面的情况下把控件移到不遮挡内容的位置。它的设置视图还暴露了另外两个工作区级别的控制项:滚轮行为可以在滚动、缩放和无操作之间切换,画布拖拽可以在框选、移动和无操作之间切换。

设置面板还支持导出图片。它会先请求 relation-graph 为图片生成准备画布,再使用 modern-screenshot 将 DOM 渲染为 blob,下载文件,然后恢复图状态。

关键代码片段

这个片段展示了该示例从内置的 center 布局开始,并在加载任何数据之前就在代码中设置了初始视觉默认值。

const graphOptions: RGOptions = {
  debug: true,
  defaultNodeBorderWidth: 0,
  defaultNodeColor: 'rgba(238, 178, 94, 1)',
  defaultLineShape: RGLineShape.StandardStraight,
  layout: {
    layoutName: 'center'
  },
  defaultJunctionPoint: RGJunctionPoint.border
};

这个片段展示了图的数据载荷是一个内联数据集,并显式指定了根节点,因此布局始终拥有一个稳定的中心点。

const myJsonData: RGJsonData = {
  rootId: '2',
  nodes: [
    { id: '1', text: 'Node-1' },
    { id: '2', text: 'Node-2' },
    { id: '3', text: 'Node-3' },
    { id: '4', text: 'Node-4' },
    { id: '6', text: 'Node-6' }
  ],
  // ...
};

这个片段证明了初始化流程会先应用选项,再加载数据集,然后围绕结果规范化视口。

const initializeGraph = async () => {
  graphInstance.setOptions(graphOptions);
  await graphInstance.setJsonData(myJsonData);
  graphInstance.moveToCenter();
  graphInstance.zoomToFit();
};

这个片段展示了运行时的核心模式:当呈现模式变化时,直接原位更新当前节点,而不是重建 RGJsonData

graphInstance.getNodes().forEach((node) => {
  if (nodeShape === RGNodeShape.circle) {
    graphInstance.updateNode(node, {
      nodeShape,
      width: 70,
      height: 70
    });
  } else {
    graphInstance.updateNode(node, {
      nodeShape,
      width: 0,
      height: 0
    });
  }
});

这个片段展示了对应的连线更新流程,包括全局的沿路径文字开关,以及非直线情况下连接点的切换。

graphInstance.getLines().forEach((line) => {
  if (lineShape === RGLineShape.StandardStraight) {
    graphInstance.updateLine(line, {
      lineShape,
      fromJunctionPoint: RGJunctionPoint.border,
      toJunctionPoint: RGJunctionPoint.border,
      useTextOnPath: textOnPath
    });
  } else {
    graphInstance.updateLine(line, {
      lineShape,
      fromJunctionPoint: RGJunctionPoint.ltrb,
      toJunctionPoint: RGJunctionPoint.ltrb,
      useTextOnPath: textOnPath
    });
  }
});

这个片段展示了这个悬浮层是一个真正的图工具界面,而不只是说明框,因为它会直接修改画布行为并驱动图片导出。

<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 }); }}
/>

这个示例的独特之处

与附近的其他 center 布局示例相比,这个示例更关注呈现方式切换,而不是布局间距或布局计算。对比记录明确将它与 center-layout-options 区分开来:两者都使用相同的悬浮窗口模式和同一内置布局家族,但 layout-center 的运行时逻辑关注的是节点形状、连线形状、连接点行为以及连线标签位置,而不是 distanceCoefficientlevelGaps

line 相比,这个示例更像一个紧凑的整图切换器,而不是一个覆盖面很广的线条样式展示集。它通过少量选择器一起修改所有可见节点和边,而不是将多种边样式直接编码进数据集、CSS 类或 marker 定义中。

deep-eachlayout-diy 相比,这里的交互更轻量,也更偏向展示层。它不会隔离子树,也不会计算自定义坐标。它把内置的 center 布局作为一个稳定的径向画布,并通过批量调用 updateNode()updateLine() 来比较已经加载好的同一张图在不同渲染模式下的效果。

这种组合正是它突出的主要原因:居中的径向构图、无边框的橙色节点、悬浮的白色覆盖层,以及三个实时选择器,它们可以在不替换数据集的前提下对现有图进行重新样式化。

这种模式还适用于哪里

这种模式很适合用于图工作台,在团队最终确定默认样式之前,先基于一份具有代表性的关系数据集比较不同的呈现方案。适用场景包括客户关系图、投资人或股权概览、服务依赖中心,以及围绕某个主要实体展开的利益相关者图。

它也适用于文档或设计评审工具。在固定一张图结构的同时切换节点几何、连线路由和标签位置,可以更容易评估可读性上的取舍,而不会把样式实验与布局变化或数据重载混在一起。