g6/packages/site/docs/manual/new-features.zh.md
Aaron e08c299a69
docs: update v5 site docs (#5162)
* docs: remove readme and navigation in site

* docs: remove v4 core concept docs

* docs: update history and lod plugin docs

* chore: update dumirc config

* docs: add api shape overview doc

* docs: update manual docs

* docs: update manual and tutorial docs

* chore: update dumirc

* docs: remove design sector

* docs: update docs meta data

* docs: update api docs
2023-11-20 09:50:36 +08:00

20 KiB
Raw Blame History

title order
v5 特性 3

相较于 v4G6 v5 的新能力体现在:

  • 🎞 视觉与动画规范,使用 JSON spec 或映射函数的方式定义样式与动画;
  • 📂信息分层能力;
  • 🎨 简单灵活的主题配置能力;
  • 🤖 灵活强大的数据处理能力;
  • 🎄 树图结构的融合;
  • 🏀 3D 大图;
  • 🚀 性能飞跃,包括渲染与布局计算;
  • 🌠 多渲染器,可运行时切换;
  • 📦 包体积减少,支持 TreeShaking。

还有其他一些微小而美好的改变:

  • 轮廓包裹 Hull 支持文本配置;
  • 折线支持自动避障;
  • 文本自动适配宽度;
  • 采用临时层画布提升交互性能;
  • 图例自动从画布中获取样式。

正式版即将来袭。如果上面 Feature 是您所期待的,现在就可以使用 G6 5.0 Beta 版本进行尝鲜!若遇到任何升级问题,请在 GitHub 给我们留言。

为了支持上述全新能力G6 v5 相比于 v4 有比较大的 Breaking Change这可能带来一定的升级成本。希望上面全新能力带来的收益远大于升级成本。

1. 视觉与动画规范

JSON Spec 定义

Specification Doc

v5 中我们将所有节点/边/ combo 的图形进行规范化,每种类型的元素基本都有若干个规范的图形名称。包括自定义的元素,也应当遵循这样的规范。如果有额外的图形,统一放入 otherShapes 中。

  • 节点keyShape主图形、labelShape文本图形、haloShape某些状态下出现的背景光晕、labelBackgroundShape文本背景图形、iconShape节点中心的 icon 图形、badgeShapes节点四周的徽标图形、anchorShapes代表锚点的圆点图形
  • keyShape主图形、labelShape文本图形、haloShape某些状态下出现的背景光晕、labelBackgroundShape文本背景图形

因此,不论是什么类型的节点和边,都可以通过如下方式对其中的图形进行配置:

const graph = new Graph({
  node: {
    keyShape: {
      fill: "#f00',
      r: {
        fields: ['size'],
        formatter: model => Math.max(model.data.size[0], model.data.size[1]) / 2
      }
      // ...keyShape 的其他样式
    },
    labelShape: {
      // 可以指定一个确定字符串,也可以使用下面的映射方式,映射到数据的某个字段中。其他属性也可以使用这种映射
      text: {
        fields: ['name'],
        formatter: model => model.data.name
      },
      // ... labelShape 的其他样式
    },
    labelBackgroundShape: {
      padding: [2,2,2,2],
      fill: '#0f0'
      // ... labelShape 的背景图形的其他样式
    },
    iconShape: {
      // 内容可以是文本或图片, img 优先
      // img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
      text: 'label',
      // ... iconShape 更多配置
    },
    anchorShapes: [
      {
        position: [0, 0.5],
        r: 2,
        fill: 'red',
      },
      {
        position: [1, 0.5],
        r: 2,
        fill: 'green',
      },
      // 更多锚点图形(绘制)
    ],
    badgeShapes: [
      {
        text: 'running',
        position: 'rightTop',
        color: 'blue',
      },
      {
        text: 'error',
        position: 'right',
        color: 'blue',
      },
      // ... 更多 badge 图形
    ],
    otherShapes: {
      xShape: {...},
      yShape: {...},
      zShape: {...},
      // ... 更多额外的图形
    }
  }
})

函数映射配置

有时,我们需要根据不同的数据,返回不同的样式配置。这种需求下,函数配置相比于可以写 field+formatter 的方式更加灵活:

const graph = new Graph({
  node: (model) => {
    const { id, data } = model;
    const { size, showLabel } = data;

    // 注意返回的数据结构为完整的节点数据类型
    return {
      id,
      data: {
        ...data, // 注意将原数据中的原始 data 也返回,否则导致这些数据的丢失
        keyShape: {
          r: Math.max(size[0], size[1]) / 2,
          // ...
        },
        labelShape: showLabel
          ? {
              // 根据一个业务字段决定是否显示文本
              text: id,
              // ... 文本图形的其他配置
            }
          : undefined,
      },
    };
  },
});

动画配置

动画配置 DEMO

在 v4 中需要为节点设置动画,必须使用自定义节点,再用图形的动画 API 进行配置。动画开始和结束的时机也难以控制。v5 提供了 JSON spec 的方式定义动画。您可以在上面介绍的 graph 配置的 node / edge / combo 字段中指定 animates 字段:

const graph = new Graph({
  node: {
    animates: {
      buildIn: [...],
      buildOut: [...],
      update: [...],
      show: [...],
      hide: [...],
    }
  }
})

node / edge / combo 的函数式映射方式:

const graph = new Graph({
  node: model => {
    const { id, data } = model
    return {
      id,
      data: {
        ...data,
        // ... 其他样式配置
        animates: {
          buildIn: [...],
          buildOut: [...],
          update: [...],
          show: [...],
          hide: [...],
        }
      }
    }
  }
})

我们规范了动画的五个场景发生在各个图形的不同时机入场buildId、出场buildOut、update数据/状态更新、show出现相对于 hide、hide隐藏。每个场景的可以为不同的图形、不同的字段指定动画还可以指定动画的配置和执行顺序。例如下面表达了指定各类更新时的各种图形的动画

update: [
  {
    // 整个节点shapeId: 'group')在 x、y 发生变化时,动画更新
    fields: ['x', 'y'],
    shapeId: 'group',
    duration: 500,
  },
  {
    // 在 selected 和 active 状态变化导致的 haloShape opacity 变化时,使 opacity 带动画地更新
    fields: ['opacity'],
    shapeId: 'haloShape',
    states: ['selected', 'active'],
    duration: 500,
  },
  // 当 keyShape 的 fill、r 同时发生变化时,按照 order 指定的顺序带动画地更新,可以实现依次动画的效果
  {
    fields: ['fill'],
    shapeId: 'keyShape',
    order: 0,
  },
  {
    fields: ['r'],
    shapeId: 'keyShape',
    order: 1,
  },
];

2. 信息分层

信息分层 DEMO

信息分层可以为复杂的图减少视觉干扰,在放大图后再显示详细信息。可以在上面介绍的 graph 配置的 node / edge / combo 字段中指定 lodLevels 字段,如下面代码片段所示。其中 levels 定义了信息分层所响应的图缩放层级animateCfg 配置由信息分层导致的图形变更时的动画方式。然后需要在不同的图形样式配置中配置 lod 字段,来指定该图形在 levels 对应的哪个层级显示。

const graph = new Graph({
  node: {
    lodLevels: {
      levels: [
        { zoomRange: [0, 0.5] }, // -1
        { zoomRange: [0.5, 1], primary: true }, // 0
        { zoomRange: [1, 1.5] }, // 1
        { zoomRange: [1.5, 1] }, // 2
        { zoomRange: [2, Infinity] }, // 3
      ],
      animateCfg: {
        duration: 500,
      },
    },
    labelShape: {
      lod: 1, // 图的缩放大于 levels 第一层定义的 zoomRange[0] 时展示,小于时隐藏
    },
  },
});

或使用 node / edge / combo 函数映射的方式配置:

const graph = new Graph({
  node: (model) => {
    const { id, data } = model;
    const { isImportant } = data;
    return {
      id,
      data: {
        ...data,
        // ... 其他配置
        lodLevels: {
          levels: [
            { zoomRange: [0, 0.5] }, // -1
            { zoomRange: [0.5, 1], primary: true }, // 0
            { zoomRange: [1, 1.5] }, // 1
            { zoomRange: [1.5, 1] }, // 2
            { zoomRange: [2, Infinity] }, // 3
          ],
          animateCfg: {
            duration: 500,
          },
        },
        labelShape: {
          lod: isImportant ? -1 : 2, // 可以根据业务属性来判断在什么层级显示 label。比如是重要节点则在所有层级都显示文本否则放大到一定程度后再显示
        },
      },
    };
  },
});

3. 主题配置

主题配置 DEMO

G6 内置了亮色、暗色主题,也可自定义。使用方式如下:

const graph = new Graph({
  theme: {
    type: 'spec', // 内置的主题解析器
    base: 'light', // 使用亮色主题,暗色主题配置 base 为 'dark'
    specification: {
      node: {
        dataTypeField: 'cluster', // 指定节点映射颜色的字段名称
        // palette: ['#bae0ff', '#91caff', '#69b1ff', '#4096ff', '#1677ff', '#0958d9', '#003eb3', '#002c8c', '#001d66'], // 自定义色板
        // palette: { a: '#f00', b: '#0f0', c: '#00f' }, // 可以为特殊的字段值指定颜色
        // getStyleSets: (palette) => {
        //   // 更加自由的配置,针对不同的状态返回不同样式
        //   const styleSetsMap = {};
        //   Object.keys(palette).forEach((dataType) => {
        //     const color = palette[dataType];
        //     styleSetsMap[dataType] = {
        //       default: {
        //         keyShape: { fill: color },
        //         labelShape: { fill: color },
        //       },
        //       state1: {
        //         keyShape: { fill: '#000' },
        //       },
        //       state2: {
        //         keyShape: { stroke: '#f00' },
        //       },
        //       state3: {
        //         keyShape: { fill: '#ff0' },
        //       },
        //     };
        //   });
        //   return styleSetsMap;
        // },
      },
      edge: {
        dataTypeField: 'cluster', // 指定边映射颜色的字段
        // ...其他
      },
    },
  },
});

4. 数据处理

业务数据格式各异,可能不符合 G6 的数据格式。有时候,可能需要为数据提前计算一些字段,比如节点的度数等。此时,可以使用到 G6 v5 的数据处理模块。它作为 G6 v5 八大类型的扩展之一,在用户数据流入 Graph 之前执行。可以配置多个数据处理模块,它们将被线性执行。配置方法如下:

const graph = new Graph({
  // ... 其他图配置
  transforms: [
    'transform-v4-data', // 内置的数据处理器,将 v4 的数据格式转换为 v5
    {
      // 内置的数据处理器,节点大小映射到节点数据的 value 字段上,大小范围归一化到 [4, 28]
      type: 'map-node-size',
      field: 'value',
      range: [4, 28],
    },
  ],
});

您可以根据自己的业务数据格式,自定义数据处理器,并注册到 Graph 上后使用:

import { Graph, extend } from '@antv/g6';

const CustomDataTransform = (data, options, userGraphCore) => {
  data.nodes.forEach((node) => (node.data.cluster = node.data.bussinessState === '0' ? 'cluster1' : 'cluster2'));
  data.edges.forEach((edge) => (edge.data.keyShape = { lineWidth: edge.data.weight / 2 }));
  return data;
};

const ExtGraph = extend(Graph, {
  transforms: {
    'custom-data-transform': CustomDataTransform,
  },
});

const graph = new ExtGraph({
  // ... 其他图配置
  transforms: [
    'transform-v4-data', // 内置的数据处理器,将 v4 的数据格式转换为 v5
    'custom-data-transform', // 使用自定义的数据处理器
  ],
});

5. 树图和图的融合

图数据与树数据通用 DEMO

v5 新增树图相关 feature

  • 布局与 Graph 通用Graph 可以指定根节点,使用最小生成树建立树结构后使用树图布局算法;
  • 交互与 Graph 通用Graph 也可以展开和收起“子树”了,即无回溯边的下游节点;
  • 支持回溯边、环存在;
  • 支持森林(多棵树)。

如果需要使用 TreeGraphData只需要在配置 Graph 时给出一个数据类型的标记:

const graph = new Graph({
  // ... 其他配置项
  data: {
    type: 'treeData', // type 可以是 'graphData'、'treeData'、'fetch',其中 fetch 将在正式版支持
    value: data, // value 在 type 是 treeData 时,可以是 TreeGraphData 或 TreeGraphData[] 以支持森林的绘制
  },
});

data 字段可以给 GraphData 类型的数据,那么 G6 将作为普通图处理,并在必要时(如使用树图布局、交互时)生成树图结构。也可以指定 type 为 'treeData' 后给 value 传入 TreeGraphData 类型的数据,那么 G6 将会存储树图结构,并转换为普通图数据进行存储。

6. 3D 大图

3D DEMO

G6 v5 提供了 3D 大图渲染和交互能力,需要在 Graph 上配置 renderer: 'webgl-3d',并且配置对应的 3D 节点类型(目前仅支持 sphere-node、3D 交互,即可使用:

import { Graph, Extensions, extend } from '@antv/g6';
const ExtGraph = extend(Graph, {
  nodes: {
    'sphere-node': Extensions.SphereNode,
  },
  behaviors: {
    'orbit-canvas-3d': Extensions.OrbitCanvas3D,
    'zoom-canvas-3d': Extensions.ZoomCanvas3D,
  },
});

const graph = new ExtGraph({
  renderer: 'webgl-3d', // 可以是 canvas, svg, webgl, webgl-3d
  node: {
    type: 'sphere-node',
  },
  modes: {
    defaualt: ['orbit-canvas-3d', 'zoom-canvas-3d'],
  },
  // ...其他图配置
});

7. 性能飞跃 & 多渲染器

G6 支持了 WebGL 的 2D 和 3D 渲染,渲染性能得到极大提升。各个渲染器还可以在运行时切换。只需要在 Graph Shang 配置不同的 renderer 渲染器 DEMO

const graph = new Graph({
  // ...其他图配置
  renderer: 'canvas', // 'canvas', 'svg', 'webgl', 'webgl-3d'
});

同时G6 的布局包 @antv/layout 支持了 WASM 计算,使用时需要具体布局算法其从 @antv/layout-wasm 包引入,通过 extend 注册到 Graph 上,即可使用。WASM 布局 DEMO

import { ForceLayout as ForceLayoutWASM, supportsThreads, initThreads } from '@antv/layout-wasm';
const ExtGraph = extend(Graph, {
  layouts: {
    'force-wasm': ForceLayoutWASM,
  },
});

const supported = await supportsThreads();
const threads = await initThreads(supported);
const graph = new ExtGraph({
  layout: {
    type: 'force-wasm',
    threads,
    maxIteration: 200,
  },
  // ...其他图配置
});

8. 包体积减少

G6 v5 仅将最常用的功能默认注册到了 Graph 上,其他功能需要从 @antv/g6 或其他包中引入并注册到 Graph 上后,方可配置到 Graph 上.

import { Graph, extend, Extensions } from '@antv/g6';
// 外部引入的功能
import { ForceLayout as ForceLayoutWASM, supportsThreads, initThreads } from '@antv/layout-wasm';

// Class CustomBehaviorClass...
// Class CustomEdge...

const ExtGraph = extend(Graph, {
  behaviors: {
    'activate-relations': Extensions.ActivateRelations, // 内置的交互,未提前注册
    'some-custom-behavior': CustomBehaviorClass, // 自定义交互
  },
  nodes: {
    'modelRect-node': Extensions.ModelRectNode, // 内置的 modelRect 节点,未提前注册
  },
  edges: {
    'custom-edge': CustomEdge, // 自定义边
  },
  layouts: {
    'force-wasm': ForceLayoutWASM,
  },
});

const supported = await supportsThreads();
const threads = await initThreads(supported);
// 使用 extend 后的图进行实例化
const graph = new ExtGraph({
  // ... 其他配置项
  modes: {
    default: [
      'drag-node', // 默认注册的交互
      'activate-relations', // 刚刚引入并注册的内置交互
      'some-custom-behavior', // 自定义并注册的交互
    ],
  },
  defaultNode: {
    type: 'modelRect-node', // 刚刚引入并注册的内置节点类型
  },
  defaultEdge: {
    type: 'custom-edge', // 自定义并注册的边类型
  },
  layout: {
    type: 'force-wasm', // 刚刚从其他包引入并注册的布局算法
    threads,
    maxIteration: 200,
  },
});

默认注册的功能有:

const stdLib = {
  transforms: {
    'validate-data': ValidateData, // 数据校验器G6 内部将执行
    'transform-v4-data': TransformV4Data, // 转换 v4 数据
    'map-node-size': MapNodeSize, // 将节点大小映射到节点的某个指定的字段上
  },
  themes: {
    light: LightTheme, // 亮色主题
    dark: DarkTheme, // 暗色主题
  },
  themeSolvers: {
    spec: SpecThemeSolver, // 默认的主题处理器
  },
  layouts: {
    force: ForceLayout, // 力导向布局
    grid: GridLayout, // 格子布局
    circular: CircularLayout, // 环形布局
    concentric: ConcentricLayout, // 同心圆布局
    ...Hierarchy, // 所有树图布局,包括 DendropgramIndentedMindmapCompactBox
  },
  behaviors: {
    'drag-canvas': DragCanvas, // 拖拽画布
    'zoom-canvas': ZoomCanvas, // 缩放画布
    'drag-node': DragNode, // 拖拽节点
    'drag-combo': DragCombo, // 拖拽 Combo
    'collapse-expand-combo': CollapseExpandCombo, // 展开/收起 Combo
    'collapse-expand-tree': CollapseExpandTree, // 展开/收起子树
    'click-select': ClickSelect, // 点击选择
  },
  plugins: {
    history: History, // 历史栈
  },
  nodes: {
    'circle-node': CircleNode, // 圆形节点
    'rect-node': RectNode, // 矩形边
  },
  edges: {
    'line-edge': LineEdge, // 直线边
  },
  combos: {
    'circle-combo': CircleCombo, // 圆形 Combo
    'rect-combo': RectCombo, // 矩形 Combo
  },
  markers: {
    // 一些常用的图标
    collapse: (x, y, r) => {
      return [
        ['M', x - r, y],
        ['a', r, r, 0, 1, 0, r * 2, 0],
        ['a', r, r, 0, 1, 0, -r * 2, 0],
        ['M', x - r + 4, y],
        ['L', x + r - 4, y],
      ];
    },
    expand: (x, y, r) => {
      return [
        ['M', x - r, y],
        ['a', r, r, 0, 1, 0, r * 2, 0],
        ['a', r, r, 0, 1, 0, -r * 2, 0],
        ['M', x - r + 4, y],
        ['L', x - r + 2 * r - 4, y],
        ['M', x - r + r, y - r + 4],
        ['L', x, y + r - 4],
      ];
    },
    upTriangle: (x, y, r) => {
      const l1 = r * Math.cos(Math.PI / 6);
      const l2 = r * Math.sin(Math.PI / 6);
      return [['M', x - l1, y + l2], ['L', x + l1, y + l2], ['L', x, y - r], ['Z']];
    },
    downTriangle: (x, y, r) => {
      const l1 = r * Math.cos(Math.PI / 6);
      const l2 = r * Math.sin(Math.PI / 6);
      return [['M', x - l1, y - l2], ['L', x + l1, y - l2], ['L', x, y + r], ['Z']];
    },
  },
};

9. 其他微小而美好的改变

  • 轮廓包裹 Hull 支持文本配置:

Hull 支持文本 DEMO

  • 折线支持自动避障:

Polyline 避障 DEMO

  • 文本自动适配宽度:

文本自适应 DEMO

  • 采用临时层画布提升交互性能:
  • 图例自动从画布中获取样式: