常用配置

本文使用到的配置以及后续 Tutorial 将会使用到到常用配置如下:

配置项类型选项 / 示例默认说明
fitViewBooleantrue / falsefalse是否将图适配到画布大小,可以防止超出画布或留白太多。
fitViewPaddingNumber / Array20 / [ 20, 40, 50, 20 ]0画布上的四周留白宽度。
animateBooleantrue / falsefalse是否启用图的动画。
modesObject{ default: [ 'drag-node', 'drag-canvas' ] }null图上行为模式的集合。由于比较复杂,按需参见:G6 中的 Modeopen in new window 教程。
defaultNodeObject{ type: 'circle', color: '#000', style: { ...... } }null节点默认的属性,包括节点的一般属性和样式属性(style)。
defaultEdgeObject{ type: 'polyline', color: '#000', style: { ...... } }null边默认的属性,包括边的一般属性和样式属性(style)。
nodeStateStylesObject{ hover: { ...... }, select: { ...... } }null节点在除默认状态外,其他状态下的样式属性(style)。例如鼠标放置(hover)、选中(select)等状态。
edgeStateStylesObject{ hover: { ...... }, select: { ...... } }null边在除默认状态外,其他状态下的样式属性(style)。例如鼠标放置(hover)、选中(select)等状态。
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Tutorial Demo</title>
  </head>
  <body>
    <div id="mountNode"></div>
    <script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.7.1/dist/g6.min.js"></script>
    <!-- 4.x and later versions -->
    <!-- <script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.3.11/dist/g6.min.js"></script> -->
    <script>
      const graph = new G6.Graph({
        container: 'mountNode',
        width: 1000,
        height: 600,
        fitView: true,
        fitViewPadding: [20, 40, 50, 20],
      });

      const main = async () => {
        const response = await fetch(
          'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json',
        );
        const remoteData = await response.json();
        graph.data(remoteData);
        graph.render();
      };
      main();
    </script>
  </body>
</html>

元素及其配置

图的元素特指图上的节点 Node Edge 。在上节中,我们已经将 Tutorial 案例的图绘制了出来,但是各个元素及其 label 在视觉上很简陋。本文通过将上一章节中简陋的元素美化成如下效果,介绍元素的属性、配置方法。

图的元素

图的元素特指图上的节点 Node Edge 。G6 内置了一系列 内置的节点open in new window内置的边open in new window,供用户自由选择。G6 不同的内置节点或不同的内置边主要区别在于元素的 图形 Shapeopen in new window,例如,节点可以是圆形、矩形、图片等。

元素的属性

不论是节点还是边,它们的属性分为两种:

例如,G6 设定 hover 或 click 节点,造成节点状态的改变,只能自动改变节点的样式属性(如 fillstroke 等**)**,其他属性(如 type 等)不能被改变。如果需要改变其他属性,要通过 graph.updateItemopen in new window 手动配置。样式属性是一个名为 style 的对象, style 字段与其他属性并行。

数据结构

以节点元素为例,其属性的数据结构如下:

{

	id: 'node0',          // 元素的 id

  type: 'circle',       // 元素的图形

  size: 40,             // 元素的大小

  label: 'node0'        // 标签文字

  visible: true,        // 控制初次渲染显示与隐藏,若为 false,代表隐藏。默认不隐藏

  labelCfg: {           // 标签配置属性

    positions: 'center',// 标签的属性,标签在元素中的位置

    style: {            // 包裹标签样式属性的字段 style 与标签其他属性在数据结构上并行

      fontSize: 12      // 标签的样式属性,文字字体大小

      // ...            // 标签的其他样式属性

    }

  }

  // ...,               // 其他属性

  style: {              // 包裹样式属性的字段 style 与其他属性在数据结构上并行

    fill: '#000',       // 样式属性,元素的填充色

    stroke: '#888',     // 样式属性,元素的描边色

    // ...              // 其他样式属性

  }

}

边元素的属性数据结构与节点元素相似,只是其他属性中多了 sourcetarget 字段,代表起始和终止节点的 id。 细化在图 1 中 Tutorial 案例 的视觉需求,我们需要完成:

  • 视觉效果:
    • R1: 节点的描边和填充色,对应节点样式属性:fillstroke
    • R2: 节点上标签文本的颜色,对应节点其他属性:labelCfg
    • R3: 边的透明度和颜色,对应边样式属性:opacitystroke
    • R4: 边上标签文本的方向和颜色,对应边其他属性:labelCfg
  • 数据与视觉映射:
    • R5: 根据数据中节点的 class 属性映射节点的形状,对应节点其他属性:type
    • R6: 根据数据中边的 weight 属性映射边的粗细,对应边样式属性:lineWidth

数据格式

{
  "nodes": [
    {"id": "0", "label": "n0", "class": "c0","x": 1000, "y": -100 },
    {"id": "1", "label": "n1", "class": "c0","x": 300, "y": -10 },
    {"id": "2", "label": "n2", "class": "c0","x": 10, "y": 10 },
    {"id": "3", "label": "n3", "class": "c0","x": 320, "y": -100 },
    {"id": "4", "label": "n4", "class": "c0","x": 100, "y": 900 },
    {"id": "5", "label": "n5", "class": "c0","x": 120, "y": 213 },
    {"id": "6", "label": "n6", "class": "c1","x": 543, "y": 12 },
    {"id": "7", "label": "n7", "class": "c1","x": 543, "y": -100 },
    {"id": "8", "label": "n8", "class": "c1","x": 1, "y": 0 },
    {"id": "9", "label": "n9", "class": "c1","x": 0, "y": -222 },
    {"id": "10", "label": "n10", "class": "c1","x": 435, "y": 69 },
    {"id": "11", "label": "n11", "class": "c1","x": 23, "y": 10 },
    {"id": "12", "label": "n12", "class": "c1","x": -129, "y": 39 },
    {"id": "13", "label": "n13", "class": "c2","x": 234, "y": 843 },
    {"id": "14", "label": "n14", "class": "c2","x": -301, "y": 129 },
    {"id": "15", "label": "n15", "class": "c2","x": -20, "y": -76 },
    {"id": "16", "label": "n16", "class": "c2","x": 1220, "y": -34 },
    {"id": "17", "label": "n17", "class": "c2","x": -10, "y": 954 },
    {"id": "18", "label": "n18", "class": "c2","x": 492, "y": 123 },
    {"id": "19", "label": "n19", "class": "c2","x": 123, "y": -241 }
  ],
  "edges": [
    {"source": "0", "target": "1", "label": "e0-1", "weight": 1 },
    {"source": "0", "target": "2", "label": "e0-2", "weight": 2 },
    {"source": "0", "target": "3", "label": "e0-3", "weight": 3 },
    {"source": "0", "target": "4", "label": "e0-4", "weight": 1.4 },
    {"source": "0", "target": "5", "label": "e0-5", "weight": 2 },
    {"source": "0", "target": "7", "label": "e0-7", "weight": 2 },
    {"source": "0", "target": "8", "label": "e0-8", "weight": 2 },
    {"source": "0", "target": "9", "label": "e0-9", "weight": 1.3 },
    {"source": "0", "target": "10", "label": "e0-10", "weight": 1.5 },
    {"source": "0", "target": "11", "label": "e0-11", "weight": 1 },
    {"source": "0", "target": "13", "label": "e0-13", "weight": 10 },
    {"source": "0", "target": "14", "label": "e0-14", "weight": 2 },
    {"source": "0", "target": "15", "label": "e0-15", "weight": 0.5 },
    {"source": "0", "target": "16", "label": "e0-16", "weight": 0.8 },
    {"source": "2", "target": "3", "label": "e2-3", "weight": 1 },
    {"source": "4", "target": "5", "label": "e4-5", "weight": 1.4 },
    {"source": "4", "target": "6", "label": "e4-6", "weight": 2.1 },
    {"source": "5", "target": "6", "label": "e5-6", "weight": 1.9 },
    {"source": "7", "target": "13", "label": "e7-13", "weight": 0.5 },
    {"source": "8", "target": "14", "label": "e8-14", "weight": 0.8 },
    {"source": "9", "target": "10", "label": "e9-10", "weight": 0.2 },
    {"source": "10", "target": "14", "label": "e10-14", "weight": 1 },
    {"source": "10", "target": "12", "label": "e10-12", "weight": 1.2 },
    {"source": "11", "target": "14", "label": "e11-14", "weight": 1.2 },
    {"source": "12", "target": "13", "label": "e12-13", "weight": 2.1 },
    {"source": "16", "target": "17", "label": "e16-17", "weight": 2.5 },
    {"source": "16", "target": "18", "label": "e16-18", "weight": 3 },
    {"source": "17", "target": "18", "label": "e17-18", "weight": 2.6 },
    {"source": "18", "target": "19", "label": "e18-19", "weight": 1.6 }
  ]
}

配置属性

在 G6 中,根据不同的场景需求,有 7 种配置元素属性的方式。这里,我们简单介绍其中的两种:

  1. 实例化图时配置元素的全局属性;
  2. 在数据中配置。

1. 实例化图时全局配置

**适用场景:**所有节点统一的属性配置,所有边统一的属性配置。 **使用方式:**使用图的两个配置项:

  • defaultNode:节点在默认状态下的样式属性style)和其他属性
  • defaultEdge:边在默认状态下的样式属性style)和其他属性

注意: 由于是统一的配置,不能根据数据中的属性(如 classweight)等值的不同进行个性化设置,因此只能满足 R1、R2、R3、R4 需求。

通过如下方式在实例化图时 defaultNodedefaultEdge ,可以完成上图效果:

const graph = new G6.Graph({

  // ...                   // 图的其他配置

  // 节点在默认状态下的样式配置(style)和其他配置

  defaultNode: {

    size: 30, // 节点大小

    // ...                 // 节点的其他配置

    // 节点样式配置

    style: {

      fill: 'steelblue', // 节点填充色

      stroke: '#666', // 节点描边色

      lineWidth: 1, // 节点描边粗细

    },

    // 节点上的标签文本配置

    labelCfg: {

      // 节点上的标签文本样式配置

      style: {

        fill: '#fff', // 节点标签文字颜色

      },

    },

  },

  // 边在默认状态下的样式配置(style)和其他配置

  defaultEdge: {

    // ...                 // 边的其他配置

    // 边样式配置

    style: {

      opacity: 0.6, // 边透明度

      stroke: 'grey', // 边描边颜色

    },

    // 边上的标签文本配置

    labelCfg: {

      autoRotate: true, // 边上的标签文本根据边的方向旋转

    },

  },

});

2. 在数据中配置

**适用场景:**不同节点/边可以有不同的个性化配置。 因此,这种配置方式可以满足 R5、R6 需求。 **使用方式:**可以直接将配置写入数据文件;也可以在读入数据后,通过遍历的方式写入配置。这里展示读入数据后,通过遍历的方式写入配置。下面代码展示了如何遍历数据进行属性的配置:

const nodes = remoteData.nodes;
nodes.forEach((node) => {
  if (!node.style) {
    node.style = {};
  }
  switch (
    node.class // 根据节点数据中的 class 属性配置图形
  ) {
    case 'c0': {
      node.type = 'circle'; // class = 'c0' 时节点图形为 circle
      break;
    }
    case 'c1': {
      node.type = 'rect'; // class = 'c1' 时节点图形为 rect
      node.size = [35, 20]; // class = 'c1' 时节点大小
      break;
    }
    case 'c2': {
      node.type = 'ellipse'; // class = 'c2' 时节点图形为 ellipse
      node.size = [35, 20]; // class = 'c2' 时节点大小
      break;
    }
  }
});

graph.data(remoteData);
  • 可以看到,图中有一些节点被渲染成了矩形,还有一些被渲染成了椭圆形。除了设置 type 属性之外,我们还覆盖了上文全局配置的节点的 size 属性,在矩形和椭圆的情况下,size 是一个数组;而在默认圆形的情况下,G6 将仍然读取全局配置的 size 属性为数字 30
  • 也就是说,动态配置属性让我们既可以根据数据的不同配置不同的属性值,也可以有能力覆盖全局静态的属性值。

进一步地,我们尝试根据数据的比重不同,配置不一样边的粗细:

// 遍历边数据
const edges = remoteData.edges;
edges.forEach((edge) => {
  if (!edge.style) {
    edge.style = {};
  }
  edge.style.lineWidth = edge.weight; // 边的粗细映射边数据中的 weight 属性数值
});

graph.data(remoteData);

边的粗细已经按照数据的比重成功渲染了出来,但是边原有的样式(透明度、颜色)却丢失了。这是因为 动态配置属性会覆盖全局配置属性,这里配置了 style.lineWidth,导致覆盖了全局的 style 对象。解决办法是将被覆盖的边的样式都移到动态配置里面来:

  • 完整代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Tutorial Demo</title>
  </head>
  <body>
    <div id="mountNode"></div>
    <script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.7.1/dist/g6.min.js"></script>
    <!-- 4.x and later versions -->
    <!-- <script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.3.11/dist/g6.min.js"></script> -->
    <script>
      const graph = new G6.Graph({
        container: 'mountNode',
        width: 800,
        height: 600,
        fitView: true,
        fitViewPadding: [20, 40, 50, 20],
        defaultNode: {
          size: 30,
          labelCfg: {
            style: {
              fill: '#fff',
            },
          },
        },
        defaultEdge: {
          labelCfg: {
            autoRotate: true,
          },
        },
      });
      const main = async () => {
        const response = await fetch(
          'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json',
        );
        const remoteData = await response.json();
        const nodes = remoteData.nodes;
        const edges = remoteData.edges;
        nodes.forEach((node) => {
          if (!node.style) {
            node.style = {};
          }
          node.style.lineWidth = 1;
          node.style.stroke = '#666';
          node.style.fill = 'steelblue';
          switch (node.class) {
            case 'c0': {
              node.type = 'circle';
              break;
            }
            case 'c1': {
              node.type = 'rect';
              node.size = [35, 20];
              break;
            }
            case 'c2': {
              node.type = 'ellipse';
              node.size = [35, 20];
              break;
            }
          }
        });
        edges.forEach((edge) => {
          if (!edge.style) {
            edge.style = {};
          }
          edge.style.lineWidth = edge.weight;
          edge.style.opacity = 0.6;
          edge.style.stroke = 'grey';
        });

        graph.data(remoteData);
        graph.render();
      };
      main();
    </script>
  </body>
</html>

图布局 Layout

  • 当数据中没有节点位置信息,或者数据中的位置信息不满足需求时,需要借助一些布局算法对图进行布局。

般图:

  • Random Layout:随机布局;

  • Force Layout:经典力导向布局:

    力导向布局:一个布局网络中,粒子与粒子之间具有引力和斥力,从初始的随机无序的布局不断演变,逐渐趋于平衡稳定的布局方式称之为力导向布局。适用于描述事物间关系,比如人物关系、计算机网络关系等。

  • Circular Layout:环形布局;

  • Radial Layout:辐射状布局;

  • MDS Layout:高维数据降维算法布局;

  • Fruchterman Layout:Fruchterman 布局,一种力导布局;

  • Dagre Layout:层次布局;

  • Concentric Layout:同心圆布局,将重要(默认以度数为度量)的节点放置在布局中心;

  • Grid Layout:格子布局,将节点有序(默认是数据顺序)排列在格子上。

树图布局:

  • Dendrogram Layout:树状布局(叶子节点布局对齐到同一层);
  • CompactBox Layout:紧凑树布局;
  • Mindmap Layout:脑图布局;
  • Indented Layout:缩进布局。

各种布局方法的具体介绍及其配置参见 图布局 APIopen in new window树图布局 APIopen in new window

取消自动适配画布

我们在之前的教程里面,为了能够将超出画布的图适配到视野中,在实例化图时使用了 fitView 配置项。这节开始我们将会去掉这个特性。因为复杂的布局系统会打破适配的规则,反而会造成更多的困扰。让我们将相关的适配代码变为注释:

const graph = new G6.Graph({

  // ...

  // fitView: true,

  // fitViewPadding: [ 20, 40, 50, 20 ]

});

默认布局

当实例化图时没有配置布局时:

  • 若数据中节点有位置信息(xy),则按照数据的位置信息进行绘制;
  • 若数据中节点没有位置信息,则默认使用 Random Layout 进行布局。

配置布局

G6 使用布局的方式非常简单,在图实例化的时候,加上 layout 配置即可。下面代码在实例化图时设置了布局方法为 type: 'force',即经典力导向图布局。并设置了参数 preventOverlap: true ,表示希望节点不重叠。力导向布局的更多配置项参见:Layout APIopen in new window

const graph = new G6.Graph({

  // ...                      // 其他配置项

  layout: {

    // Object,可选,布局的方法及其配置项,默认为 random 布局。

    type: 'force', // 指定为力导向布局

    preventOverlap: true, // 防止节点重叠

    // nodeSize: 30        // 节点大小,用于算法中防止节点重叠时的碰撞检测。由于已经在上一节的元素配置中设置了每个节点的 size 属性,则不需要在此设置 nodeSize。

  },

});

图的交互 Behavior

G6 封装了一系列交互方法,方便用户直接使用。本文将为 Tutorial 案例 增加简单的交互:hover 节点、点击节点、点击边、放缩画布、拖拽画布。

交互行为 Behavior

G6 中的交互行为。G6 内置了一系列交互行为,用户可以直接使用。简单地理解,就是可以一键开启这些交互行为:

  • drag-canvas:拖拽画布;
  • zoom-canvas:缩放画布。

更多详见:交互行为 Behavioropen in new window

交互管理 Mode

Mode 是 G6 交互行为的管理机制,一个 mode 是多种行为 Behavior 的组合,允许用户通过切换不同的模式进行交互行为的管理。由于该概念较为复杂,在本入门教程中,读者不需要对该机制深入理解。如有需求,参见 交互模式 Modeopen in new window

交互状态 State

状态 Stateopen in new window 是 G6 中的状态机制。用户可以为图中的元素(节点/边)设置不同的状态及不同状态下的样式。在状态发生变化时,G6 自动更新元素的样式。例如,可以为节点设置状态 'click'truefalse,并为节点设置 'click' 的样式为加粗节点边框。当 'click' 状态被切换为 true 时,节点的边框将会被加粗,'click' 状态被切换为 false 时,节点的样式恢复到默认。在下面的使用方法中,将会有具体例子。

使用方法

拖拽、缩放——内置的交互行为

  • 在 G6 中使用内置 Behavior 的方式非常简单,只需要在图实例化时配置 modes。拖拽和缩放属于 G6 内置交互行为,修改代码如下:
const graph = new G6.Graph({
  // ...                                          // 其他配置项
  modes: {
    default: ['drag-canvas', 'zoom-canvas', 'drag-node'], // 允许拖拽画布、放缩画布、拖拽节点
  },
});

除了直接使用内置交互名称外,也可以为 Behavior 配置参数,例如放缩画布的敏感度、最大/最小放缩程度等,具体用法参见 交互行为 Behavioropen in new window

上面代码中的 modes 定义了 G6 的模式,default 是默认的模式,还可以允许有其他的模式,比如:编辑模式 edit 等。不同的模式,用户能进行的行为可以不同,比如默认模式能拖拽画布,编辑模式不允许拖拽画布:

Hover、Click 改变样式——状态式交互

有时我们希望通过交互可以将元素样式变成特定样式, 鼠标 hover 节点、点击节点、点击边时,样式发生了变化。这里涉及到了 G6 中 状态 Stateopen in new window 的概念。简单地说,是否 hoverclick 、任何操作(可以是自己起的状态名),都可以称为一种状态(state)。用户可以自由设置不同状态下的元素样式。要达到交互更改元素样式,需要两步:

  • Step 1: 设置各状态下的元素样式;
  • Step 2: 监听事件并切换元素状态。

设置各状态下的元素样式

在实例化图时,通过 nodeStateStylesedgeStateStyles 两个配置项可以配置元素在不同状态下的样式。 为达到 Tutorial 案例 中的效果:

  • 鼠标 hover 节点时,该节点颜色变浅;
  • 点击节点时,该节点边框加粗变黑;
  • 点击边时,该边变成蓝色。

下面代码设置了节点分别在 hoverclick 状态为 true 时的样式,边在 click 状态为 true 时的样式:

const graph = new G6.Graph({

  // ...                           // 其他配置项

  // 节点不同状态下的样式集合

  nodeStateStyles: {

    // 鼠标 hover 上节点,即 hover 状态为 true 时的样式

    hover: {

      fill: 'lightsteelblue',

    },

    // 鼠标点击节点,即 click 状态为 true 时的样式

    click: {

      stroke: '#000',

      lineWidth: 3,

    },

  },

  // 边不同状态下的样式集合

  edgeStateStyles: {

    // 鼠标点击边,即 click 状态为 true 时的样式

    click: {

      stroke: 'steelblue',

    },

  },

});

监听事件并切换元素状态

G6 中所有元素监听都挂载在图实例上,如下代码中的 graph 对象是 G6.Graph 的实例,graph.on() 函数监听了某元素类型(node / edge)的某种事件(click / mouseenter / mouseleave / ... 所有事件参见:Event APIopen in new window)。

// 在图实例 graph 上监听

graph.on('元素类型:事件名', (e) => {

  // do something

});

现在,我们通过下面代码,为 Tutorial 案例 增加点和边上的监听事件,并在监听函数里使用 graph.setItemState() 改变元素的状态:

// 鼠标进入节点
graph.on('node:mouseenter', (e) => {
  const nodeItem = e.item; // 获取鼠标进入的节点元素对象
  graph.setItemState(nodeItem, 'hover', true); // 设置当前节点的 hover 状态为 true
});

// 鼠标离开节点
graph.on('node:mouseleave', (e) => {
  const nodeItem = e.item; // 获取鼠标离开的节点元素对象
  graph.setItemState(nodeItem, 'hover', false); // 设置当前节点的 hover 状态为 false
});

// 点击节点
graph.on('node:click', (e) => {
  // 先将所有当前是 click 状态的节点置为非 click 状态
  const clickNodes = graph.findAllByState('node', 'click');
  clickNodes.forEach((cn) => {
    graph.setItemState(cn, 'click', false);
  });
  const nodeItem = e.item; // 获取被点击的节点元素对象
  graph.setItemState(nodeItem, 'click', true); // 设置当前节点的 click 状态为 true
});

// 点击边
graph.on('edge:click', (e) => {
  // 先将所有当前是 click 状态的边置为非 click 状态
  const clickEdges = graph.findAllByState('edge', 'click');
  clickEdges.forEach((ce) => {
    graph.setItemState(ce, 'click', false);
  });
  const edgeItem = e.item; // 获取被点击的边元素对象
  graph.setItemState(edgeItem, 'click', true); // 设置当前边的 click 状态为 true
});

插件与工具

插件

使用插件时,有三个步骤: Step 1: 引入插件; Step 2: 实例化插件; Step 3: 在实例化图时将插件的实例配置到图上。

Minimap 是 G6 的插件之一,引入 G6 后可以直接使用。实例化 Minimap 对象,并将其配置到图实例的插件列表里即可:

// 实例化 minimap 插件
const minimap = new G6.Minimap({
  size: [100, 100],
  className: 'minimap',
  type: 'delegate',
});

// 实例化图
const graph = new G6.Graph({
  // ...                           // 其他配置项
  plugins: [minimap], // 将 minimap 实例配置到图上
});

Image Minimap

  • 由于 Minimapopen in new window 的原理是将主画布内容复制到 minimap 的画布上,在大数据量下可能会造成双倍的绘制效率成本。
  • 为缓解该问题,Image Minimap 采用另一种机制,根据提供的图片地址或 base64 字符串 graphImg 绘制 <img /> 代替 minimap 上的 canvas。
  • 该方法可以大大减轻两倍 canvas 绘制的压力。
  • graphImg 完全交由 G6 的用户控制,需要注意主画布更新时需要使用 updateGraphImg 方法替换 graphImg

使用方法

实例化 Image Minimap 插件时,graphImg 是必要参数。建议在主画布更新时使用 updateGraphImg(img) 同步更新 minimap 图片,其中参数 img 是图片地址或 base64 文本。

// 实例化 Image Minimap 插件
const imageMinimap = new G6.ImageMinimap({
  width: 200,
  graphImg: 'https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*eD7nT6tmYgAAAAAAAAAAAABkARQnAQ'
});
const graph = new G6.Graph({
  //... 其他配置项
  plugins: [imageMinimap], // 配置 imageMinimap 插件
});

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

... // 一些主画布更新操作
imageMinimap.updateGraphImg(img); // 使用新的图片(用户自己生成)替换 minimap 图片

Grid 网格

网格可用于辅助用户在拖拽节点时对齐到网格。

// const minimap = ...

// 实例化 grid 插件
const grid = new G6.Grid();

// 实例化图
const graph = new G6.Graph({
  // ...                        // 其他配置项
  plugins: [minimap, grid], // 将 grid 实例配置到图上
});

交互工具

交互工具是指配置到图上交互模式中的工具。使用交互工具时,有两个步骤: Step 1: 在实例化图时配置 modes; Step 2: 为交互工具定义样式。

tooltip 节点提示框

节点提示框可以用在节点的详细信息的展示。当鼠标滑过节点时,显示一个浮层告知节点的详细信息。更多配置参见 内置交互 tooltipopen in new window

  • 实例化图时配置 'tooltip'modes 中:
const graph = new G6.Graph({
  modes: {
    default: [
      // ...
      {
        type: 'tooltip', // 提示框
        formatText(model) {
          // 提示框文本内容
          const text = 'label: ' + model.label + '<br/> class: ' + model.class;
          return text;
        },
      },
    ],
  },
});
  • 由于 tooltip 实际上是一个悬浮的 <div> 标签,因此可在 HTML 的 <style> 标签或 CSS 中设置样式。下面展示在 HTML 中设置样式:
<head>
  <meta charset="UTF-8" />
  <title>Tutorial Demo</title>

  <style>
    /* 提示框的样式 */
    .g6-tooltip {
      border: 1px solid #e2e2e2;
      border-radius: 4px;
      font-size: 12px;
      color: #545454;
      background-color: rgba(255, 255, 255, 0.9);
      padding: 10px 8px;
      box-shadow: rgb(174, 174, 174) 0px 0px 10px;
    }
  </style>
</head>

edge-tooltip 边提示框

边提示框可以用在边的详细信息的展示。当鼠标滑过边时,显示一个浮层告知边的详细信息。更多配置参见 内置交互 edge-tooltipopen in new window

const graph = new G6.Graph({
  modes: {
    default: [
      // ...
      {
        type: 'tooltip', // 节点提示框
        // ...
      },
      {
        type: 'edge-tooltip', // 边提示框
        formatText(model) {
          // 边提示框文本内容
          const text =
            'source: ' +
            model.source +
            '<br/> target: ' +
            model.target +
            '<br/> weight: ' +
            model.weight;
          return text;
        },
      },
    ],
  },
});

与 tooltip 相同,edge-tooltip 是一个悬浮的 <div> 标签,可以使用与 tooltip 相同的方法设置其悬浮框的样式。

G6 的动画分为两个层次:

  • 图全局动画:图整体变化时的动画过渡;
  • 元素动画:节点和边的动画效果。

全局动画

G6 的全局动画指通过图实例进行操作时,产生的动画效果。例如:

  • graph.updateLayout(cfg)

通过实例化图时配置 animate: true,可以达到每次进行上述操作时,动画效果变化的目的。

例子

const graph = new G6.Graph({

  // ...                      // 其他配置项

  animate: true, // Boolean,可选,全局变化时否使用动画过渡

});

G6 核心概念总览

G6 Main concepts overview

图 Graph

  • 初始化和渲染

图形(Shape)(选读)

  • 图形和属性
  • 关键图形(Key Shape)
  • 图形分组(Shape group)
  • 图形变换 (Transform)

图元素(节点、边、Combo)

  • 节点
    • 内置节点
    • 自定义节点
    • 节点的连接点 anchorPoints
    • 内置边
    • 自定义边
  • Combo
    • 内置 Combo
    • 自定义 Combo
    • Combo 机制
    • 创建与拆分 Combo
  • 高级样式
    • 设置元素背景
    • 设置元素渐变色
    • 设置纹理
    • 更新文本样式
  • 高级操作
    • 更新节点或边的样式
    • 层级调整
    • 显示隐藏
    • 锁定/解锁

图布局

  • 内置布局
  • 自定义布局

交互与事件

  • 监听与绑定事件
  • 内置交互行为
  • 自定义交互行为(选读)
  • 交互模式 Mode

动画

  • 全局动画
  • 元素动画

图算法

插件

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <title>Tutorial Demo</title>
</head>
<style>
    /* 提示框的样式 */
    .g6-tooltip {
      border: 1px solid #e2e2e2;
      border-radius: 4px;
      font-size: 12px;
      color: #545454;
      background-color: rgba(255, 255, 255, 0.9);
      padding: 10px 8px;
      box-shadow: rgb(174, 174, 174) 0px 0px 10px;
    }
  </style>
<body>
    <!-- 引入 G6 -->
    <script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.7.1/dist/g6.min.js"></script>
    <!-- 4.x and later versions -->
    <!-- <script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.3.11/dist/g6.min.js"></script> -->


    <div id="mountNode"></div>


    <script>
        console.log(G6.Global.version);
        const initData = {
            // 点集
            nodes: [
                {
                    id: 'node1', // 节点的唯一标识
                    x: 100, // 节点横坐标
                    y: 200, // 节点纵坐标
                    label: '起始点', // 节点文本
                },
                {
                    id: 'node2',
                    x: 300,
                    y: 200,
                    label: '目标点',
                },
            ],
            // 边集
            edges: [
                // 表示一条从 node1 节点连接到 node2 节点的边
                {
                    source: 'node1', // 起始点 id
                    target: 'node2', // 目标点 id
                    label: '我是连线', // 边的文本
                },
            ],
        };

        
// 实例化 grid 插件
const grid = new G6.Grid();

        const graph = new G6.Graph({
            container: 'mountNode', // 指定挂载容器
            width: 800, // 图的宽度
            height: 500, // 图的高度

            plugins: [ grid], // 将 grid 实例配置到图上
            // fitView: true,
            // fitViewPadding: [20, 40, 50, 20],
            modes: {
                default: ['drag-canvas', 'zoom-canvas', 'drag-node'
            ,  {
        type: 'tooltip', // 提示框
        formatText(model) {
          // 提示框文本内容
          const text = 'label: ' + model.label + '<br/> class: ' + model.class;
          return text;
        },
      },
            
            ], // 允许拖拽画布、放缩画布、拖拽节点
            },

            layout: {
                // Object,可选,布局的方法及其配置项,默认为 random 布局。
                type: 'force', // 指定为力导向布局
                preventOverlap: true, // 防止节点重叠
                // nodeSize: 30        // 节点大小,用于算法中防止节点重叠时的碰撞检测。由于已经在上一节的元素配置中设置了每个节点的 size 属性,则不需要在此设置 nodeSize。
                linkDistance: 300, // 指定边距离为100
            },
            nodeStateStyles: {

                // 鼠标 hover 上节点,即 hover 状态为 true 时的样式

                hover: {

                    fill: 'lightsteelblue',

                },

                // 鼠标点击节点,即 click 状态为 true 时的样式

                click: {

                    stroke: '#000',

                    lineWidth: 3,

                },

            },

            // 边不同状态下的样式集合

            edgeStateStyles: {

                // 鼠标点击边,即 click 状态为 true 时的样式

                click: {

                    stroke: 'steelblue',

                },

            },
            // 节点在默认状态下的样式配置(style)和其他配置
            defaultNode: {
                size: 30, // 节点大小
                // ...                 // 节点的其他配置
                // 节点样式配置
                style: {
                    fill: 'steelblue', // 节点填充色
                    stroke: '#666', // 节点描边色
                    lineWidth: 1, // 节点描边粗细
                },
                // 节点上的标签文本配置
                labelCfg: {
                    // 节点上的标签文本样式配置
                    style: {
                        fill: '#fff', // 节点标签文字颜色
                    },
                },
            },



        });


        //   graph.data(initData); // 加载数据
        //   graph.render(); // 渲染
        //  fetch 函数允许我们发起网络请求,加载数据,而其异步的过程可以通过 async/await 进行更合理的控制。这里我们为了方便起见,将主要逻辑放在了 main 函数里面。 

        //  const graph = ...
        const main = async () => {
            const response = await fetch(
                'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json',
            );
            const remoteData = await response.json();
            // 在读入数据后,通过遍历的方式写入配置。

            remoteData.nodes.forEach((node) => {
                if (!node.style) {
                    node.style = {};
                }
                switch (
                node.class // 根据节点数据中的 class 属性配置图形
                ) {
                    case 'c0': {
                        node.type = 'circle'; // class = 'c0' 时节点图形为 circle
                        break;
                    }
                    case 'c1': {
                        node.type = 'rect'; // class = 'c1' 时节点图形为 rect
                        node.size = [35, 20]; // class = 'c1' 时节点大小
                        break;
                    }
                    case 'c2': {
                        node.type = 'ellipse'; // class = 'c2' 时节点图形为 ellipse
                        node.size = [35, 20]; // class = 'c2' 时节点大小
                        break;
                    }
                }
            });

            // ... 很多节点的  x 和 y 不在图的宽高(width: 800, height: 600)范围内:





            const edges = remoteData.edges;
            edges.forEach((edge) => {
                if (!edge.style) {
                    edge.style = {};
                }
                edge.style.lineWidth = edge.weight; // 边的粗细映射边数据中的 weight 属性数值
            });

            graph.data(remoteData); // 加载远程数据
            graph.render(); // 渲染
        };
        main();

        // 鼠标进入节点
        graph.on('node:mouseenter', (e) => {
            console.log(e);
            const nodeItem = e.item; // 获取鼠标进入的节点元素对象
            graph.setItemState(nodeItem, 'hover', true); // 设置当前节点的 hover 状态为 true
        });

        // 鼠标离开节点
        graph.on('node:mouseleave', (e) => {
            const nodeItem = e.item; // 获取鼠标离开的节点元素对象
            graph.setItemState(nodeItem, 'hover', false); // 设置当前节点的 hover 状态为 false
        });

        /*
                                          G6 提供了图的两个相关配置项:
                                        
                                        fitView:设置是否将图适配到画布中;
                                        fitViewPadding:画布上四周的留白宽度。
                                        我们将实例化图的代码更改为如下形式:
                                         
                                        const graph = new G6.Graph({
                                          // ...
                                          fitView: true,
                                          fitViewPadding: [20, 40, 50, 20],
                                        });
                                        */
    </script>
</body>

</html>