AntV 入门系列:G6 图可视化实战

AntV 入门系列:G6 图可视化实战

什么是图可视化?

你有没有想过,如何直观地展示人与人之间的关系?或者如何画出一个系统的架构图?这就是图可视化要解决的问题!

图(Graph) 由两种元素组成:

  • 节点(Node):代表实体,比如人、公司、设备等
  • 边(Edge):代表实体之间的关系

G6 就是专门用来处理这种关系数据的神器!

案例一:创建一个简单的社交关系图谱

让我们从一个简单的例子开始------创建一个朋友关系图谱。

安装 G6

首先安装 G6 库:

bash 复制代码
npm install @antv/g6

创建 HTML 文件

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>社交关系图谱</title>
</head>
<body>
  <div id="container" style="width: 800px; height: 600px;"></div>
  <script src="./index.js"></script>
</body>
</html>

创建关系图谱

javascript 复制代码
import G6 from '@antv/g6';

// 定义数据:节点和边
const data = {
  nodes: [
    { id: 'Alice', label: 'Alice', x: 200, y: 300 },
    { id: 'Bob', label: 'Bob', x: 400, y: 150 },
    { id: 'Charlie', label: 'Charlie', x: 600, y: 300 },
    { id: 'David', label: 'David', x: 400, y: 450 },
  ],
  edges: [
    { source: 'Alice', target: 'Bob', label: '朋友' },
    { source: 'Bob', target: 'Charlie', label: '同事' },
    { source: 'Charlie', target: 'David', label: '同学' },
    { source: 'David', target: 'Alice', label: '家人' },
  ],
};

// 创建图实例
const graph = new G6.Graph({
  container: 'container',
  width: 800,
  height: 600,
  // 默认节点样式
  defaultNode: {
    size: 60,
    style: {
      fill: '#5B8FF9',
      stroke: '#5B8FF9',
      lineWidth: 2,
    },
    labelCfg: {
      style: {
        fill: '#fff',
        fontSize: 14,
      },
    },
  },
  // 默认边样式
  defaultEdge: {
    style: {
      stroke: '#e2e2e2',
      lineWidth: 2,
    },
    labelCfg: {
      autoRotate: true,
      style: {
        fill: '#666',
        fontSize: 12,
      },
    },
  },
});

// 加载数据
graph.data(data);

// 渲染图表
graph.render();

运行后你会看到一个漂亮的社交关系图!

案例二:组织结构图

让我们来创建一个公司的组织结构图。

数据准备

javascript 复制代码
const orgData = {
  nodes: [
    { id: 'CEO', label: 'CEO', x: 400, y: 50 },
    { id: 'CTO', label: 'CTO', x: 200, y: 180 },
    { id: 'COO', label: 'COO', x: 600, y: 180 },
    { id: 'TechLead', label: '技术负责人', x: 200, y: 310 },
    { id: 'ProductLead', label: '产品负责人', x: 600, y: 310 },
    { id: 'Dev1', label: '开发工程师', x: 200, y: 440 },
    { id: 'Dev2', label: '开发工程师', x: 200, y: 550 },
  ],
  edges: [
    { source: 'CEO', target: 'CTO' },
    { source: 'CEO', target: 'COO' },
    { source: 'CTO', target: 'TechLead' },
    { source: 'COO', target: 'ProductLead' },
    { source: 'TechLead', target: 'Dev1' },
    { source: 'TechLead', target: 'Dev2' },
  ],
};

创建树形布局

javascript 复制代码
const graph = new G6.Graph({
  container: 'container',
  width: 800,
  height: 600,
  defaultNode: {
    type: 'rect', // 矩形节点
    size: [120, 50],
    style: {
      fill: '#5B8FF9',
      stroke: '#5B8FF9',
      lineWidth: 2,
      radius: 8,
    },
    labelCfg: {
      style: {
        fill: '#fff',
        fontSize: 14,
      },
    },
  },
  defaultEdge: {
    type: 'polyline', // 折线
    style: {
      stroke: '#91cc75',
      lineWidth: 2,
    },
  },
  layout: {
    type: 'dagre', // 树形布局
    rankdir: 'TB', // 从上到下
    nodesep: 20,
    ranksep: 50,
  },
});

graph.data(orgData);
graph.render();

案例三:流程图编辑器

让我们创建一个简单的流程图编辑器,支持拖拽创建节点!

创建编辑器

javascript 复制代码
const graph = new G6.Graph({
  container: 'container',
  width: 800,
  height: 600,
  modes: {
    default: ['drag-canvas', 'zoom-canvas', 'drag-node', 'click-select'],
  },
  defaultNode: {
    size: 80,
    style: {
      fill: '#5B8FF9',
      stroke: '#5B8FF9',
      lineWidth: 2,
    },
    labelCfg: {
      style: {
        fill: '#fff',
        fontSize: 14,
      },
    },
  },
  defaultEdge: {
    style: {
      stroke: '#e2e2e2',
      lineWidth: 2,
      endArrow: {
        path: G6.Arrow.triangle(10, 10, 0),
        fill: '#e2e2e2',
      },
    },
  },
});

// 初始节点
const data = {
  nodes: [
    { id: 'start', label: '开始', x: 100, y: 300, type: 'circle', style: { fill: '#91cc75' } },
    { id: 'process1', label: '处理步骤1', x: 350, y: 300 },
    { id: 'decision', label: '判断', x: 600, y: 300, type: 'diamond', style: { fill: '#fac858' } },
    { id: 'end', label: '结束', x: 850, y: 300, type: 'circle', style: { fill: '#ee6666' } },
  ],
  edges: [
    { source: 'start', target: 'process1' },
    { source: 'process1', target: 'decision' },
    { source: 'decision', target: 'end' },
  ],
};

graph.data(data);
graph.render();

// 添加右键菜单
graph.on('contextmenu', (e) => {
  const { x, y } = e.canvasPoint;
  const node = {
    id: `node-${Date.now()}`,
    label: '新节点',
    x,
    y,
  };
  graph.addItem('node', node);
});

案例四:知识图谱

让我们创建一个知识图谱,展示《西游记》中的人物关系!

数据准备

javascript 复制代码
const journeyData = {
  nodes: [
    { id: '唐僧', label: '唐僧', category: '师徒', x: 400, y: 80 },
    { id: '孙悟空', label: '孙悟空', category: '师徒', x: 200, y: 220 },
    { id: '猪八戒', label: '猪八戒', category: '师徒', x: 400, y: 220 },
    { id: '沙僧', label: '沙僧', category: '师徒', x: 600, y: 220 },
    { id: '白龙马', label: '白龙马', category: '师徒', x: 400, y: 360 },
    { id: '如来佛祖', label: '如来佛祖', category: '神仙', x: 700, y: 100 },
    { id: '观音菩萨', label: '观音菩萨', category: '神仙', x: 100, y: 100 },
    { id: '牛魔王', label: '牛魔王', category: '妖怪', x: 150, y: 400 },
    { id: '白骨精', label: '白骨精', category: '妖怪', x: 650, y: 400 },
  ],
  edges: [
    { source: '唐僧', target: '孙悟空', label: '徒弟' },
    { source: '唐僧', target: '猪八戒', label: '徒弟' },
    { source: '唐僧', target: '沙僧', label: '徒弟' },
    { source: '唐僧', target: '白龙马', label: '坐骑' },
    { source: '孙悟空', target: '牛魔王', label: '结拜兄弟' },
    { source: '观音菩萨', target: '唐僧', label: '指导' },
    { source: '如来佛祖', target: '孙悟空', label: '降服' },
    { source: '孙悟空', target: '白骨精', label: '打败' },
  ],
};

创建知识图谱

javascript 复制代码
// 定义节点类型样式
const categoryStyles = {
  '师徒': { fill: '#5B8FF9', stroke: '#5B8FF9' },
  '神仙': { fill: '#91cc75', stroke: '#91cc75' },
  '妖怪': { fill: '#ee6666', stroke: '#ee6666' },
};

const graph = new G6.Graph({
  container: 'container',
  width: 800,
  height: 500,
  defaultNode: {
    size: 60,
    labelCfg: {
      style: {
        fill: '#fff',
        fontSize: 14,
      },
    },
  },
  defaultEdge: {
    style: {
      stroke: '#e2e2e2',
      lineWidth: 2,
    },
    labelCfg: {
      style: {
        fill: '#666',
        fontSize: 12,
      },
    },
  },
  // 自定义节点样式
  nodeStateStyles: {
    hover: {
      scale: 1.1,
    },
  },
});

// 加载数据并设置样式
graph.data({
  nodes: journeyData.nodes.map(node => ({
    ...node,
    style: {
      ...categoryStyles[node.category],
    },
  })),
  edges: journeyData.edges,
});

// 添加图例
const legend = new G6.Legend({
  container: document.getElementById('container'),
  data: [
    { name: '师徒', marker: { symbol: 'circle', fill: '#5B8FF9' } },
    { name: '神仙', marker: { symbol: 'circle', fill: '#91cc75' } },
    { name: '妖怪', marker: { symbol: 'circle', fill: '#ee6666' } },
  ],
});

graph.render();

// 添加交互效果
graph.on('node:mouseenter', (e) => {
  const item = e.item;
  graph.setItemState(item, 'hover', true);
});

graph.on('node:mouseleave', (e) => {
  const item = e.item;
  graph.setItemState(item, 'hover', false);
});

进阶技巧:自定义节点

G6 允许我们创建自定义形状的节点!

创建自定义节点

javascript 复制代码
// 注册自定义节点
G6.registerNode('custom-node', {
  draw(cfg, group) {
    const { label } = cfg;
    // 创建圆形背景
    const circle = group.addShape('circle', {
      attrs: {
        x: 0,
        y: 0,
        r: 30,
        fill: '#5B8FF9',
        stroke: '#5B8FF9',
        lineWidth: 2,
      },
      name: 'circle-shape',
    });
    // 创建文字
    const text = group.addShape('text', {
      attrs: {
        x: 0,
        y: 0,
        text: label,
        fill: '#fff',
        fontSize: 14,
        textAlign: 'center',
        textBaseline: 'middle',
      },
      name: 'text-shape',
    });
    // 添加装饰元素
    const star = group.addShape('polygon', {
      attrs: {
        points: [
          [0, -35], [5, -28], [12, -30], [8, -23], [10, -15], [0, -20], [-10, -15], [-8, -23], [-12, -30], [-5, -28],
        ],
        fill: '#FFD700',
      },
      name: 'star-shape',
    });
    return group;
  },
});

// 使用自定义节点
const graph = new G6.Graph({
  container: 'container',
  width: 800,
  height: 600,
  defaultNode: {
    type: 'custom-node',
  },
});

实战练习:创建一个任务流程图

让我们来一个实战练习!创建一个简单的任务流程图。

需求分析

我们需要创建一个包含以下节点的流程图:

  1. 开始节点(圆形,绿色)
  2. 任务节点(矩形,蓝色)
  3. 判断节点(菱形,黄色)
  4. 结束节点(圆形,红色)

代码实现

javascript 复制代码
const flowData = {
  nodes: [
    { id: 'start', label: '开始', type: 'circle', style: { fill: '#91cc75', r: 30 } },
    { id: 'task1', label: '收集需求', type: 'rect', style: { fill: '#5B8FF9', width: 100, height: 40 } },
    { id: 'decision1', label: '需求明确?', type: 'diamond', style: { fill: '#fac858', width: 80, height: 80 } },
    { id: 'task2', label: '设计方案', type: 'rect', style: { fill: '#5B8FF9', width: 100, height: 40 } },
    { id: 'task3', label: '开发实现', type: 'rect', style: { fill: '#5B8FF9', width: 100, height: 40 } },
    { id: 'decision2', label: '测试通过?', type: 'diamond', style: { fill: '#fac858', width: 80, height: 80 } },
    { id: 'end', label: '结束', type: 'circle', style: { fill: '#ee6666', r: 30 } },
  ],
  edges: [
    { source: 'start', target: 'task1' },
    { source: 'task1', target: 'decision1' },
    { source: 'decision1', target: 'task2', label: '是' },
    { source: 'decision1', target: 'task1', label: '否' },
    { source: 'task2', target: 'task3' },
    { source: 'task3', target: 'decision2' },
    { source: 'decision2', target: 'end', label: '是' },
    { source: 'decision2', target: 'task3', label: '否' },
  ],
};

const graph = new G6.Graph({
  container: 'container',
  width: 900,
  height: 500,
  modes: {
    default: ['drag-canvas', 'zoom-canvas', 'drag-node'],
  },
  layout: {
    type: 'dagre',
    rankdir: 'LR', // 从左到右
    nodesep: 20,
    ranksep: 60,
  },
  defaultEdge: {
    style: {
      stroke: '#e2e2e2',
      lineWidth: 2,
      endArrow: {
        path: G6.Arrow.triangle(8, 10, 0),
        fill: '#e2e2e2',
      },
    },
    labelCfg: {
      style: {
        fill: '#666',
        fontSize: 12,
      },
    },
  },
});

graph.data(flowData);
graph.render();
相关推荐
尽欢i1 小时前
Vue3 customRef 封神教程:防抖、本地存储、自动埋点一套搞定,模板干干净净
前端·javascript·vue.js
VOLUN1 小时前
TypeScript封装通用RESTful BaseAPI,后台接口代码精简80%
前端·javascript
胡永双1 小时前
Hexo + GitHub Pages搭建个人Blog教程(三)
前端
hunterandroid1 小时前
[Android 从零到一] 权限管理:运行时权限与最佳实践
前端
kyrie281 小时前
Redux 完整基础操作(原生 Redux,不结合 React-Redux)
前端
因_崔斯汀1 小时前
Vue 模板编译:HTML 是怎么变成 JS 的?
前端·vue.js
UXbot1 小时前
帮助企业低门槛开展AI应用开发的平台推荐
前端·低代码·ui·交互·产品经理·原型模式·web app
橘子星2 小时前
基于 Vite 的多模态生图前端工程实践
前端·javascript·人工智能