概述

G6.registNodeG6.registEdge 是开发者基于 G6 进行二次开发最主要的接口,没有之一!

注册

// 注册节点
G6.registNode(name, {
  // 绘制
  draw: function(cfg, group){
    return keyShape;
  },
  // 绘制后执行
  afterDraw: function(cfg, group, keyShape){

  },
  // 获取锚点
  getAnchorPoints: function(cfg){
    return anchorPoints;
  }
});

// 注册边
G6.registNode(name, {
  // 绘制
  draw: function(cfg, group){
    return keyShape;
  },
  // 绘制后执行
  afterDraw: function(cfg, group, keyShape){

  }
});

接口如上,接下来会逐章跟大家说明。

!注意:G6 内置一些基础的节点如:矩形 rect、圆形 circle、文本 text、菱形 rhombus,一些基础的边如:直线 line、箭头 arrow、曲线 smooth、曲线箭头 smoothArrow 。建议大家在注册型(设置 name)时保留这几个字段,否则会覆盖 G6 内置的图形!内置形详见

绘制

与 G2 自定 Shape 类似,draw 是子项最终绘制的接口,决定了一个子项最终画成什么样。

容器

group 是绘图容器,其本身是一个完整的绘图引擎,通过操作我们图形库的 API,我们能在里面画出千千万万的图形!绘图 API 参见绘图库 Canvas API

实例:

image

var data = {
  "nodes": [
    {
      "shape": "customNode",
      "id": "d62d1569"
    }
  ],
  "edges": []
}

G6.registNode('customNode', {
  draw: function(cfg, group){
    group.addShape('text', {
      attrs: {
        x: 100,
        y: 100,
        fill: '#333',
        text: '我是一个自定义节点,\n有下面那个方形和我自己组成'
      }
    });
    return group.addShape('rect', {
      attrs: {
        x: 100,
        y: 100,
        width: 100,
        height: 100,
        stroke: 'red'
      }
    });
  }
});

var net = new G6.Net({
  id: 'c1',           // 容器ID
  width: 500,   // 画布宽
  height: 500, // 画布高
  grid: {
    forceAlign: true, // 是否支持网格对齐
    cell: 10          // 网格大小
  }
});
net.source(data.nodes, data.edges);
net.render();

配置项

group 使我们有画图的能力,cfg 则是绘制一个子项的配置信息。其三个视觉通道位置大小颜色和一个原始数据字段origin。一个子项的形态就由这个四个信息决定!

image

实例:

var data = {
  "nodes": [
    {
      "shape": "customNode",
      "x": 100,
      "y": 250,
      "id": "d62d1569"
    },
    {
      "shape": "customNode",
      "x": 380,
      "y": 250,
      "id": "d62s1569"
    }
  ],
  "edges": [
    {
      "shape": "customEdge",
      "source": "d62d1569",
      "id": "75ae90a8",
      "target": "d62s1569"
    }
  ]
}

G6.registNode('customNode', {
  draw: function(cfg, group){
    group.addShape('text', {
      attrs: {
        x: cfg.x-50,
        y: cfg.y-50,
        fill: '#333',
        text: '我是一个自定义节点(node)'
      }
    });
    return group.addShape('rect', {
      attrs: {
        x: cfg.x-50,
        y: cfg.y-50,
        width: cfg.size,
        height: cfg.size,
        stroke: cfg.color
      }
    });
  }
});

G6.registEdge('customEdge', {
  draw: function(cfg, group){
    group.addShape('text', {
      attrs: {
        x: (cfg.points[0].x + cfg.points[1].x)/2,
        y: (cfg.points[0].y + cfg.points[1].y)/2,

        fill: '#333',
        text: '我是一个自定义边(edge)',
        textAlign: 'center'
      }
    });
    return group.addShape('polyline', {
      attrs: {
        points: [
          [cfg.points[0].x, cfg.points[0].y],
          [cfg.points[1].x, cfg.points[1].y]
        ],
        stroke: cfg.color,
        lineWidth: cfg.size
      }
    });
  }
});

var net = new G6.Net({
  id: 'c1',       // 容器ID
  width: 500,     // 画布宽
  height: 500     // 画布高
});
net.source(data.nodes, data.edges);
net.node()
  .color('red')
  .size(100);
net.edge()
  .color('blue')
  .size(3);
net.render();

!注意:值得注意的是,绘制节点(Node)的位置信息是:cfg.xcfg.y。而边(Edge)的是 points

关键形

keyShape 是 G6 特有的概念。简单来说,keyShape 是该子项参与图形计算的关键图形。所有的击中锚点控制点都是根据关键图形生成的,所以这个形(shape)真的非常非常关键!!

比如:

image

返回 rect,则控制点参考 rect 生成!

          var data = {
              "nodes": [
                  {
                      "shape": "customNode",
                      "id": "d62d1569"
                  }
              ],
              "edges": []
          }

          G6.registNode('customNode', {
              draw: function(cfg, group){
                var text = group.addShape('text', {
                  attrs: {
                    x: 100,
                    y: 100,
                    fill: '#333',
                    text: '我是一个自定义节点,\n有下面那个方形和我自己组成'
                  }
                });
                var rect = group.addShape('rect', {
                  attrs: {
                    x: 100,
                    y: 100,
                    width: 100,
                    height: 100,
                    stroke: 'red'
                  }
                });

                return rect;
              }
          });

          net = new G6.Net({
              id: 'c1',           // 容器ID
              width: 500,   // 画布宽
              height: 500, // 画布高
              grid: {
                  forceAlign: true, // 是否支持网格对齐
                  cell: 10          // 网格大小
              }
          });
          net.source(data.nodes, data.edges);
          net.render();

image

返回 text,则控制点参考 text 生成!

          var data = {
              "nodes": [
                  {
                      "shape": "customNode",
                      "id": "d62d1569"
                  }
              ],
              "edges": []
          }

          G6.registNode('customNode', {
              draw: function(cfg, group){
                var text = group.addShape('text', {
                  attrs: {
                    x: 100,
                    y: 100,
                    fill: '#333',
                    text: '我是一个自定义节点,\n有下面那个方形和我自己组成'
                  }
                });
                var rect = group.addShape('rect', {
                  attrs: {
                    x: 100,
                    y: 100,
                    width: 100,
                    height: 100,
                    stroke: 'red'
                  }
                });

                return text;
              }
          });

          var net = new G6.Net({
              id: 'c1',           // 容器ID
              width: 500,   // 画布宽
              height: 500, // 画布高
              grid: {
                  forceAlign: true, // 是否支持网格对齐
                  cell: 10          // 网格大小
              }
          });
          net.source(data.nodes, data.edges);
          net.render();

锚点

锚点的配置

位置定义

锚点是自定义节点时一个重要的概念,它表示一个节点可以被线连接的地方。在 G6 中,我们可以通过 getAnchorPoints 方法自定义锚点,示意图如下:

G6.registNode('customNode', {
  getAnchorPoints: function(cfg, group) {
    return [
      [0.5, 1], // 上边的中点
      [1, 0.5], // 右边的中点
      [0.5, 0], // 下边的中点
      [0, 0.5]  // 左边的中点
    ];
  }
});

属性配置

除了定义位置,在做编辑的交互的时候,我们可能还要配置锚点的颜色、鼠标悬浮时候的颜色、该锚点是否可以连接,这个时候,我们还能通过设置第三个参数进行锚点配置项的设置,示例

G6.registNode('customNode', {
  getAnchorPoints: function(cfg, group) {
    return [
      [0.5, 1, {
        // 锚点图形属性
        style: {
          fill: 'red',
          fillOpacity: 0.7
        },
        // 悬浮锚点图形属性
        hoverStyle: {
          stroke: null
        },
        // 是否可以连接
        linkable: false
      }], // 上边的中点
      [1, 0.5], // 右边的中点
      [0.5, 0], // 下边的中点
      [0, 0.5]  // 左边的中点
    ];
  }
});

锚点类型

这里的 getAnchorPoints 方法除了返回 二维数组 ,还能返回 autofalse,分别表示自动锚点和不使用锚点。示例

G6.registNode('custom1', {
  // 常规锚点
  getAnchorPoints: function(){
    return [
      [0, 0.5],   // 左边中点 索引为 0
      [1, 0.5]    // 右边中点 索引为 1
    ];
  }
});
G6.registNode('custom2', {
  // 自动锚点
  getAnchorPoints: function(){
    return 'auto';
  }
});
G6.registNode('custom3', {
  // 不使用锚点( 自动连接中心 )
  getAnchorPoints: function(){
    return null;
  }
});

锚点的交互

在更复杂的一些场景里,有的时候我们可能还需要动态控制锚点的数量,甚至对锚点的连接做一些控制。这个时候,我们不妨把锚点的配置信息和节点的数据模型进行绑定,从而实现节点的动态锚点的需求。示例

G6.registNode('custom', {
  getAnchorPoints: function(cfg){
    var model = cfg.model;
    // 将锚点与数据源关联
    return model.anchorPoints;
  }
});

综合示例

image

示例源码

绘制后

通过上面的接口我们已经能定义出任何节点,但是很多时候,我们希望基于当前的基础上添加一些信息,而不是用draw方法全部重新画。这个时候我们能通过调用 afterDraw 方法在原有形基础上添加新的图形。用法如下:

G6.registNode('customRect', {
  afterDraw: function(cfg, group) {
    group.addShape('text', {
      attrs: {
        x: cfg.x-cfg.size[0]/2,
        y: cfg.y-cfg.size[1]/2,
        fill: 'red',
        text: '我是绘制(draw)节点\n后添加的文本'
      }
    });
  }
});
G6.registEdge('line', {
  afterDraw: function(cfg, group, keyShape) {
    var center = keyShape.getPoint(0.5);
    group.addShape('text', {
      attrs: {
        x: center.x,
        y: center.y,
        fill: 'blue',
        textAlign: 'center',
        text: '我是绘制(draw)边\n后添加的文本'
      }
    });
  }
});
var data = {
  source: {
    "nodes": [
      {
        "x": 100,
        "y": 210,
        "id": "node1"
      },
      {
        "x": 300,
        "y": 210,
        "id": "node2"
      }
    ],
    "edges": [
      {
        "source": "node1",
        "id": "edge1",
        "target": "node2"
      }
    ]
  }
};
var net = new G6.Net({
    id: 'c1',      // 容器ID
    width: 500,    // 画布宽
    height: 500,   // 画布高
    mode: 'none',  // 模式
    grid: {
      forceAlign: true, // 是否支持网格对齐
      cell: 10          // 网格大小
    }
  });
net.node().shape('customRect');
net.read(data);
net.render();

详解自定义节点

配置项详解

  • 位置:cfg.x, cfg.y
  • 颜色:cfg.color
  • 尺寸:cfg.size
  • 原始数据:cfg.model

使用 HTML 节点

用原生 canvas 或是用我们内部封装的 2D 绘图引擎 G6.Canvas,画一些图形都挺方便。但直接操作它们去画一些列表复杂图标,或是处理文本的对齐、行高。对于 web 工程师来说总是不如使用 HTML 来得更直接、更方便。如今 HTML 节点已正式加入 G6 豪华流程图大礼包,各位再也不用看着 HTML CSS 眼馋了!

简单示例

image

示例源码

综合示例

image

该图展示了对鸢尾花数据集进行关联分析的过程中,如何可视化关联分析的结果。该示例用颜色映射的花的种类,连接线映射了关联度在 0.7 以上的关系。连线上点的大小表示关联度的强弱。每个节点所画的占比玉玦图,表示每个品种在该属性下,不同数值区间内的占比。由图可得出结论,花瓣长度和花瓣宽度,在所有品种下都有关联关系,并且各个品类间有明显的分层。所以这两个属性,适合用于做区分种类的关键指示器。该示例主要向大家展示了如何使用 html 节点巧妙的结合 G6 与 G2,从而呈现一份精彩的可视化作品。示例链接

动态计算锚点

有的时候,我们并不能写死锚点,需要根据节点本身动态计算锚点的数量和位置。这意味这 draw 方法里的信息要能传到,getAnchorPoints 里,此时我们不妨在 draw 方法中计算好锚点,再存到 group 里,然后在 getAnchorPoints 时将锚点信息取出,return 回去即可。

image

Demo源码

详解自定义边

相较于自定义节点,自定义边相对比较复杂一点,这里给出一些最佳实践,以帮助用户更好的画边。

注意:由于画边相对复杂,不建议大家直接复写 draw 方法。

配置项详解

  • 边控制点:points // 理论上 points 可以有无穷多个点,从 points[0] 到 points[n],依次是源节点到目标节点的控制点位置。
  • 目标节点: cfg.target // 目标节点 详见:Node API
  • 源节点:cfg.source // 源节点 详见:Node API
  • 颜色:cfg.color
  • 尺寸:cfg.size
  • 原始数据:cfg.model

自定义箭头

自定义箭头是常见的需求,但这个需求看似简单,真正实现起来却没那么简单。目前 G6 里还没有提供十分便捷的添加箭头的方式,只提供了一个比较基础的工具方法,若有自定义箭头的需求,不妨使用。

Demo源码