揭秘 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 模型转化为屏幕上可见的图形,并处理交互。
相关推荐
WeiXiao_Hyy30 分钟前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端
吃杠碰小鸡1 小时前
高中数学-数列-导数证明
前端·数学·算法
kingwebo'sZone1 小时前
C#使用Aspose.Words把 word转成图片
前端·c#·word
xjt_09011 小时前
基于 Vue 3 构建企业级 Web Components 组件库
前端·javascript·vue.js
我是伪码农1 小时前
Vue 2.3
前端·javascript·vue.js
夜郎king2 小时前
HTML5 SVG 实现日出日落动画与实时天气可视化
前端·html5·svg 日出日落
辰风沐阳2 小时前
JavaScript 的宏任务和微任务
javascript
夏幻灵3 小时前
HTML5里最常用的十大标签
前端·html·html5
冰暮流星3 小时前
javascript之二重循环练习
开发语言·javascript·数据库
Mr Xu_3 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js