自定义视图与背景(#view / #background)
#view、#background 和 #canvas-above 都属于“图谱主体之外或之上”的扩展层,但它们的坐标系和用途不同。
#view:相对 RelationGraph 组件视口固定,不跟随画布移动缩放,适合工具栏、菜单、面板。#background:相对视口固定,位于画布底层,适合背景图、水印、主题底纹。#canvas-above:使用画布坐标,跟随画布移动缩放,但显示在节点/连线上方。
1. #view 视口层
#view 渲染在:
<div class="rg-graph-plugs">
<div class="rg-view-slot">
<!-- #view -->
</div>
</div>
它位于图谱视口最上层,不随 canvasOffset 和 canvasZoom 变化。
| 特性 | 说明 |
|---|---|
| 坐标系 | RelationGraph 组件视口坐标,左上角为 (0, 0) |
| 是否跟随画布缩放/平移 | 否 |
| 默认事件 | 外层 .rg-graph-plugs 为 pointer-events: none |
| 适合内容 | 工具栏、右键菜单、属性面板、缩略图、编辑控制器、悬浮提示 |
| 不适合内容 | 应该跟随节点/画布移动的分组框、标注、区域 |
各平台写法
| 平台 | 写法 |
|---|---|
| Vue 3 / Vue 2 | <template #view>...</template> |
| React | <RGSlotOnView>...</RGSlotOnView> |
| Svelte | <div slot="view">...</div> |
事件注意事项
#view 外层默认 pointer-events: none。如果你放的是可点击 UI,需要让实际 UI 元素启用事件:
<div class="my-panel rg-events-all">...</div>
或:
<div class="my-panel" style="pointer-events: auto;">...</div>
不要给整个全屏覆盖层开启事件,除非你明确要拦截图谱操作;否则它会挡住画布拖拽、节点拖拽和线条点击。
2. #view 示例:固定工具栏
Vue3 示例:
<script setup lang="ts">
import { shallowRef } from 'vue';
import type { RelationGraphInstance } from '@relation-graph/vue';
const graphInstance = shallowRef<RelationGraphInstance | null>(null);
function onReady(instance: RelationGraphInstance) {
graphInstance.value = instance;
}
function centerGraph() {
graphInstance.value?.moveToCenter();
}
function zoomIn() {
graphInstance.value?.zoom(20);
}
function zoomOut() {
graphInstance.value?.zoom(-20);
}
</script>
<template>
<RelationGraph
:options="graphOptions"
:initial-data="graphData"
@onReady="onReady"
>
<template #view>
<div class="floating-toolbar rg-events-all">
<button @click="centerGraph">居中</button>
<button @click="zoomIn">放大</button>
<button @click="zoomOut">缩小</button>
</div>
</template>
</RelationGraph>
</template>
.floating-toolbar {
position: absolute;
right: 12px;
top: 12px;
display: flex;
gap: 6px;
padding: 6px;
border-radius: 6px;
background: #ffffff;
box-shadow: 0 2px 10px rgba(15, 23, 42, 0.16);
}
说明:
position: absolute的定位基准是 RelationGraph 组件视口。- 工具栏不会随画布缩放。
rg-events-all让按钮可以点击。
3. #view 示例:右键菜单
右键菜单属于视口层,因为它应该出现在鼠标所在的屏幕位置,而不是固定到某个画布坐标。
<script setup lang="ts">
import { ref, shallowRef } from 'vue';
import type { RelationGraphInstance } from '@relation-graph/vue';
const graphInstance = shallowRef<RelationGraphInstance | null>(null);
const menu = ref({
visible: false,
x: 0,
y: 0,
canvasX: 0,
canvasY: 0
});
function onReady(instance: RelationGraphInstance) {
graphInstance.value = instance;
}
function onContextmenu(
event,
objectType,
object,
eventPositionOnCanvas,
eventPositionOnView
) {
menu.value = {
visible: true,
x: eventPositionOnView.x,
y: eventPositionOnView.y,
canvasX: eventPositionOnCanvas.x,
canvasY: eventPositionOnCanvas.y
};
}
function createNodeHere() {
if (!graphInstance.value) return;
graphInstance.value.addNodes([
{
id: `node-${Date.now()}`,
text: '新节点',
x: menu.value.canvasX,
y: menu.value.canvasY
}
]);
menu.value.visible = false;
}
</script>
<template>
<RelationGraph
:options="graphOptions"
:initial-data="graphData"
@onReady="onReady"
@onContextmenu="onContextmenu"
>
<template #view>
<div
v-if="menu.visible"
class="context-menu rg-events-all"
:style="{ left: menu.x + 'px', top: menu.y + 'px' }"
>
<button @click="createNodeHere">在这里创建节点</button>
</div>
</template>
</RelationGraph>
</template>
onContextmenu 的事件参数会提供事件目标类型、目标对象、画布坐标和视口坐标。这里同时保存视口坐标和画布坐标:
| 坐标 | 用途 |
|---|---|
menu.x/menu.y |
定位右键菜单 |
menu.canvasX/menu.canvasY |
创建节点时作为节点画布坐标 |
4. #canvas-above 画布前景层
#canvas-above 和 #canvas 使用同一画布坐标系,但层级更高。
当前渲染位置:
<div class="rg-map-canvas rg-canvas-above">
<div class="rg-canvas-slot rg-canvas-slot-above">
<!-- #canvas-above -->
</div>
</div>
| 特性 | 说明 |
|---|---|
| 坐标系 | 画布坐标 |
| 是否跟随画布缩放/平移 | 是 |
| 层级 | 节点/连线上方 |
| 默认事件 | 受 .rg-map-canvas { pointer-events: none; } 影响 |
| 适合内容 | 框选提示、拖拽预览、对齐辅助线、节点上方临时锚点 |
示例:在画布坐标中显示一个临时选择框:
<template #canvas-above>
<div
v-if="selectionBox.visible"
class="selection-box"
:style="{
left: selectionBox.x + 'px',
top: selectionBox.y + 'px',
width: selectionBox.width + 'px',
height: selectionBox.height + 'px'
}"
/>
</template>
.selection-box {
position: absolute;
box-sizing: border-box;
border: 1px solid #3b82f6;
background: rgba(59, 130, 246, 0.12);
pointer-events: none;
}
如果前景层内容需要点击,同样要加 rg-events-all 或 pointer-events: auto。
5. #background 背景层
#background 渲染在:
<div class="rg-map-background">
<!-- #background -->
</div>
| 特性 | 说明 |
|---|---|
| 坐标系 | RelationGraph 组件视口坐标 |
| 是否跟随画布缩放/平移 | 否 |
| 层级 | 画布底层 |
| 默认事件 | .rg-map-background 为 pointer-events: none |
| 适合内容 | 背景图、水印、主题底纹、静态装饰 |
Vue 写法:
<template #background>
<div class="graph-background">
<div class="watermark">Relation Graph</div>
</div>
</template>
React 写法:
import { RelationGraph, RGBackground } from '@relation-graph/react';
<RelationGraph options={graphOptions} initialData={graphData}>
<RGBackground>
<div className="graph-background">
<div className="watermark">Relation Graph</div>
</div>
</RGBackground>
</RelationGraph>
样式:
.graph-background {
position: absolute;
inset: 0;
background:
linear-gradient(rgba(148, 163, 184, 0.16) 1px, transparent 1px),
linear-gradient(90deg, rgba(148, 163, 184, 0.16) 1px, transparent 1px);
background-size: 24px 24px;
}
.graph-background .watermark {
position: absolute;
right: 18px;
bottom: 14px;
font-size: 12px;
color: rgba(100, 116, 139, 0.5);
}
说明:
- 背景层不随画布缩放,因此适合“屏幕背景”,不适合“图谱坐标中的网格”。如果网格需要和节点一起缩放移动,请放到
#canvas。 - 当前背景是图谱 DOM 的一部分。导出图片时,自定义背景通常会参与导出流程;如果背景中使用跨域图片,仍需注意浏览器 canvas 污染限制。
6. 内置小组件放在哪一层
relation-graph 暴露了一些内置组件。它们通常更适合放在 #view 中。
| 组件 | 推荐层 | 说明 |
|---|---|---|
RGToolBar / RGMiniToolBar |
#view |
固定工具栏,不应随画布缩放 |
RGMiniView |
#view |
缩略图通常固定在角落 |
RGEditingNodeController |
#view 或内部编辑层 |
节点编辑控制器,通常根据节点位置映射到视口 |
RGEditingLineController |
#view 或内部编辑层 |
连线编辑控制器 |
RGEditingConnectController |
#view 或内部编辑层 |
连线创建/连接控制器 |
RGWatermark |
#background 或 #view |
纯背景水印放 background,固定覆盖水印放 view |
RGBackground |
#background / React children |
React 中用于识别背景 slot |
具体使用方式可能随平台封装不同而略有差异。原则是:固定 UI 放 #view,背景装饰放 #background,画布坐标内容放 #canvas 或 #canvas-above。
7. 与 showToolBar 的关系
options.showToolBar 控制内置小工具栏是否显示。源码中当 showToolBar 为真时会自动渲染内置 GraphXsToolBar。
| 配置 | 行为 |
|---|---|
showToolBar: true |
显示内置工具栏 |
showToolBar: false |
不显示内置工具栏,你可以在 #view 中放自定义工具栏 |
如果你要完全自定义工具栏,通常设置:
const graphOptions = {
showToolBar: false
};
然后在 #view 中实现自己的缩放、居中、刷新、导出等操作。
8. 层级选择指南
| 需求 | 推荐层 | 原因 |
|---|---|---|
| 固定在右上角的工具栏 | #view |
不随画布缩放 |
| 节点右侧的业务悬浮卡片,随节点移动 | #canvas-above 或节点插槽 |
需要画布坐标 |
| 右键菜单 | #view |
菜单位置来自鼠标视口坐标 |
| 创建节点时的坐标提示 | #view + 坐标换算 |
UI 固定,业务坐标用 API 换算 |
| 分组背景、泳道 | #canvas |
属于图谱坐标且应在节点下方 |
| 框选矩形 | #canvas-above |
属于图谱坐标且要在节点上方 |
| 静态水印 | #background |
不需要交互,不随画布移动 |
| 跟随图谱缩放的网格 | #canvas |
使用画布坐标 |
| 不随图谱缩放的背景网格 | #background |
使用视口坐标 |
9. 常见问题
为什么 #view 里的按钮点不了?
因为外层 .rg-graph-plugs 默认 pointer-events: none。给实际面板或按钮添加 class="rg-events-all" 或 style="pointer-events:auto"。
为什么右键菜单在缩放后位置不对?
右键菜单本身应使用视口坐标定位:getViewXyByEvent(event)。如果菜单里的操作要在画布上创建节点,再额外把视口坐标转换为画布坐标:getCanvasXyByViewXy(viewPoint)。
为什么背景图不会跟着图谱移动?
#background 是视口背景,不跟随画布。如果需要背景图与节点同步移动缩放,放到 #canvas。
为什么 #canvas-above 的内容挡住了节点?
它本来就在节点/连线上方。如果只是视觉提示,设置 pointer-events: none;如果它只在局部需要交互,只给具体按钮/锚点开启事件。
#view 能不能读取当前缩放和偏移?
可以通过 hooks 或实例 API 读取图谱状态,例如 RGHooks.useGraphInstance()、RGHooks.useViewInformation()。Vue3 当前还导出了 RGHooks.useGraphOptions(),React/Svelte 可按各自平台使用 useGraphStore() 或更具体的状态 hook。如果需要把画布坐标映射到视口,优先使用 getViewXyByCanvasXy。