G6(三)G6元素及配置

706 阅读5分钟

图的元素特指图上的节点 Node 和边 Edge 。G6 内置了一系列 内置的节点 和 内置的边,供用户自由选择。G6 不同的内置节点或不同的内置边主要区别在于元素的 图形 Shape,例如,节点可以是圆形、矩形、图片等。

G6元素的属性

不论是节点还是边,它们的属性分为两种:

  • 样式属性 style:对应 Canvas 中的各种样式,在元素状态 State 发生变化时,可以被改变;
  • 其他属性:例如图形类型( type)、id(id )一类在元素状态 State 发生变化时不能被改变的属性。

例如,G6 设定 hover 或 click 节点,造成节点状态的改变,只能自动改变节点的样式属性(如 fill、stroke 等),其他属性(如 type 等)不能被改变。如果需要改变其他属性,要通过 graph.updateItem 手动配置。样式属性是一个名为 style 的对象, style 字段与其他属性并行。

数据结构

以节点元素为例,其属性的数据结构如下:

{
  id: 'node0',          // 元素的 id
  type: 'circle',       // 元素的图形
  size: 40,             // 元素的大小
  label: 'node0'        // 标签文字
  visible: true,        // 控制初次渲染显示与隐藏,若为 false,代表隐藏。默认不隐藏
  labelCfg: {           // 标签配置属性
    positions: 'center',// 标签的属性,标签在元素中的位置
    style: {            // 包裹标签样式属性的字段 style 与标签其他属性在数据结构上并行
      fontSize: 12      // 标签的样式属性,文字字体大小
      // ...            // 标签的其他样式属性
    }
  }
  // ...,               // 其他属性
  style: {              // 包裹样式属性的字段 style 与其他属性在数据结构上并行
    fill: '#000',       // 样式属性,元素的填充色
    stroke: '#888',     // 样式属性,元素的描边色
    // ...              // 其他样式属性
  }
}

边元素的属性数据结构与节点元素相似,只是其他属性中多了 source 和 target 字段,代表起始和终止节点的 id。

属性配置

G6 中,根据不同的场景需求,有 7 种配置元素属性的方式。这里,介绍其中的两种:

  1. 实例化图时配置元素的全局属性;
  2. 在数据中配置。

1. 实例化图时全局配置

适用场景: 所有节点统一的属性配置,所有边统一的属性配置。

使用方式: 使用图的两个配置项:

  • defaultNode:节点在默认状态下的样式属性(style)和其他属性;
  • defaultEdge:边在默认状态下的样式属性(style)和其他属性。

由于是统一的配置,不能根据数据中的属性(如 class、weight)等值的不同进行个性化设置。

效果:

image.png 代码:

const graph = new G6.Graph({
    container: containerRef.current, // String | HTMLElement,必须,在 Step 1 中创建的容器 id 或容器本身
    width: 800, // Number,必须,图的宽度
    height: 600, // Number,必须,图的高度
    fitView: true, // 是否将图适配到画布中
    fitViewPadding: [20, 40, 50, 20], // 画布上四周的留白宽度
    // 节点在默认状态下的样式配置(style)和其他配置
    defaultNode: {
        size: 40, //节点的大小
        // 节点样式配置
        style: {
            fill: "steelblue", // 节点填充色
            stroke: "#666", // 节点描边色
            lineWidth: 1, // 节点描边粗细
        },
        // 节点上的标签文本配置
        labelCfg: {
            // 节点上的标签文本样式配置
            style: {
                fill: "#fff", // 节点标签文字颜色
            },
        },
    },
    // 边在默认状态下的样式配置(style)和其他配置
    defaultEdge: {
        // 边样式配置
        style: {
            opacity: 0.6, // 边透明度
            stroke: "grey", // 边描边颜色
        },
        // 边上的标签文本配置
        labelCfg: {
            autoRotate: true, // 边上的标签文本根据边的方向旋转
        },
    },
});

2. 在数据中配置

适用场景: 不同节点/边可以有不同的个性化配置。

因此,这种配置方式可以满足下面需求:

  • 根据数据中节点的 class 属性映射节点的形状,对应节点其他属性:type;
  • 根据数据中边的 weight 属性映射边的粗细,对应边样式属性:lineWidth。

使用方式: 可以直接将配置写入数据文件;也可以在读入数据后,通过遍历的方式写入配置。

遍历数据进行属性的配置:

const remoteData = await response.json();
const nodes = remoteData.nodes;
nodes.forEach((node) => {
    if (!node.style) {
        node.style = {};
    }
    // 根据节点数据中的 class 属性配置图形
    switch (node.class) {
        case "c0": {
            node.type = "circle"; // class = 'c0' 时节点图形为 circle
            break;
        }
        case "c1": {
            node.type = "rect"; // class = 'c1' 时节点图形为 rect
            node.size = [35, 20]; // class = 'c1' 时节点大小
            break;
        }
        case "c2": {
            node.type = "ellipse"; // class = 'c2' 时节点图形为 ellipse
            node.size = [35, 20]; // class = 'c2' 时节点大小
            break;
        }
    }
});
graph.data(remoteData); // 绑定数据

效果如下: image.png

可以看到,图中有一些节点被渲染成了矩形,还有一些被渲染成了椭圆形。除了设置 type 属性之外,我们还覆盖了上文全局配置的节点的 size 属性,在矩形和椭圆的情况下,size 是一个数组;而在默认圆形的情况下,G6 将仍然读取全局配置的 size 属性为数字 30。也就是说,动态配置属性让我们既可以根据数据的不同配置不同的属性值,也可以有能力覆盖全局静态的属性值。

进一步地,我们尝试根据数据的比重不同,配置不一样边的粗细:

const edges = remoteData.edges;
edges.forEach((edge) => {
    if (!edge.style) {
        edge.style = {};
    }
    edge.style.lineWidth = edge.weight; // 边的粗细映射边数据中的 weight 属性数值
});

效果如下:

image.png

如图所示,边的粗细已经按照数据的比重成功渲染了出来,但是边原有的样式(透明度、颜色)却丢失了。这是因为我们提到过动态配置属性会覆盖全局配置属性,这里配置了 style.lineWidth,导致覆盖了全局的 style 对象。解决办法是将被覆盖的边的样式都移到动态配置里面来:

const graph = new G6.Graph({
    // ...
    // 边在默认状态下的样式配置(style)和其他配置
    defaultEdge: {
        // 去掉全局配置的 style
        // 边上的标签文本配置
        labelCfg: {
            autoRotate: true, // 边上的标签文本根据边的方向旋转
        },
    },
});

// 遍历边数据
const edges = remoteData.edges;
edges.forEach((edge) => {
  if (!edge.style) {
    edge.style = {};
  }
  edge.style.lineWidth = edge.weight; // 边的粗细映射边数据中的 weight 属性数值
  // 移到此处
  edge.style.opacity = 0.6;
  edge.style.stroke = 'grey';
});

graph.data(remoteData);
graph.render();