JavaScript is required

自定义视图与背景(#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>

它位于图谱视口最上层,不随 canvasOffsetcanvasZoom 变化。

特性 说明
坐标系 RelationGraph 组件视口坐标,左上角为 (0, 0)
是否跟随画布缩放/平移
默认事件 外层 .rg-graph-plugspointer-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-allpointer-events: auto

5. #background 背景层

#background 渲染在:

<div class="rg-map-background">
  <!-- #background -->
</div>
特性 说明
坐标系 RelationGraph 组件视口坐标
是否跟随画布缩放/平移
层级 画布底层
默认事件 .rg-map-backgroundpointer-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

10. 下一步阅读