插槽自定义节点内容与样式
这个示例渲染一棵从左到右的树图,用自定义 React 插槽内容替换默认节点主体。单一静态数据集混合了轮播卡片、视频卡片、按钮节点、圆形徽章和玻璃风标签,并通过共享悬浮面板提供运行时画布设置与图片导出。
树形图中基于插槽的节点内容与样式
这个示例构建了什么
这个示例构建了一个从左到右布局的树形图,其节点并不共用同一种视觉模板。相反,图中通过单一节点插槽渲染多种不同的节点主体:带轮播的大型根节点卡片、视频卡片、矩形按钮节点、圆形徽章,以及半透明的胶囊标签。
最终可见效果是一个全高展示画布,背景为黄绿色,连接线为半透明白色,并带有一个悬浮的白色工具窗口。用户可以像查看器一样浏览图谱,与嵌入节点中的控件交互,打开悬浮设置面板,切换画布交互模式,并将当前图谱导出为图片。
最重要的点并不在于这棵树本身。这个示例展示了:一个静态的 relation-graph 数据集,如何通过 RGSlotOnNode 驱动异构的 React 节点内容;同时 SCSS 如何把选中态样式重定向到已渲染的插槽内容上,而不是库默认的节点主体。
数据是如何组织的
图数据以内联方式创建为一个 RGJsonData 对象,包含 rootId、nodes 和 lines。这里没有 fetch,没有异步转换流程,也没有加载后的动态图编辑。结构很直接:
nodes保存图中的身份信息和展示提示。lines定义树的关系。type决定MyNodeContent应该渲染哪一种自定义节点主体。data.buttonText携带按钮标签这种按节点区分的负载数据。- 即使可见主体主要是自定义插槽内容,
nodeShape仍然会影响 relation-graph 的几何计算。
在调用 setJsonData() 之前并没有单独的预处理步骤。这个示例在组装 JSON 字面量时就直接给节点打上标签,因此图谱加载后,插槽渲染器可以立即基于这些元数据进行分支。
在真实应用中,同样的模式可以映射到组织角色、工作流状态、富媒体内容卡片、KPI 徽章、告警节点或操作节点等场景,在这些场景中,每种节点类型都需要不同的 HTML 结构,但仍然属于同一个图数据集。
relation-graph 是如何使用的
这个图将 relation-graph 用作面向查看的树布局。MyGraph 配置了一个 tree 布局,使其从左向右生长,并采用较大的水平间距与更紧凑的垂直间距,从而让这些混合节点主体保持可读性。选项中还通过将节点填充设为透明、默认边框宽度设为 0,去掉了大部分默认节点外观,使插槽渲染出的 React 内容来定义最终视觉效果。
RelationGraph 被包裹在 RGProvider 中,示例通过 RGHooks.useGraphInstance() 读取实时图实例。该实例用于一次性的初始化流程:通过 setJsonData(...) 加载数据,将视口移动到中心,然后调用 zoomToFit()。同一个 hook 也在共享的悬浮辅助窗口中复用,用于更新交互选项并执行图片导出流程。
核心定制点是 RGSlotOnNode。每个节点都会经过同一个插槽函数,它会把当前节点对象传给 MyNodeContent。该组件根据 node.type 分支并渲染:
- 根节点的轮播卡片,
- 内置媒体控制的视频卡片,
my-button的黑色操作按钮,my-circle的半透明圆形徽章,- 其余节点的玻璃风格胶囊标签。
SCSS 补完了这套定制。样式表为工具栏提供半透明白色外观,去掉默认选中节点阴影,并把高亮环应用到自定义插槽包装层上。这一点很重要,因为当前可见的节点主体已经不再是 relation-graph 默认的节点框。
这里没有图编辑流程。这个示例明确处于查看模式,运行时可修改的选项仅限于画布交互行为和导出支持。
关键交互
- 根节点包含一个带有 Previous 和 Next 控件的轮播组件,证明交互式 React 小部件可以放进节点主体中。
- 视频节点暴露原生媒体控制,说明插槽内容可以包含富 HTML 媒体,而不仅仅是文本或图标。
- 按钮节点在节点内容内部处理 DOM 点击事件,并记录当前节点 id。这个行为虽然很小,但它展示了自定义节点小部件可以拥有自己的事件处理逻辑。
- 悬浮工具窗口可以被拖动、最小化,并切换到设置覆盖层。
- 设置覆盖层会在实时图实例上修改
wheelEventAction和dragEventAction,因此用户无需重新加载数据,就能在滚动、缩放、选择、移动或无画布响应之间切换。 - 同一个覆盖层还可以通过预处理图 DOM、使用
modern-screenshot进行捕获、下载 blob,然后恢复图状态的方式,将图谱导出为图片。
关键代码片段
这段代码展示了:该示例依赖 relation-graph 的布局与选项调优,来在一个展示型画布上承载自定义节点内容。
const graphOptions: RGOptions = {
debug: true,
backgroundColor: 'rgb(101, 163, 13)',
defaultLineColor: 'rgba(255, 255, 255, 0.6)',
defaultNodeColor: 'transparent',
defaultNodeBorderWidth: 0,
defaultNodeShape: RGNodeShape.rect,
toolBarDirection: 'h',
toolBarPositionH: 'right',
toolBarPositionV: 'bottom',
defaultPolyLineRadius: 10,
defaultLineShape: RGLineShape.StandardCurve,
defaultJunctionPoint: RGJunctionPoint.lr,
layout: {
layoutName: 'tree',
from: 'left',
treeNodeGapH: 200,
treeNodeGapV: 30
}
};
这段代码展示了:图数据本身就携带了插槽渲染器所使用的节点类型元数据。
const myJsonData: RGJsonData = {
rootId: 'a',
nodes: [
{ id: 'a', text: 'a', type: 'my-root', nodeShape: RGNodeShape.rect },
{ id: 'b', text: 'b', type: 'my-circle', nodeShape: RGNodeShape.circle },
// ...
{ id: 'c', text: 'c', type: 'my-video' },
{ id: 'c1', text: 'c1', type: 'my-button', nodeShape: RGNodeShape.rect, data: { buttonText: 'Button 1' } },
{ id: 'c2', text: 'c2', type: 'my-button', nodeShape: RGNodeShape.rect, data: { buttonText: 'Button 2' } }
],
这段代码展示了核心的插槽渲染技术:一个节点组件分支成多种不同的节点主体实现。
if (node.type === 'my-root') {
return (
<div className="bg-white rounded">
<SimpleUICarousel contents={nodeContentTypes} width={400} height={200} />
</div>
);
} else if (node.type === 'my-video') {
return (
<div className="relative h-56 w-72 rounded-lg overflow-hidden">
<video playsInline className="h-full w-full object-cover overflow-clip-margin-content-box overflow-clip" controls autoPlay loop muted src="https://relation-graph.com/images/video-dribbble.mp4" />
这段代码展示了样式表如何把选中态强调效果,从库默认节点框转移到自定义插槽内容上。
.rg-node-peel.rg-node-checked {
.rg-node {
box-shadow: none;
& > div {
box-shadow: 0 0 0 8px var(--rg-checked-item-bg-color);
}
}
}
这段代码展示了共享辅助窗口如何修改实时图的交互模式,并通过图实例 API 支持图片导出。
<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 }); }}
/>
这个示例的独特之处
与附近的 node-style2、node-style3 和 node-style4 等示例相比,这个示例在需求是“替换为异构节点主体”而不是“细化主题样式”时更有价值。对比数据在这一点上很明确:其他示例也可能使用 RGSlotOnNode,但它们通常是在重复同一种视觉系统,或更侧重于宽泛的样式对比。而这个示例则是在一棵紧凑的树中,同时把多种明显不同的内容类型放入节点中。
最强的区分性组合是:
- 从左到右的树布局,
- 透明的默认节点主体,
- 半透明白色曲线连接线,
- 黄绿色全高画布,
- 由多个
node.type驱动的插槽主体, - 包裹自定义插槽内容的选中态样式,
- 复用的悬浮辅助窗口,用于画布设置和导出。
当内置节点渲染器不够用时,这组特性让它比 node-style2 更适合作为起点;而当目标不是广泛的技术对比,而是寻找一个在节点内混合 HTML 与媒体内容的实用模式时,它又比 node 更聚焦。
悬浮辅助窗口应被视为次要部分。对比数据明确提醒不要把它描述成独有能力,因为这套外壳在附近多个示例中都会复用。这里真正有辨识度的经验,是基于插槽的节点内容系统,以及为让选中态和整体外观依然协调而必须做出的样式调整。
这种模式还能应用到哪里
这种模式非常适合那些必须在同一张图里混合多种节点展示方式、但又不改变图数据契约的场景:
- 产品或内容地图,其中有些节点是标签,有些是媒体预览,有些是操作卡片,
- 工作流仪表盘,其中审核节点、决策按钮和状态徽章需要不同的 HTML 主体,
- 教育或叙事类图谱,其中根节点充当特性卡片,子节点充当紧凑标签或检查点,
- 监控视图,其中告警节点、指标胶囊和下钻操作共享同一拓扑,但不共享相同的渲染需求。
同样的结构也可以继续扩展,例如把内联示例数据替换为 API 数据、把 node.type 扩展为更丰富的渲染注册表,或者将嵌入式节点控件连接到真实业务动作,而不是仅仅做 console logging。