概述

本文档主要向大家介绍如何拓展 G6 的边( Edge ),如有描述不清楚、有误的地方欢迎大家在 GitHub 上提 Issue 指正,或是直接 PR 修缮。根据您的贡献度,我们会视情况将您加入 AntV 共建者名录 :-)
提示 : 在使用自定义图项之前,应尽量熟练掌握绘图引擎 G 的使用。

注册 -- registerEdge

我们通过以下接口往 G6 全局注册边:
// 注册边
G6.registerEdge(name, {
  // 绘制
  draw(item) {
    return keyShape
  },
  // 获取路径
  getPath(item) {
    return path; // svg 规范 path 路径 
  },
  // 起始箭头
  startArrow: {
    // 路径
    path(item) {},
    // 线缩短偏移
    shorten(item) {},
    // 样式
    style(item) {}
  },
  // 结束箭头
  endArrow: {
    // 路径
    path(item) {},
    // 线缩短偏移
    shorten(item) {},
    // 样式
    style(item) {}
  }
}, extendShape);

绘制 -- draw

跟节点一样,边的 draw 也是自定边图形的最小接口,决定了边最终画成什么样。 为了性能、箭头绘制成本等种种考虑,边的绘制不是绝对自由的。有以下几个规范的限制:
例如:
G6.registerEdge('line', {
  draw(item) {
    const group = item.getGraphicGroup();
    const path = this.getPath(item);
    return group.addShape('path', {
      attrs: {
        path,
        stroke: 'red'
      }
    });
  },
  getPath(item) {
    const points = item.getPoints();
    return Util.pointsToPolygon(points);
  }
});

获得路径 -- getPath

getPath() 是用于获取边路径的方法。在自定义边的时候,既可以像上面那样,复写 draw()getPath() 也可以只复写 getPath() ,如果采用后者方法的好处是,你可以复用一些 G6 内部的实现,如 color,label,size 等,如下:
G6.registerEdge('horizontal-smooth', {
  getPath(item) {
    var points = item.getPoints();
    var start = points[0];
    var end = points[points.length - 1];
    var hgap = Math.abs(end.x - start.x);
    if (end.x > start.x) {
      return [['M', start.x, start.y], ['C', start.x + hgap / 4, start.y, end.x - hgap / 2, end.y, end.x, end.y]];
    }
    return [['M', start.x, start.y], ['C', start.x - hgap / 4, start.y, end.x + hgap / 2, end.y, end.x, end.y]];
  }
});

const data = {
  nodes: [{
    id: 'node1',
    x: 100,
    y: 200
 },{
    id: 'node2',
    x: 300,
    y: 260
 }],
  edges: [{
    shape: 'horizontal-smooth',
    target: 'node2',
    source: 'node1'
 }]
};
const graph = new G6.Graph({
  container: 'mountNode',
  width: 500,
  height: 500
});
graph.edge({
  label(model) {
    return model.shape;
  },
  color: 'red',
  size: 2
});
graph.read(data);

箭头 -- arrow

箭头虽然是个小东西,但实现它却是一个非常细致,且有一定复杂度的活儿。如果你稍微不注意,可能就会画出下面这样的东西:
  1. 露点
  2. 戳进去
绘制一个完美的箭头并不是件容易的事,详见:聊个 5 毛钱箭头 。为了降低自定义箭头的复杂度,G6 有自己的一套定义箭头的机制,如果您需要自定义一个箭头,你最多需要以下三个步骤:

第一步:设置箭头的形状 path [必选]

首先,我们要构建一个以自身坐标系原点,为箭头端点的,箭头 path 路径。

第二步:设置边的端点偏移 shorten [可选]

第三步:设置箭头的通用样式 style [可选]

示例:

这个示例给出了根据 AntV 箭头的设计稿,基于 G6 箭头机制的具体实现:
G6.registerEdge('customEdge', {
  endArrow: {
    path(item) {
      const keyShape = item.getKeyShape();
      let lineWidth = keyShape.attr('lineWidth');
      lineWidth = lineWidth > MIN_ARROW_SIZE ? lineWidth : MIN_ARROW_SIZE;
      const width = lineWidth * 10 / 3;
      const halfHeight = lineWidth * 4 / 3;
      const radius = lineWidth * 4;
      return [
      [ 'M', -width, halfHeight ],
      [ 'L', 0, 0 ],
      [ 'L', -width, -halfHeight ],
      [ 'A', radius, radius, 0, 0, 1, -width, halfHeight ],
      [ 'Z' ]
      ];
    },
    shorten(item) {
      const keyShape = item.getKeyShape();
      const lineWidth = keyShape.attr('lineWidth');
      return (lineWidth > MIN_ARROW_SIZE ? lineWidth : MIN_ARROW_SIZE) * 3.1;
    },
    style(item) {
      const keyShape = item.getKeyShape();
      const { strokeOpacity, stroke } = keyShape.attr();
      return {
        fillOpacity: strokeOpacity,
        fill: stroke
      };
    }
   }
});
示例片段