节点与边的层级

节点与边在视觉上的层级涉及到了它们相对应的图形分组 Group。本文提到的所有分组 Group 都为 G6 的 图形分组 Group,而非 G6 的 节点分组 Group,请注意区分这两种 Group,其区别在 图形分组 Group 中说明。

图形分组 Group 中我们提到:在 G6 中,Graph 的一个实例中的所有节点属于同一个变量名为 nodeGroup 的 group,所有的边属于同一个变量名为 edgeGroup 的 group。节点 group 在视觉上的层级(zIndex)高于边 group,即所有节点会绘制在所有边的上层。

但有时,我们需要让边在视觉上在节点上层。例如,高亮节点及其相关边和邻居、高亮一条边等。可以通过配合图实例的配置项 groupByTypes 以及节点和边的 toFront()toBack() 函数实现。为实现如下效果:鼠标进入节点时,提升相关边以及邻居节点的层级;离开节点时恢复;鼠标进入边时,提升边及其两端点的层级;离开边时恢复。Demo 完整代码节点与边的层级 - 图1

要实现上图效果,需要以下步骤:

  • Step 1:实例化图时配置 groupByTypesfalse
  • Step 2:将节点放置在边上层;
  • Step 3:监听鼠标事件并改变目标元素层级。

前提代码

下面代码完成了引入 G6、数据设置、实例化图、渲染图的命令等。后文将修改下面这份代码中以达到上图高亮效果。

  1. // 数据源
  2. const data = {
  3. nodes: [
  4. {
  5. id: 'node0',
  6. x: 100,
  7. y: 100,
  8. size: 20,
  9. },
  10. {
  11. id: 'node1',
  12. x: 200,
  13. y: 200,
  14. size: 20,
  15. },
  16. {
  17. id: 'node2',
  18. x: 150,
  19. y: 150,
  20. size: 20,
  21. },
  22. {
  23. id: 'node3',
  24. x: 150,
  25. y: 250,
  26. size: 20,
  27. },
  28. {
  29. id: 'node4',
  30. x: 150,
  31. y: 200,
  32. size: 20,
  33. },
  34. ],
  35. edges: [
  36. {
  37. id: 'edge0',
  38. source: 'node0',
  39. target: 'node1',
  40. },
  41. {
  42. id: 'edge1',
  43. source: 'node2',
  44. target: 'node3',
  45. },
  46. ],
  47. };
  48. // 实例化图
  49. const graph = new G6.Graph({
  50. container: 'mountNode',
  51. width: 800,
  52. height: 600,
  53. // 为方便演示,加粗边
  54. defaultEdge: {
  55. style: {
  56. lineWidth: 2,
  57. },
  58. },
  59. });
  60. // 读取数据
  61. graph.data(data);
  62. // 渲染图
  63. graph.render();

Step 1 实例化图时的配置

groupByTypes 是图的一个配置项,当其为默认值 true 时,所有节点在一个名为 nodeGroup 的分组,所有边在另一个名为 edgeGroup 的分组,且 nodeGroupedgeGroup 上层。将其设置为 false 后,将不存在 nodeGroupedgeGroup,所有节点和边在同一个分组,它们的层级根据生成的顺序决定。

参数描述

名称类型默认值描述
groupByTypesBooleantrue各种元素是否在一个分组内,决定节点和边的层级问题,默认情况下所有的节点在一个分组中,所有的边在一个分组中,当这个参数为 false 时,节点和边的层级根据生成的顺序确定。

使用方式

更改 前提代码 中实例化图部分代码,添加 groupByTypes 配置项,并设置为 false

  1. const graph = new G6.Graph({
  2. // ... // 其他配置
  3. groupByTypes: false,
  4. });

此时,将会得到如下效果:节点与边的层级 - 图2

Step 2 将节点放置在边上层

上一步结果中节点在边的下层不符合常规,是由于 groupByTypesfalse 时,节点和边的层级根据生成的顺序确定,而边的生成顺序在节点之后,因此所有边被绘制到了节点上方。为了使图符合常规——节点在上层,边在下层,可以在 graph.render() 之后将所有的节点通过 toFront() 函数提前。

函数描述

  1. // 将节点实例 nodeItem 提前到其父级 group 的最前面
  2. nodeItem.toFront();
  3. // 将节点实例 nodeItem 放置到其父级 group 的最后面
  4. nodeItem.toBack();
  5. // 将边实例 edgeItem 提前到其父级 group 的最前面
  6. edgeItem.toFront();
  7. // 将边实例 edgeItem 放置到其父级 group 的最后面
  8. edgeItem.toBack();

书用方法

  1. // const graph = ...
  2. graph.data(data);
  3. graph.render();
  4. // 获取图上的所有边实例
  5. const nodes = graph.getNodes();
  6. // 遍历边实例,将所有边提前。
  7. nodes.forEach(node => {
  8. node.toFront();
  9. });
  10. // 更改层级后需要重新绘制图
  11. graph.paint();

这样,所有节点被绘制在边上层:节点与边的层级 - 图3

Step 3 监听鼠标事件并改变目标元素层级

在效果图中,鼠标进入节点时,相关边和节点的层级被提升到最上层,鼠标离开节点使恢复。边同理。这一步将实现这一交互效果。

函数描述

使用下面四个函数监听鼠标的进入、离开元素的事件:

  1. // 鼠标进入节点事件
  2. graph.on('node:mouseenter', ev => {
  3. // ...
  4. });
  5. // 鼠标离开节点事件
  6. graph.on('node:mouseleave', ev => {
  7. // ...
  8. });
  9. // 鼠标进入边事件
  10. graph.on('edge:mouseenter', ev => {
  11. // ...
  12. });
  13. // 鼠标离开边事件
  14. graph.on('edge:mouseleave', ev => {
  15. // ...
  16. });

使用方法

  1. // 鼠标进入节点事件
  2. graph.on('edge:mouseenter', ev => {
  3. // 获得鼠标当前目标边
  4. const edge = ev.item;
  5. // 该边的起始点
  6. const source = edge.getSource();
  7. // 该边的结束点
  8. const target = edge.getTarget();
  9. // 先将边提前,再将端点提前。这样该边两个端点还是在该边上层,较符合常规。
  10. edge.toFront();
  11. source.toFront();
  12. target.toFront();
  13. // 注意:必须调用以根据新的层级顺序重绘
  14. graph.paint();
  15. });
  16. graph.on('edge:mouseleave', ev => {
  17. // 获得图上所有边实例
  18. const edges = graph.getEdges();
  19. // 遍历边,将所有边的层级放置在后方,以恢复原样
  20. edges.forEach(edge => {
  21. edge.toBack();
  22. });
  23. // 注意:必须调用以根据新的层级顺序重绘
  24. graph.paint();
  25. });
  26. graph.on('node:mouseenter', ev => {
  27. // 获得鼠标当前目标节点
  28. const node = ev.item;
  29. // 获取该节点的所有相关边
  30. const edges = node.getEdges();
  31. // 遍历相关边,将所有相关边提前,再将相关边的两个端点提前,以保证相关边的端点在边的上方常规效果
  32. edges.forEach(edge => {
  33. edge.toFront();
  34. edge.getSource().toFront();
  35. edge.getTarget().toFront();
  36. });
  37. // 注意:必须调用以根据新的层级顺序重绘
  38. graph.paint();
  39. });
  40. graph.on('node:mouseleave', ev => {
  41. // 获得图上所有边实例
  42. const edges = graph.getEdges();
  43. // 遍历边,将所有边的层级放置在后方,以恢复原样
  44. edges.forEach(edge => {
  45. edge.toBack();
  46. });
  47. // 注意:必须调用以根据新的层级顺序重绘
  48. graph.paint();
  49. });