概述

本文档主要向大家介绍如何拓展 G6 的边( Edge ),如有描述不清楚、有误的地方欢迎大家在 GitHub 上提 Issue 指正,或是直接 PR 修缮。根据您的贡献度,我们会视情况将您加入 AntV 共建者名录 :-)

提示 : 在使用自定义图项之前,应尽量熟练掌握绘图引擎 G 的使用。

注册 — registerEdge

我们通过以下接口往 G6 全局注册边:

  1. // 注册边
  2. G6.registerEdge(name, {
  3. // 绘制
  4. draw(item) {
  5. return keyShape
  6. },
  7. // 获取路径
  8. getPath(item) {
  9. return path; // svg 规范 path 路径
  10. },
  11. // 起始箭头
  12. startArrow: {
  13. // 路径
  14. path(item) {},
  15. // 线缩短偏移
  16. shorten(item) {},
  17. // 样式
  18. style(item) {}
  19. },
  20. // 结束箭头
  21. endArrow: {
  22. // 路径
  23. path(item) {},
  24. // 线缩短偏移
  25. shorten(item) {},
  26. // 样式
  27. style(item) {}
  28. }
  29. }, extendShape);

绘制 — draw

跟节点一样,边的 draw 也是自定边图形的最小接口,决定了边最终画成什么样。 为了性能、箭头绘制成本等种种考虑,边的绘制不是绝对自由的。有以下几个规范的限制:

  • 必须 keyShape (既 draw 方法返回的图形) 必须是 G.Path 图形。

  • 必须 使用 getPath 方法获取 keyShape 的路径。

  • 推荐 根据 item.getPoints() 获取边的路径。

例如:

自定义边 - 图1

  1. G6.registerEdge('line', {
  2. draw(item) {
  3. const group = item.getGraphicGroup();
  4. const path = this.getPath(item);
  5. return group.addShape('path', {
  6. attrs: {
  7. path,
  8. stroke: 'red'
  9. }
  10. });
  11. },
  12. getPath(item) {
  13. const points = item.getPoints();
  14. return Util.pointsToPolygon(points);
  15. }
  16. });

获得路径 — getPath

getPath() 是用于获取边路径的方法。在自定义边的时候,既可以像上面那样,复写 draw()getPath() 也可以只复写 getPath() ,如果采用后者方法的好处是,你可以复用一些 G6 内部的实现,如 color,label,size 等,如下:

自定义边 - 图2

  1. G6.registerEdge('horizontal-smooth', {
  2. getPath(item) {
  3. var points = item.getPoints();
  4. var start = points[0];
  5. var end = points[points.length - 1];
  6. var hgap = Math.abs(end.x - start.x);
  7. if (end.x > start.x) {
  8. return [['M', start.x, start.y], ['C', start.x + hgap / 4, start.y, end.x - hgap / 2, end.y, end.x, end.y]];
  9. }
  10. return [['M', start.x, start.y], ['C', start.x - hgap / 4, start.y, end.x + hgap / 2, end.y, end.x, end.y]];
  11. }
  12. });
  13. const data = {
  14. nodes: [{
  15. id: 'node1',
  16. x: 100,
  17. y: 200
  18. },{
  19. id: 'node2',
  20. x: 300,
  21. y: 260
  22. }],
  23. edges: [{
  24. shape: 'horizontal-smooth',
  25. target: 'node2',
  26. source: 'node1'
  27. }]
  28. };
  29. const graph = new G6.Graph({
  30. container: 'mountNode',
  31. width: 500,
  32. height: 500
  33. });
  34. graph.edge({
  35. label(model) {
  36. return model.shape;
  37. },
  38. color: 'red',
  39. size: 2
  40. });
  41. graph.read(data);

箭头 — arrow

箭头虽然是个小东西,但实现它却是一个非常细致,且有一定复杂度的活儿。如果你稍微不注意,可能就会画出下面这样的东西:

  1. 露点自定义边 - 图3

  2. 戳进去自定义边 - 图4

绘制一个完美的箭头并不是件容易的事,详见:聊个 5 毛钱箭头 。为了降低自定义箭头的复杂度,G6 有自己的一套定义箭头的机制,如果您需要自定义一个箭头,你最多需要以下三个步骤:

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

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

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

自定义边 - 图6

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

自定义边 - 图7

示例:

这个示例给出了根据 AntV 箭头的设计稿,基于 G6 箭头机制的具体实现:

自定义边 - 图8

自定义边 - 图9

  1. G6.registerEdge('customEdge', {
  2. endArrow: {
  3. path(item) {
  4. const keyShape = item.getKeyShape();
  5. let lineWidth = keyShape.attr('lineWidth');
  6. lineWidth = lineWidth > MIN_ARROW_SIZE ? lineWidth : MIN_ARROW_SIZE;
  7. const width = lineWidth * 10 / 3;
  8. const halfHeight = lineWidth * 4 / 3;
  9. const radius = lineWidth * 4;
  10. return [
  11. [ 'M', -width, halfHeight ],
  12. [ 'L', 0, 0 ],
  13. [ 'L', -width, -halfHeight ],
  14. [ 'A', radius, radius, 0, 0, 1, -width, halfHeight ],
  15. [ 'Z' ]
  16. ];
  17. },
  18. shorten(item) {
  19. const keyShape = item.getKeyShape();
  20. const lineWidth = keyShape.attr('lineWidth');
  21. return (lineWidth > MIN_ARROW_SIZE ? lineWidth : MIN_ARROW_SIZE) * 3.1;
  22. },
  23. style(item) {
  24. const keyShape = item.getKeyShape();
  25. const { strokeOpacity, stroke } = keyShape.attr();
  26. return {
  27. fillOpacity: strokeOpacity,
  28. fill: stroke
  29. };
  30. }
  31. }
  32. });

示例片段