揭秘 X6 核心概念:Graph、Node、Edge 与 View

准备写关于x6的一个系列文章,大致有以下内容:

  1. X6 极速入门:手把手教你绘制第一个拓扑图
  2. 揭秘 X6 核心概念:Graph、Node、Edge 与 View
  3. 自定义节点与边
  4. X6 交互魔法:拖拽、连线、缩放、对齐线
  5. X6 布局之道:内置布局算法与自定义布局
  6. 基于 React + X6 构建可视化流程图编辑器
  7. X6 性能优化秘籍:应对大规模节点渲染的挑战
  8. X6源码分析
  9. 手写最小 X6(X6-mini)

一、 Graph

Graph 是 X6 的核心中的核心,它是所有元素的容器和管理者

1.1 核心职责

  • 容器管理:负责在指定的 DOM 容器中渲染整个图谱。
  • 事件中枢 :管理画布和其上所有元素的事件系统(点击、拖拽、缩放等)。
  • 视图操控 :提供对画布的平移、缩放、滚动、裁切等视图操作。
  • 元素管理 :负责所有节点和边的增、删、改、查(CRUD)
  • 渲染控制 :控制渲染时机(如 batchUpdate 批量更新)和渲染模式(SVG 或 HTML)。

1.2 代码示例

javascript 复制代码
import { Graph } from '@antv/x6';

// 创建 Graph 实例是一切的开始
const graph = new Graph({
  container: document.getElementById('container'),
  width: 1000,
  height: 800,
  grid: true, // 显示网格
  panning: true, // 启用画布拖拽
  mousewheel: true, // 启用滚轮缩放
  background: { color: '#F8F9FA' },
  // 图的主要交互行为都通过 `graph.options` 配置
  connecting: {
    snap: true, // 连线时自动吸附到节点
    allowEdge: false, // 不允许边连接边
    allowBlank: false, // 不允许连接到画布空白处
  },
});

// Graph 是数据管理器
// 1. 添加元素
const node = graph.addNode({ ... });
const edge = graph.addEdge({ ... });

// 2. 获取元素
graph.getNodes(); // 获取所有节点
graph.getCellById(node.id); // 根据ID获取元素(Cell是Node和Edge的基类)

// 3. 批量操作,性能关键!
graph.batchUpdate(() => {
  for (let i = 0; i < 1000; i++) {
    graph.addNode(createNode(i));
  }
});

// 4. 视图控制
graph.zoom(0.8); // 缩放到 80%
graph.translate(100, 50); // 平移画布
graph.centerContent(); // 将所有内容居中显示

// 5. 事件监听
graph.on('node:click', ({ node }) => { // 监听节点点击事件
  console.log(`Node ${node.id}被点击了!`);
});
graph.on('edge:connected', ({ edge }) => { // 监听连线成功事件
  console.log('一条新的连接建立了!', edge);
});

二、 Node & Edge

Node(节点)和 Edge(边)继承自共同的基类 Cell

2.1 共同的基类:Cell

Cell 提供了所有图形元素最基础的属性和方法:

  • id: 唯一标识符
  • shape : 元素的形状(如 'rect', 'circle', 'edge'),决定了其默认行为和渲染方式。
  • data : 用于存储业务数据的万能字段,这是将视图与业务模型关联的关键
  • zIndex: 控制元素的层级。
  • visible: 显示/隐藏元素。
  • attr : 最强大的属性,用于设置元素的样式(SVG 属性或 CSS)。

2.2 Node

节点通常代表一个实体

javascript 复制代码
// 创建一个节点,本质是创建了一个数据模型
const node = graph.addNode({
  id: 'server-1', // 强烈建议指定ID!
  shape: 'rect', // 指定形状
  x: 100,
  y: 100,
  width: 80,
  height: 40,
  label: 'Web Server', // 文本
  // 业务数据:这是将图形与你的应用逻辑连接起来的桥梁
  data: {
    ip: '192.168.1.1',
    status: 'online',
    capacity: 0.8,
  },
  // attr: 精细化控制样式(覆盖shape的默认样式)
  attrs: {
    // 对应SVG的 <rect> 元素
    body: { 
      fill: '#e6f7ff', 
      stroke: '#1890ff',
      strokeWidth: 2,
      rx: 4, // 圆角
      ry: 4,
    },
    // 对应SVG的 <text> 元素
    label: { 
      text: 'Web Server', // 会覆盖外层的label属性
      fontSize: 12,
      fill: '#333',
      refX: '50%', // 文本定位技巧:相对节点中心
      refY: '50%',
      textAnchor: 'middle', // 文本锚点居中
      textVerticalAnchor: 'middle',
    },
  },
});

// 后续更新节点
node.prop('attrs/body/fill', '#ffccc7'); // 更改填充色
node.prop('data/status', 'offline'); // 更新业务数据
// 或使用标准方法
node.attr('body/fill', '#ffccc7');

2.3 Edge

边代表节点之间的关系,如数据流、依赖、关联等。

javascript 复制代码
const edge = graph.addEdge({
  id: 'link-1',
  shape: 'edge',
  source: { cell: 'server-1' }, // 源节点ID
  target: { cell: 'db-1' },     // 目标节点ID
  // 边的业务数据
  data: {
    type: 'database_connection',
    bandwidth: '1Gbps',
  },
  // 边的样式控制
  attrs: {
    // 对应SVG的 <path> 元素(线条本身)
    line: {
      sourceMarker: { name: 'circle' }, // 起点标记
      targetMarker: { name: 'block' },  // 终点标记(箭头)
      stroke: '#9254de',
      strokeWidth: 1.5,
      strokeDasharray: '5, 5', // 虚线
    },
  },
  // 顶点(路径点),用于控制边的路径
  vertices: [
    { x: 250, y: 100 },
  ],
  // 标签(可以多个)
  labels: [
    {
      attrs: { label: { text: '1Gbps' } },
      position: 0.3, // 沿边位置,0-1
    },
  ],
  // 路由器(routing):决定路径如何路由(如:orth-正交,manhattan-曼哈顿)
  router: { name: 'manhattan' },
  // 连接器(connector):决定拐角样式(如:rounded-圆角)
  connector: { name: 'rounded' },
});

// 动态更新边
edge.attr('line/stroke', '#ff4d4f'); // 线条变红
edge.prop('vertices', [{ x: 300, y: 150 }]); // 修改路径点

三、 View

它定义了如何将数据模型转换为屏幕上的实际元素。

3.1 为什么需要 View?

X6 内置了大量 Shape(如 rect, circle, edge),每个 Shape 都对应一个默认的 View。这个默认的 View 知道如何根据模型的 attrs 去设置 SVG 元素的属性。

当你需要超越内置形状 时,View 的作用就凸显出来了:

  • 创建一个全新的、复杂的节点形状。
  • 在节点内部实现复杂的交互(如一个可点击的关闭按钮)。
  • 使用 HTML/DOM 来渲染节点内容(性能不如 SVG,但更灵活)。

3.2 自定义 View 示例:创建一个自定义节点

假设我们要创建一个带有关闭按钮的标签节点。

javascript 复制代码
import { Shape } from '@antv/x6';

// 1. 继承 Shape 注册一个新的节点形状
Graph.registerNode('custom-tag', class extends Shape.Rect {
  // 2. 定义默认属性(会合并到用户传入的attrs中)
  getDefaults() {
    return {
      ...super.getDefaults(),
      markup: [ // 3. 定义节点的DOM结构(Markup)
        {
          tagName: 'rect',
          selector: 'body',
        },
        {
          tagName: 'text',
          selector: 'label',
        },
        {
          tagName: 'path', // 一个"x"形的关闭图标
          selector: 'closeButton',
          attrs: {
            d: 'M 5 5 L 13 13 M 5 13 L 13 5',
            fill: 'none',
            stroke: '#999',
            strokeWidth: 1.5,
          },
        },
      ],
      attrs: {
        body: { ... },
        label: { ... },
        // 关闭按钮默认隐藏,鼠标悬浮时显示
        closeButton: { visibility: 'hidden' },
      },
    };
  }

  // 4. 自定义事件监听
  // 在View被初始化后调用(类似mounted)
  postprocess() {
    this.on('mouseenter', () => {
      this.attr('closeButton', { visibility: 'visible' });
    });
    this.on('mouseleave', () => {
      this.attr('closeButton', { visibility: 'hidden' });
    });

    // 监听关闭按钮的点击事件
    this.on('cell:closeButton:click', (e) => {
      e.stopPropagation(); // 阻止事件冒泡(避免触发cell:click)
      this.remove(); // 删除自己这个节点
    });
  }
}, true);

// 使用自定义节点
const tagNode = graph.addNode({
  shape: 'custom-tag', // 使用注册的形状名
  x: 50,
  y: 50,
  width: 100,
  height: 30,
  label: 'Important',
  attrs: {
    body: { fill: '#ffd591', stroke: '#ffa940', rx: 4, ry: 4 },
    label: { text: 'Important', fontSize: 12, fill: '#333', refX: 18 },
    closeButton: { 
      // 定义按钮位置,相对于节点
      transform: 'translate(85, 8)',
      stroke: { type: 'palette', palette: 'primaryColor' }, // 可使用主题色
    },
  },
});

关系图

四大核心概念的关系:

graph TD A[Graph
画布/舞台/管理器] -- 管理 --> B[Cell
元素基类] B -- 继承 --> C[Node
节点/实体] B -- 继承 --> D[Edge
边/关系] B -- 拥有 --> E[Data
业务数据] B -- 拥有 --> F[Attrs
样式属性] B -- 由...渲染 --> G[View
视图/渲染器] G -- 监听/响应 --> H[Events
用户交互事件] H -- 触发 --> A
  • Graph管理者 ,持有并管理所有 Cell(Node 和 Edge)。
  • Cell数据模型 ,其 attrs 决定样式,data 承载业务含义。
  • View渲染器 ,负责将 Cell 模型转化为屏幕上可见的图形,并处理交互。
相关推荐
qwy7152292581632 小时前
Vue中的Provide/Inject如何实现动态数据
前端·javascript·vue.js
艾小码2 小时前
告别重复代码!React自定义Hook让逻辑复用如此简单
前端·javascript·react.js
yoyoma2 小时前
react-infinite-scroll-component 使用注意事项
前端
快乐是一切2 小时前
PDF文件的交叉引用表(xref)与 trailer
前端
Never_Satisfied2 小时前
在JavaScript / HTML中,让<audio>元素中的多个<source>标签连续播放
开发语言·javascript·html
emma羊羊2 小时前
【CSRF】防御
前端·网络安全·csrf
Paddy哥3 小时前
html调起exe程序
前端·html
emma羊羊3 小时前
【CSRF】跨站请求伪造
前端·网络安全·csrf
患得患失9493 小时前
【ThreeJs】【HTML载入】Three.js 中的 CSS2DRenderer 与 CSS3DRenderer 全面解析
javascript·html·css3