自定义节点企业服务宣传卡片
这个示例把固定布局 `RelationGraph` 当作展示画布,通过 `RGSlotOnNode` 渲染一张超大企业宣传卡。它还复用共享悬浮辅助窗提供滚轮/拖拽模式切换和图片导出,更适合作为图谱承载品牌化 UI 的参考,而非布局变化示例。
将 relation-graph 用作固定式宣传卡片画布
这个示例构建了什么
这个示例构建了一个全屏 RelationGraph 视图,它的行为更像是一个展示面板,而不是关系图。示例自带的数据只包含一个根节点且没有连线,这个根节点会被替换为一张 1200 x 600 的企业会员卡片,其中包含品牌文案、服务要点、外部链接,以及一个次级的 3D 内嵌卡片。
用户可以拖动或最小化浮动辅助窗口、打开设置浮层、切换滚轮与画布拖拽行为,并将当前画布导出为图片。这个示例最值得研究的重点并不是布局变化,而是如何把一个固定布局的图,当作承载超大自定义 React 组合内容的宿主画布。
数据是如何组织的
图数据直接在 initializeGraph() 中以内联方式声明为一个很小的 RGJsonData 对象,其中包含 rootId: 'root'、一个节点,以及一个空的 lines 数组。渲染图之前,没有获取数据的步骤、没有外部转换层,也没有额外的派生定位处理。
唯一真正的预处理步骤是执行顺序控制:代码会等待 setJsonData() 完成,然后再调用 moveToCenter() 和 zoomToFit(),以便在 relation-graph 完成节点渲染尺寸测量后再调整视口。在生产系统中,同样的结构可以表示落地页风格的产品面板、品牌化门户入口、仪表盘头图卡片,或任何其他仍然能从图视口控制与导出能力中受益的单一焦点卡片。
relation-graph 是如何使用的
index.tsx 使用 RGProvider 包裹页面,这让图组件和浮动辅助窗口都可以从同一个图上下文中使用 relation-graph hooks。在 MyGraph.tsx 中,RGHooks.useGraphInstance() 是主要的集成入口:它会加载内联数据、在挂载后将视图居中,并让视口适配内容。
图选项将画布保持在 layoutName: 'fixed',并使用矩形节点、正交默认连线、上下连接点、零节点边框,以及中性的兜底颜色。这个固定布局很重要,因为该示例并不是让 relation-graph 去计算一个网络布局,而是把图当作承载编排内容的稳定画布。
主要的渲染定制点是 RGSlotOnNode。当插槽遇到 root 节点时,它会渲染 RootNodeContent;否则会回退为一个简单的带边框文本块。尽管导入了一些相关符号,但实际交付的渲染路径中并没有启用中的画布或视图插槽。
共享的查看器工具来自 DraggableWindow 及其 CanvasSettingsPanel。这个辅助组件会通过 RGHooks.useGraphStore() 读取当前交互模式,再通过 graphInstance.setOptions(...) 在运行时更新这些模式,并借助 prepareForImageGeneration()、getOptions()、restoreAfterImageGeneration() 以及共享的 modern-screenshot 封装来导出图像。样式分为两部分:示例自身的 SCSS 用于重设图内部元素(如线标签和 logo 填充)的主题;可复用的 Simple3DCard 样式则为内嵌卡片增加悬停倾斜、辉光和全息网格效果。
关键交互
- 浮动辅助窗口可以通过标题栏拖动。
- 辅助窗口可以最小化和展开。
- 设置按钮会在辅助窗口内打开一个浮层面板,点击半透明背景会关闭它。
- 设置面板可以将滚轮行为切换为
scroll、zoom和none。 - 设置面板可以将画布拖拽行为切换为
selection、move和none。 Download Image操作会先准备图 DOM、截取当前视图、下载图片,然后恢复图状态。- 自定义根卡片中包含指向
https://relation-graph.com/services的外部链接。 - 节点和连线点击处理器已经接入,但在示例自带的数据集中,它们只会记录对象,而且没有可交互的连线。
关键代码片段
这段代码表明,该示例有意让 relation-graph 保持在固定模式,并将其用作手工组合内容的承载面。
const graphOptions: RGOptions = {
debug: false,
layout: {
layoutName: 'fixed'
},
defaultNodeShape: RGNodeShape.rect,
defaultLineShape: RGLineShape.StandardOrthogonal,
defaultJunctionPoint: RGJunctionPoint.tb,
defaultNodeBorderWidth: 0,
defaultLineColor: '#666',
defaultNodeColor: '#fff'
};
这段代码证明,数据集是在本地构建的,并且会在视口居中与适配前先完成加载。
const initializeGraph = async () => {
const myJsonData: RGJsonData = {
rootId: 'root',
nodes: [
{ id: 'root', text: 'Node' }
],
lines: []
};
await graphInstance.setJsonData(myJsonData);
graphInstance.moveToCenter();
graphInstance.zoomToFit();
};
这个插槽渲染器是核心技巧:在保留其他节点兜底渲染方式的同时,把根节点替换为完整的自定义 React 布局。
<RGSlotOnNode>
{({ node }) => {
return (
node.id === 'root' ? (
<RootNodeContent />
) : (
<div className="px-2 min-w-[100px] rounded border border-gray-500 flex items-center justify-center w-full h-full text-sm text-slate-800 font-medium select-none">
{node.text}
</div>
)
);
}}
</RGSlotOnNode>
这段摘录展示了,辅助浮层会直接在运行中的实例上修改 relation-graph 的交互模式,而不是重新构建图数据。
<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 }); }}
/>
这个导出流程展示了,示例如何在截图前准备 relation-graph,把画布 DOM 交给截图辅助函数,并在结束后恢复图状态。
const downloadImage = async () => {
const canvasDom = await graphInstance.prepareForImageGeneration();
let graphBackgroundColor = graphInstance.getOptions().backgroundColor;
if (!graphBackgroundColor || graphBackgroundColor === 'transparent') {
graphBackgroundColor = '#ffffff';
}
const imageBlob = await domToImageByModernScreenshot(canvasDom, {
backgroundColor: graphBackgroundColor
});
if (imageBlob) {
downloadBlob(imageBlob, 'my-image-name');
}
await graphInstance.restoreAfterImageGeneration();
};
这个示例的独特之处
根据对比数据,这个示例的特殊之处在于:它把图拓扑维持得极小,却把几乎所有复杂度都集中到一个通过插槽渲染的根节点中。它的独特组合包括固定布局初始化、挂载时 setJsonData() 后再执行居中与适配、使用 RGSlotOnNode 完整替换节点内容、嵌入外部链接、复用 Simple3DCard,以及在同一屏幕中使用共享的浮动设置与导出外壳。
与 table-relationship 相比,这里的自定义 HTML 不是结构性的,也不承担行级端点或可见数据连线的职责。与 css-theme 相比,重点不在于运行时切换图主题,而在于一个预先编排好的品牌化卡片。与 use-dagre-layout-2 相比,这里的固定布局只是一个容器;并不存在二次布局算法、间距控制或由 minimap 驱动的工作流。
对比记录还指出了一个重要的反向结论:不应把它描述为一个真正的多布局示例。实际交付的实现是在 layoutName: 'fixed' 下使用一个根节点,真正持久的启示是“将图作为展示画布进行组合”,而不是布局变化。
这种模式还能用在哪里
这种模式非常适合这样的场景:图画布需要承载一个占主导地位的自定义界面,而不是一个密集的关系图。示例包括企业升级面板、产品能力概览、发布或营销卡片、引导检查点,以及仍然希望保留图平移、缩放行为和图片导出能力的仪表盘头部模块。
当团队希望尽早引入图基础设施,而更丰富的拓扑结构尚未出现时,它也很有用。像这样单卡片、固定布局的示例,今天可以作为品牌化入口状态、高级功能说明,或门户卡片存在,之后再逐步演变为更具连接性的图体验,而无需更换底层查看器外壳。