---
title: 自定义数据处理
order: 4
---
## 概述
自定义数据处理允许用户在业务实现过程中,把额外的数据处理封装起来,在渲染前或者布局后对数据进行进一步处理。用户通过自定义数据处理,实现部分数据处理解耦,更好地进行管理、编排代码以及提高代码可维护性。
## 开始前
用户在进行自定义数据处理前,需要仔细阅读并掌握数据处理器的 [实现原理和执行时机](/manual/transform/overview#实现原理) 。
## 代码示例
接下来将讲述两个可能的业务场景,并通过自定义数据处理来实现:
### 不展示游离节点
- **需求**
游离节点,即没有连线的节点,画布渲染时不展示游离节点
- **实现**
```typescript
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;
}
}
```
- **说明**
示例中总共有6个节点,id为1-6,id为4的节点没有连线,因此被移除了。
通过 `getRelatedEdgesData` 获取节点的相关连线,没有则把该节点放到 `remove.nodes` 里面去,并从 `add.nodes` 里面移除。
### 环形布局径向label
- **需求**
使用 [环形布局](/manual/layout/circular-layout) 时,节点 label 的也需要像内置数据处理器 [PlaceRadialLabels](/manual/transform/place-radial-labels) 一样实现径向展示(但 PlaceRadialLabels 只支持径向布局,环形布局不是径向布局)
- **实现**
```typescript
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 {
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();
}
}
```
- **说明**
上面的实现基本是参考内置数据处理器 [PlaceRadialLabels](/manual/transform/place-radial-labels) 来实现的,区别是这里的实现是通过拿到布局中心来计算偏移和旋转,具体可参考 PlaceRadialLabels 的 [源码](https://github.com/antvis/G6/blob/v5/packages/g6/src/transforms/place-radial-labels.ts)