插槽系统概览
relation-graph 的插槽用于替换或扩展图谱内部的可视层。它们不是简单的“HTML 挂载点”,而是分别挂在节点层、连线层、画布坐标层、视口固定层和背景层上。不同插槽所在层级不同,坐标系、缩放行为、事件处理方式也不同。
如果你只是调整颜色、线宽、圆角、字体,优先使用 RGOptions、节点/连线数据字段或 CSS 变量;如果你需要自定义 DOM/SVG 结构、在节点上放业务按钮、给连线增加多段文本、在画布上放分组框、在视口上放工具栏,再使用插槽。
1. 插槽总览
| 插槽 | Vue 3 / Vue 2 | React | Svelte | 所在层级 | 是否跟随画布缩放/平移 | 主要用途 |
|---|---|---|---|---|---|---|
| 节点内容 | #node |
<RGSlotOnNode> 或 nodeSlot |
slot="node" |
节点外壳 .rg-node 内部 |
是 | 卡片节点、头像节点、状态徽标、业务按钮 |
| 节点展开按钮 | #node-expand-button |
nodeExpandButtonSlot |
slot="node-expand-button" |
节点外壳内,默认展开按钮位置 | 是 | 替换展开/收起按钮 |
| 连线内容 | #line |
<RGSlotOnLine> 或 lineSlot |
slot="line" |
SVG 连线层 | 是 | 自定义 path、箭头、线条标签、连线状态 |
| 画布底层 | #canvas 或默认插槽 |
<RGSlotOnCanvas> 或普通 children |
slot="canvas" 或默认插槽 |
节点/连线下方 | 是 | 分组框、泳道、坐标辅助线、图谱底层业务标记 |
| 画布前景 | #canvas-above |
<RGSlotOnCanvasAbove> |
slot="canvas-above" |
节点/连线上方 | 是 | 框选提示、悬浮锚点、画布坐标中的覆盖层 |
| 视口层 | #view |
<RGSlotOnView> |
slot="view" |
图谱视口最上层 | 否 | 固定工具栏、右键菜单、浮层面板、缩略图 |
| 背景层 | #background |
<RGBackground> |
slot="background" |
.rg-map-background |
否 | 背景图、水印、主题底纹 |
说明:
- Vue3 当前公开包名在站点文档中统一写作
@relation-graph/vue;Vue2 为@relation-graph/vue2。 - React 既可以使用
<RGSlotOnNode>/<RGSlotOnLine>等插槽组件,也可以直接通过nodeSlot/lineSlot/nodeExpandButtonSlot属性传入渲染函数。两种写法不要混用同一个插槽,否则源码会抛出错误。 - Vue3/React/Svelte 的默认子内容都会被放到画布底层,等价于
canvas层,适合放跟随图谱移动的辅助内容。
2. 渲染层级
图谱内部的主要层级可以理解为:
| 从下到上 | 内部容器 | 公开插槽/内容 | 行为 |
|---|---|---|---|
| 1 | .rg-map-background |
background |
视口固定,不随画布缩放;通常无交互 |
| 2 | .rg-map-canvas.rg-canvas-behind |
canvas / 默认子内容 |
使用画布坐标,跟随 canvasOffset 和 canvasZoom |
| 3 | EasyView / 缩略辅助层 | 内部使用 | 用于性能模式或简化视图 |
| 4 | .rg-map-canvas |
节点、连线、节点/连线插槽 | 主图谱层,节点和连线都在这里渲染 |
| 5 | .rg-map-canvas.rg-canvas-above |
canvas-above |
使用画布坐标,但显示在节点/连线之上 |
| 6 | .rg-graph-plugs .rg-view-slot |
view |
视口固定,不随画布缩放 |
canvas、canvas-above、节点、连线所在的 .rg-map-canvas 会应用如下变换:
transform: translate(canvasOffset.x, canvasOffset.y) scale(canvasZoom / 100);
transform-origin: 0 0;
因此这些内容的 left/top/x/y 都应当使用画布坐标。view 和 background 不应用这层变换,它们的 left/top 是 RelationGraph 组件视口坐标。
3. 坐标系
relation-graph 中最常接触三类坐标:
| 坐标系 | 示例 | 说明 |
|---|---|---|
| 客户端坐标 | MouseEvent.clientX/clientY |
浏览器窗口坐标,一般来自鼠标或触摸事件 |
| 视口坐标 | graphInstance.getViewXyByEvent(e) |
相对于 RelationGraph 组件左上角的坐标,适合放 view 层菜单 |
| 画布坐标 | graphInstance.getCanvasXyByViewXy(viewPoint) |
图谱内部坐标,节点 x/y、画布插槽定位、连线端点都使用它 |
常用换算 API:
| API | 入参 | 返回 | 用途 |
|---|---|---|---|
getViewXyByEvent(e) |
鼠标/触摸事件 | { x, y } 视口坐标 |
根据事件位置显示视口层菜单 |
getViewXyByClientXy({ x, y }) |
浏览器客户端坐标 | { x, y } 视口坐标 |
从外部事件换算到组件内部 |
getCanvasXyByClientXy({ x, y }) |
浏览器客户端坐标 | { x, y } 画布坐标 |
根据鼠标位置创建节点 |
getCanvasXyByViewXy({ x, y }) |
视口坐标 | { x, y } 画布坐标 |
将固定层操作映射到画布 |
getViewXyByCanvasXy({ x, y }) |
画布坐标 | { x, y } 视口坐标 |
把节点/业务点映射到固定浮层 |
示例:在鼠标位置新增一个节点时,应当使用画布坐标:
const canvasPoint = graphInstance.getCanvasXyByClientXy({
x: event.clientX,
y: event.clientY
});
graphInstance.addNodes([
{
id: 'new-node',
text: '新节点',
x: canvasPoint.x,
y: canvasPoint.y
}
]);
4. 插槽参数类型
节点、连线和展开按钮插槽会传入结构化参数。
节点插槽 #node
export type RGNodeSlotProps = {
node: RGNode;
defaultExpandHolderPosition?: string;
dragging?: boolean;
checked?: boolean;
};
| 字段 | 类型 | 说明 |
|---|---|---|
node |
RGNode |
当前渲染的运行时节点对象。包含 id、text、type、data、位置、样式、状态等字段 |
defaultExpandHolderPosition |
string | undefined |
全局默认展开按钮位置,来源于 options.defaultExpandHolderPosition |
dragging |
boolean | undefined |
当前节点是否处于拖拽状态 |
checked |
boolean | undefined |
当前节点是否是内部“checked”节点,用于编辑/控制器状态 |
节点展开按钮插槽 #node-expand-button
export type RGNodeExpandHolderProps = {
node: RGNode;
expandOrCollapseNode: (e: MouseEvent | TouchEvent) => void;
expandHolderPosition?: string;
};
| 字段 | 类型 | 说明 |
|---|---|---|
node |
RGNode |
当前节点 |
expandOrCollapseNode |
(e) => void |
内置展开/收起逻辑,通常在自定义按钮点击时调用 |
expandHolderPosition |
string | undefined |
实际展开按钮位置,常见值为 left、right、top、bottom、hide |
连线插槽 #line
export type RGLineSlotProps = {
lineConfig: RGGenerateLineConfig;
checked?: boolean;
defaultLineTextOnPath?: boolean;
graphInstanceId?: string;
};
| 字段 | 类型 | 说明 |
|---|---|---|
lineConfig |
RGGenerateLineConfig |
已由实例根据 RGLine 或 RGFakeLine 计算出的连线渲染配置 |
checked |
boolean | undefined |
当前线是否是内部“checked”线 |
defaultLineTextOnPath |
boolean | undefined |
全局默认是否让线条文本沿路径显示 |
graphInstanceId |
string | undefined |
当前图谱实例 ID,常用于生成唯一的 SVG path id |
RGGenerateLineConfig 的核心字段:
| 字段 | 类型 | 说明 |
|---|---|---|
line |
RGLine | RGFakeLine |
当前要渲染的线。普通线来自数据模型,假线来自拖拽/编辑状态 |
from |
RGLineTarget | undefined |
起点目标信息,可能是节点、节点点位、画布点等 |
to |
RGLineTarget | undefined |
终点目标信息 |
totalLinesBetweenNodes |
number | undefined |
两个节点之间的线条总数,用于多线偏移 |
currentLineIndex |
number | undefined |
当前线在多线组中的序号 |
defaultOptions |
any |
当前渲染所需的默认配置快照 |
5. 事件处理原则
图谱内部容器为了让拖拽、框选、连线点击等行为稳定,很多外层元素默认设置了 pointer-events: none。这意味着你在 canvas、canvas-above、view 中放的交互元素,如果需要点击,需要显式启用事件。
推荐做法:
<button class="rg-events-all">操作</button>
或:
<button style="pointer-events: auto;">操作</button>
注意:
- 节点插槽位于
.rg-node内部,节点外壳已经处理了拖拽开始事件。节点内按钮如果不希望触发节点拖拽,需要在按钮事件上stopPropagation。 - 连线插槽位于 SVG 层,默认连线使用
.rg-line-bg承接点击,实际可见 path 常常保持较细线宽。自定义连线时也建议保留一个较粗的透明点击路径。 view层外壳.rg-graph-plugs默认pointer-events: none,固定工具栏、菜单、面板本身要开启pointer-events。
6. 数据字段、CSS 与插槽的职责边界
推荐分工:
| 层级 | 负责内容 | 示例 |
|---|---|---|
| 数据字段 | 语义性外观、状态、业务数据 | node.color、line.color、line.lineShape、node.data.status |
RGOptions |
全局默认值和交互规则 | defaultNodeColor、defaultLineWidth、defaultLineShape、disableDragNode |
| CSS / CSS 变量 | 主题、状态样式、局部覆盖 | .rg-node-selected、.rg-line-checked、--rg-node-color |
| 插槽 | DOM/SVG 结构、复杂内容、业务控件 | 卡片节点、多文本连线、分组框、工具栏 |
| 实例 API | 数据变更、布局、视口、事件协作 | addNodes、updateLine、getCanvasXyByClientXy |
不要把所有逻辑都写进插槽中。例如线条颜色这种影响主图、缩略图和导出一致性的内容,优先放在 line.color 或 defaultLineColor;节点类型、状态、标签等业务信息放在 node.data;插槽只负责把这些信息渲染出来。
7. 平台写法速查
Vue 3
<RelationGraph :options="graphOptions" :initial-data="graphData">
<template #node="{ node, checked, dragging }">
<div class="my-node">{{ node.text }}</div>
</template>
<template #line="{ lineConfig, checked, graphInstanceId }">
<MyLine :line-config="lineConfig" :checked="checked" :graph-instance-id="graphInstanceId" />
</template>
<template #canvas>
<div class="group-box" style="position:absolute;left:0;top:0;">分组</div>
</template>
<template #view>
<div class="toolbar rg-events-all">工具栏</div>
</template>
</RelationGraph>
React
import {
RelationGraph,
RGSlotOnNode,
RGSlotOnLine,
RGSlotOnCanvas,
RGSlotOnView
} from '@relation-graph/react';
<RelationGraph options={graphOptions} initialData={graphData}>
<RGSlotOnNode>
{({ node, checked, dragging }) => (
<div className="my-node">{node.text}</div>
)}
</RGSlotOnNode>
<RGSlotOnLine>
{({ lineConfig, checked, graphInstanceId }) => (
<MyLine lineConfig={lineConfig} checked={checked} graphInstanceId={graphInstanceId} />
)}
</RGSlotOnLine>
<RGSlotOnCanvas>
<div className="group-box">分组</div>
</RGSlotOnCanvas>
<RGSlotOnView>
<div className="toolbar rg-events-all">工具栏</div>
</RGSlotOnView>
</RelationGraph>
Svelte
<RelationGraph {options} initialData={graphData}>
<div slot="node" let:node let:checked let:dragging class="my-node">
{node.text}
</div>
<div slot="canvas" class="group-box">
分组
</div>
<div slot="view" class="toolbar rg-events-all">
工具栏
</div>
</RelationGraph>
8. 常见场景选择
| 需求 | 推荐插槽/方式 | 原因 |
|---|---|---|
| 节点显示头像、标签、按钮 | node |
节点外壳仍由组件处理定位、拖拽和状态 |
| 替换树节点展开按钮 | node-expand-button |
能复用 expandOrCollapseNode,不需要自己维护树状态 |
| 给不同类型连线画不同图形 | line |
可使用 lineConfig.line.type 分发模板 |
| 给连线添加多个标签 | line + RGLineText 或自定义 HTML/SVG |
默认文本只渲染一个标签 |
| 画泳道、分组框、网格 | canvas |
跟随画布坐标移动缩放 |
| 画框选提示、拖拽辅助锚点 | canvas-above |
跟随画布坐标,同时显示在图元上方 |
| 固定工具栏、右键菜单、属性面板 | view |
不受画布缩放影响 |
| 水印、背景图、纯装饰底纹 | background |
不影响节点和连线层级 |