本文根据 #嘉* 《基于G6 的转化数据可视化展示 Demo》 整理而成。

背景

在某些项目中,可能会需要以树形的可视化形式来展示链路的转换数据,如有一个餐馆,统计当天的营业额,会分为线上和线下两条链路来统计,而线上链路,又分为饿了么及其他外卖平台等等。类似这种需求,参考了 G6 官网上的 Demo,提供的树图不能满足需求,我们就基于 G6 实现了该可视化链路转换图。

效果

image.png

分析

从上面的效果图我们可以看得出来,该流量链路转换图,共涉及到 2 个不同类型的节点,2 种不同类型的边,且边上还需要有 label,用于表示转换的百分比。

节点类型:

  • 主节点,会在第一行上面展示,可能会分为多个链路;
  • 叶子节点:在第二行展示,不会在进行分流和转换的节点。

边类型:

  • 直线,边上需要显示 label;
  • 折线:在主节点和第一个子节点连线的中间处转折,边上同样需要显示 label。

    方案

    有了上面的分析,我们就可以针对不同节点和边,使用 G6 的自定义节点及自定义边的功能来实现。

自定义转换节点

在数据中,我们会通过指定字段来区分该节点是主节点还是叶子节点,比如在我们的测试数据中,我们通过 row 字段来区分是展示到第一行还是第二行上面。

  1. G6.registerNode('operation', {
  2. drawShape: function (cfg, group) {
  3. var rect = group.addShape('rect', {
  4. attrs: cfg.row === 1
  5. ? {
  6. x: cfg.x,
  7. y: cfg.content ? cfg.y - 10 : cfg.y,
  8. width: 150,
  9. height: cfg.content ? 48 : 28,
  10. radius: cfg.content ? 4 : 12,
  11. stroke: '#1890FF',
  12. fill: '#E6F7FF',
  13. fillOpacity: 0.4,
  14. lineWidth: 2
  15. } : {
  16. x: cfg.x,
  17. y: cfg.content ? cfg.y - 10 : cfg.y,
  18. width: 150,
  19. height: cfg.content ? 48 : 28,
  20. radius: cfg.content ? 24 : 12,
  21. stroke: '#13C2C2',
  22. fill: '#E6FFFB',
  23. fillOpacity: 0.4,
  24. lineWidth: 2
  25. }
  26. });
  27. return rect;
  28. },
  29. drawLabel(cfg: any, group) {
  30. const label = group.addShape('text', {
  31. position: 'center',
  32. attrs: {
  33. x: cfg.x + 75,
  34. y: cfg.y + 14,
  35. position: 'center',
  36. text: cfg.content ? cfg.label + cfg.content : cfg.label,
  37. textAlign: 'center',
  38. textBaseline: 'middle',
  39. fill: '#666',
  40. stroke: '#FFF',
  41. fontSize: 12,
  42. }
  43. });
  44. return label;
  45. },
  46. }, 'single-node');

自定义折线

折线的实现主要通过控制 path 来实现,path 完全和 SVG 中 path 一样,不熟悉的同学可以参考这里

  1. G6.registerEdge('hvh', {
  2. draw(cfg, group) {
  3. return this.drawShape(cfg, group);
  4. },
  5. drawShape(cfg: EdgeConfig, group) {
  6. const startPoint = cfg.startPoint;
  7. const endPoint = cfg.endPoint;
  8. const shape = group.addShape('path', {
  9. attrs: {
  10. stroke: '#13C2C2',
  11. endArrow: {
  12. path: 'M 0 0 L 0, -5 L -10, 0, L 0, 5 Z',
  13. fill: '#13C2C2'
  14. },
  15. path: [
  16. ['M', startPoint.x, startPoint.y],
  17. ['L', endPoint.x / 3 + 2 / 3 * startPoint.x , startPoint.y],
  18. ['L', endPoint.x / 3 + 2 / 3 * startPoint.x , endPoint.y],
  19. ['L', endPoint.x - 10, endPoint.y]
  20. ]
  21. }
  22. });
  23. return shape;
  24. },
  25. drawLabel: function (cfg: EdgeConfig, group) {
  26. const endPoint = cfg.endPoint;
  27. var label = group.addShape('text', {
  28. position: 'right',
  29. attrs: {
  30. x: endPoint.x - 20,
  31. y: endPoint.y,
  32. position: 'right',
  33. textAlign: 'right',
  34. text: cfg.label,
  35. textBaseline: 'middle',
  36. fill: '#fff',
  37. stroke: '#fff',
  38. fontSize: 12,
  39. }
  40. });
  41. return label;
  42. },
  43. });

直线的定义比上面这些的还要简单,即 path 部分只需要 M 和 一个 L 即可。

箭头

需要特别说明一下箭头的实现,在 G6 3.4.1 以上的版本中,渲染引擎底层重构了箭头机制,使用上和之前的版本有所不同,具体区别如下:
image.png
关于箭头更详细的内容,请参考 G6 自定义边部分。

总结

在实现过程中,连接线起止锚点的指定在文档中没有找到,通过在其他 Demo 中看到,可以通过 sourceAnchor 和 targetAnchor 分别指定起止节点作为连接起点的锚点。

连线上的说明文案的设置,起初通过 edge 传值作为 label 字段设置,但对自定义折线不生效,因此通过自定义的方式,设置终点处的普通带文本的矩形节点进行添加。

说明:在 G6 3.4.7 及以上版本中,给边上的 label 设置背景色就不用这么麻烦了,直接通过 background 属性即可设置,更详细的内容请参考节点或边上 label 的背景