import GraphGL, { register } from '@graphgl/react';
import AntVLayout, { AntVLayoutOptions } from '@graphgl/react/layouts/adapter/antv';
AntVLayout.layouts.forEach((type) => {
AntVLayout.workerScirptURL = [
'https://unpkg.com/@antv/layout@1.2.13/dist/index.min.js',
'https://unpkg.com/@antv/graphlib@2.0.4/dist/index.umd.min.js',
];
register('layout', type, AntVLayout);
});
...
<GraphGL<AntVLayoutOptions>
options={{
layout: {
type: 'd3force'
}
}}
/>
import { Layouts, LayoutBaseProps, LayoutOptionsBase } from '@graphgl/react';
import forceAtlas2, {
ForceAtlas2Settings,
} from 'graphology-layout-forceatlas2';
import FA2Layout from 'graphology-layout-forceatlas2/worker';
import Graphology from 'graphology';
const getNodesBuffer = (nodes: any) => {
const length = nodes.length;
const nodeBuffer = new Float32Array(length * 2);
for (let index = 0; index < length; index++) {
nodeBuffer[index * 2] = nodes[index].attributes?.x as number;
nodeBuffer[index * 2 + 1] = nodes[index].attributes?.y as number;
}
return nodeBuffer;
};
export type GraphologyLayoutOptions = LayoutOptionsBase<
{
type: 'graphology-forceatlas2';
isTick?: boolean;
} & ForceAtlas2Settings
>;
export type GraphologyLayoutProps = LayoutBaseProps<GraphologyLayoutOptions>;
export default class GraphologyLayout extends Layouts.BaseLayout {
public options: GraphologyLayoutProps['options'] | null = null;
public nodes: GraphologyLayoutProps['nodes'] = [];
public edges: GraphologyLayoutProps['edges'] = [];
public layout: FA2Layout | null = null;
static layouts = ['graphology-forceatlas2'];
static workerScirptURL: string | string[] = [];
constructor(opts: GraphologyLayoutProps) {
super();
this.options = opts.options;
this.nodes = opts.nodes;
this.edges = opts.edges;
}
public destroy() {
this.layout?.stop();
this.layout?.kill();
}
public execute() {
const { nodes, edges, options } = this;
const { onTick, onLayoutEnd, isTick, type, ...restOptions } =
options as GraphologyLayoutProps['options'];
const graph = new Graphology();
graph.import({
nodes: nodes.map((item) => ({
key: item.id,
attributes: {
x: item.x === undefined ? Math.random() * 2000 - 1000 : item.x,
y: item.y === undefined ? Math.random() * 2000 - 1000 : item.y,
},
})),
edges: edges,
});
const sensibleSettings = forceAtlas2.inferSettings(graph);
const layout = new FA2Layout(graph, {
settings: {
...sensibleSettings,
...restOptions,
},
});
this.layout = layout;
this.layout.start();
const animate = () => {
requestAnimationFrame(() => {
const results = graph.export().nodes;
const nodesBuffer = getNodesBuffer(results);
if (this.layout?.isRunning()) {
onTick?.(nodesBuffer);
animate();
} else {
onLayoutEnd?.(nodesBuffer);
}
});
};
animate();
}
}