跳到主要内容

自定义数据处理

概述

自定义数据处理允许用户在业务实现过程中,把额外的数据处理封装起来,在渲染前或者布局后对数据进行进一步处理。用户通过自定义数据处理,实现部分数据处理解耦,更好地进行管理、编排代码以及提高代码可维护性。

开始前

用户在进行自定义数据处理前,需要仔细阅读并掌握数据处理器的 实现原理和执行时机 。

代码示例

接下来将讲述两个可能的业务场景,并通过自定义数据处理来实现:

不展示游离节点

  • 需求

    游离节点,即没有连线的节点,画布渲染时不展示游离节点

  • 实现

    import type \{ DrawData, DrawContext \} from '@antv/g6';
    import \{ Graph, BaseTransform, register, ExtensionCategory \} from '@antv/g6';
    
    class HideFreeNode extends BaseTransform \{
      public beforeDraw(input: DrawData, context: DrawContext): DrawData \{
        const \{ model \} = this.context;
        const \{ add, update, remove \} = input;
    
        add.nodes.forEach((nodeData, nodeId) => \{
          // 获取节点的相关连线
          const edges = model.getRelatedEdgesData(nodeId);
          // 没有任何连线的的节点则从add里面移除,添加到remove里面
          if (!edges.length) \{
            add.nodes.delete(nodeId);
            remove.nodes.set(nodeId, nodeData);
          \}
        \});
    
        return input;
      \}
    \}
    import { BaseTransform, ExtensionCategory, Graph, register } from '@antv/g6';
    
    class HideFreeNode extends BaseTransform {
      beforeDraw(input, context) {
        const { model } = this.context;
        const { add, update, remove } = input;
    
        add.nodes.forEach((nodeData, nodeId) => {
          // 获取节点的相关连线
          const edges = model.getRelatedEdgesData(nodeId);
          // 没有任何连线的的节点则从add里面移除,添加到remove里面
          if (!edges.length) {
            add.nodes.delete(nodeId);
            remove.nodes.set(nodeId, nodeData);
          }
        });
    
        return input;
      }
    }
    
    register(ExtensionCategory.TRANSFORM, 'hide-free-node', HideFreeNode);
    
    const data = {
      nodes: [{ id: '1' }, { id: '2' }, { id: '3' }, { id: '4' }, { id: '5' }],
      edges: [
        { source: '1', target: '2' },
        { source: '2', target: '3' },
        { source: '3', target: '5' },
      ],
    };
    
    const graph = new Graph({
      container: 'container',
      width: 600,
      height: 400,
      autoFit: 'center',
      data,
      node: {
        style: {
          labelText: (d) => d.id,
        },
      },
      layout: {
        type: 'antv-dagre',
        rankdir: 'LR',
      },
      transforms: ['hide-free-node'],
    });
    
    graph.render();
    
  • 说明

    示例中总共有6个节点,id为1-6,id为4的节点没有连线,因此被移除了。

    通过 getRelatedEdgesData 获取节点的相关连线,没有则把该节点放到 remove.nodes 里面去,并从 add.nodes 里面移除。

环形布局径向label

  • 需求

    使用 环形布局 时,节点 label 的也需要像内置数据处理器 PlaceRadialLabels 一样实现径向展示(但 PlaceRadialLabels 只支持径向布局,环形布局不是径向布局)

  • 实现

    import type \{ RuntimeContext, DrawContext, Point, TransformArray, Vector2, Vector3 \} from '@antv/g6';
    import \{ Graph, BaseTransform, register, ExtensionCategory, BaseTransformOptions \} from '@antv/g6';
    
    // 目前circular布局没有暴露方法可以获取布局中心,这里简单处理先固定一个,配置circular布局时center与这里保持一致即可
    const circularCenter = [300, 300];
    
    // 下面的函数 G6 没有暴露出来,先自行声明
    function subtract(a: Vector2 | Vector3, b: Vector2 | Vector3): Vector2 | Vector3 \{
      return a.map((v, i) => v - b[i]) as Vector2 | Vector3;
    \}
    function rad(a: Vector2 | Vector3): number \{
      const [x, y] = a;
      if (!x && !y) return 0;
      return Math.atan2(y, x);
    \}
    function rad2deg(rad: number): number \{
      return rad * (180 / Math.PI);
    \}
    
    interface CircularRadialLabelsOptions extends BaseTransformOptions \{
      offset?: number; // 偏移量
    \}
    
    class CircularRadialLabels extends BaseTransform<CircularRadialLabelsOptions> \{
      static defaultOptions = \{
        offset: 5,
      \};
      constructor(context: RuntimeContext, options: CircularRadialLabelsOptions) \{
        super(context, Object.assign(\{\}, CircularRadialLabels.defaultOptions, options));
      \}
      get center(): Point \{
        return circularCenter;
      \}
      public afterLayout() \{
        const \{ graph, model \} = this.context;
        const data = model.getData();
        data.nodes?.forEach((datum) => \{
          const radian = rad(subtract([datum.style.x, datum.style.y], this.center));
          const isLeft = Math.abs(radian) > Math.PI / 2;
          const isLeaf = !datum.children || datum.children.length === 0;
          const nodeId = datum.id;
          const node = this.context.element?.getElement(nodeId);
          if (!node || !node.isVisible()) return;
    
          const nodeHalfWidth = graph.getElementRenderStyle(nodeId).size / 2;
          const offset = (isLeaf ? 1 : -1) * (nodeHalfWidth + this.options.offset);
    
          const labelTransform: TransformArray = [
            ['translate', offset * Math.cos(radian), offset * Math.sin(radian)],
            ['rotate', isLeft ? rad2deg(radian) + 180 : rad2deg(radian)],
          ];
    
          model.updateNodeData([
            \{
              id: datum.id,
              style: \{
                labelTextAlign: isLeft === isLeaf ? 'right' : 'left',
                labelTextBaseline: 'middle',
                labelTransform,
              \},
            \},
          ]);
        \});
    
        graph.draw();
      \}
    \}
    import { BaseTransform, ExtensionCategory, Graph, register } from '@antv/g6';
    
    // 目前circular布局没有暴露方法可以获取布局中心,这里简单处理先固定一个
    const circularCenter = [300, 300];
    
    // 下面的函数 G6 没有暴露出来,先自行声明
    function subtract(a, b) {
      return a.map((v, i) => v - b[i]);
    }
    function rad(a) {
      const [x, y] = a;
      if (!x && !y) return 0;
      return Math.atan2(y, x);
    }
    function rad2deg(rad) {
      return rad * (180 / Math.PI);
    }
    
    class CircularRadialLabels extends BaseTransform {
      static defaultOptions = {
        offset: 5,
      };
      constructor(context, options) {
        super(context, Object.assign({}, CircularRadialLabels.defaultOptions, options));
      }
      get center() {
        return circularCenter;
      }
      afterLayout() {
        const { graph, model } = this.context;
        const data = model.getData();
        data.nodes?.forEach((datum) => {
          const radian = rad(subtract([datum.style.x, datum.style.y], this.center));
          const isLeft = Math.abs(radian) > Math.PI / 2;
          const isLeaf = !datum.children || datum.children.length === 0;
          const nodeId = datum.id;
          const node = this.context.element?.getElement(nodeId);
          if (!node || !node.isVisible()) return;
    
          const nodeHalfWidth = graph.getElementRenderStyle(nodeId).size / 2;
          const offset = (isLeaf ? 1 : -1) * (nodeHalfWidth + this.options.offset);
    
          const labelTransform = [
            ['translate', offset * Math.cos(radian), offset * Math.sin(radian)],
            ['rotate', isLeft ? rad2deg(radian) + 180 : rad2deg(radian)],
          ];
    
          model.updateNodeData([
            {
              id: datum.id,
              style: {
                labelTextAlign: isLeft === isLeaf ? 'right' : 'left',
                labelTextBaseline: 'middle',
                labelTransform,
              },
            },
          ]);
        });
    
        graph.draw();
      }
    }
    
    register(ExtensionCategory.TRANSFORM, 'circular-radial-labels', CircularRadialLabels);
    
    const data = {
      nodes: [{ id: '1' }, { id: '2' }, { id: '3' }, { id: '4' }, { id: '5' }, { id: '6' }],
      edges: [
        { source: '1', target: '2' },
        { source: '2', target: '3' },
        { source: '3', target: '4' },
        { source: '4', target: '5' },
        { source: '5', target: '6' },
        { source: '6', target: '1' },
      ],
    };
    
    const graph = new Graph({
      container: 'container',
      width: 600,
      height: 400,
      autoFit: 'center',
      data,
      node: {
        style: {
          labelText: (d) => 'label' + d.id,
          size: 30,
        },
      },
      layout: {
        type: 'circular',
        width: 200,
        center: circularCenter,
        preLayout: false, // 不能是渲染前布局,否则不生效
      },
      transforms: ['circular-radial-labels'],
    });
    
    graph.render();
    
  • 说明 上面的实现基本是参考内置数据处理器 PlaceRadialLabels 来实现的,区别是这里的实现是通过拿到布局中心来计算偏移和旋转,具体可参考 PlaceRadialLabels 的 源码

© Copyright 2026 Ant Group Co., Ltd..备案号:京ICP备15032932号-38
全站搜索
文档与示例输入关键词开始搜索

输入关键词,查找文档、API 与示例

↑↓选择↵打开Esc关闭