G6 provides a comprehensive collection of layout algorithms for automatic graph positioning. Layouts can be executed synchronously or asynchronously, with extensive configuration options for different graph types and use cases.
import { Graph } from '@antv/g6';
import type { LayoutOptions } from '@antv/g6';
// Simple layout configuration
const graph = new Graph({
layout: {
type: 'force',
preventOverlap: true,
nodeSize: 30
}
});
// Advanced layout configuration
const graph = new Graph({
layout: {
type: 'd3-force',
linkDistance: 100,
nodeStrength: -300,
edgeStrength: 0.6,
preventOverlap: true,
nodeSize: (node) => node.data?.size || 20,
animation: {
duration: 1000,
easing: 'ease-out'
}
}
});// Set and execute layout
await graph.setLayout({
type: 'circular',
radius: 200,
startRadius: 50,
endRadius: 300
});
await graph.layout(); // Execute current layout
// Execute layout with custom options
await graph.layout({
type: 'grid',
cols: 5,
rows: 4,
sortBy: 'degree'
});
// Stop running layout
graph.stopLayout();
// Get current layout configuration
const currentLayout = graph.getLayout();import type { D3ForceLayoutOptions } from '@antv/g6';
interface D3ForceLayoutOptions {
type: 'd3-force';
// Node forces
nodeStrength?: number | ((node: NodeData) => number); // Node repulsion strength (-300)
nodeSize?: number | ((node: NodeData) => number); // Node size for collision
// Edge forces
edgeStrength?: number | ((edge: EdgeData) => number); // Edge attraction strength (0.6)
linkDistance?: number | ((edge: EdgeData) => number); // Desired edge length (100)
// Simulation parameters
alpha?: number; // Initial energy (1)
alphaDecay?: number; // Energy decay rate (0.0228)
alphaMin?: number; // Minimum energy (0.001)
velocityDecay?: number; // Velocity decay (0.4)
// Positioning
center?: [number, number]; // Layout center
preventOverlap?: boolean; // Prevent node overlapping
// Animation
animation?: boolean | {
duration?: number;
easing?: string;
};
// Event callbacks
onLayoutStart?: () => void;
onLayoutEnd?: () => void;
onTick?: (progress: number) => void;
}
const d3ForceLayout = {
type: 'd3-force',
nodeStrength: (node) => -300 * (node.data?.importance || 1),
edgeStrength: 0.8,
linkDistance: (edge) => edge.data?.distance || 100,
preventOverlap: true,
nodeSize: 20,
animation: {
duration: 2000,
easing: 'ease-in-out'
},
onTick: (progress) => {
console.log(`Layout progress: ${Math.round(progress * 100)}%`);
}
};import type { ForceLayoutOptions } from '@antv/g6';
const forceLayout = {
type: 'force',
preventOverlap: true,
nodeSize: 30,
nodeSpacing: (node) => node.data?.spacing || 10,
linkDistance: 150,
nodeStrength: -500,
edgeStrength: 200,
collideStrength: 0.8,
center: [400, 300],
animation: true
};import type { ForceAtlas2LayoutOptions } from '@antv/g6';
const forceAtlas2Layout = {
type: 'forceAtlas2',
kr: 5, // Repulsive force strength
kg: 1, // Gravitational force strength
mode: 'normal', // 'normal' | 'linlog'
preventOverlap: true,
dissuadeHubs: false, // Reduce hub node influence
linLogMode: false, // Lin-log mode
strongGravityMode: false, // Strong gravity mode
gravity: 1, // Gravity strength
ks: 0.1, // Speed multiplier
ksMax: 10, // Maximum speed
tao: 0.1, // Convergence threshold
maxIteration: 1000 // Maximum iterations
};import type { DagreLayoutOptions } from '@antv/g6';
interface DagreLayoutOptions {
type: 'dagre';
rankdir?: 'TB' | 'BT' | 'LR' | 'RL'; // Layout direction
ranksep?: number; // Rank separation (50)
nodesep?: number; // Node separation (50)
controlPoints?: boolean; // Use control points (true)
// Node sizing
nodeSize?: number | [number, number] | ((node: NodeData) => number | [number, number]);
// Sorting and ranking
sortByCombo?: boolean; // Sort by combo membership
radial?: boolean; // Radial layout mode
}
const dagreLayout = {
type: 'dagre',
rankdir: 'TB', // Top to bottom
ranksep: 70,
nodesep: 50,
nodeSize: [60, 40],
controlPoints: true,
sortByCombo: false
};
// Radial dagre layout
const radialDagreLayout = {
type: 'dagre',
radial: true,
focusNode: 'root-node',
unitRadius: 100,
linkDistance: 150
};const antvDagreLayout = {
type: 'antv-dagre',
rankdir: 'LR', // Left to right
ranksep: 80,
nodesep: 40,
nodeSize: [80, 60],
preventOverlap: true
};import type { CompactBoxLayoutOptions } from '@antv/g6';
const compactBoxLayout = {
type: 'compactBox',
direction: 'LR', // 'LR' | 'RL' | 'TB' | 'BT'
getId: (node) => node.id,
getHeight: () => 32,
getWidth: () => 60,
getVGap: () => 16, // Vertical gap
getHGap: () => 32, // Horizontal gap
radial: false // Enable radial mode
};
// Radial compact box layout
const radialCompactBoxLayout = {
type: 'compactBox',
direction: 'LR',
radial: true,
focusNode: 'root'
};const dendrogramLayout = {
type: 'dendrogram',
direction: 'TB', // Tree direction
nodeSep: 36, // Node separation
rankSep: 128, // Rank separation
radial: false
};const indentedLayout = {
type: 'indented',
direction: 'LR',
indent: 30, // Indentation distance
getHeight: () => 16,
getWidth: () => 100
};const mindmapLayout = {
type: 'mindmap',
direction: 'H', // 'H' (horizontal) | 'V' (vertical)
getHeight: () => 32,
getWidth: () => 80,
getVGap: () => 16,
getHGap: () => 24,
getSide: (node) => {
// Determine which side of the root (for H direction)
return node.data?.side || 'right';
}
};import type { CircularLayoutOptions } from '@antv/g6';
const circularLayout = {
type: 'circular',
center: [400, 300], // Circle center
radius: 200, // Circle radius
startRadius: 50, // Starting radius (for spiral)
endRadius: 300, // Ending radius (for spiral)
clockwise: true, // Clockwise arrangement
divisions: 1, // Number of divisions
ordering: 'topology', // 'topology' | 'degree' | null
angleRatio: 1 // Angle ratio for partial circle
};const radialLayout = {
type: 'radial',
focusNode: 'center-node', // Focus node ID
unitRadius: 100, // Unit radius between levels
linkDistance: 150, // Edge length
maxIteration: 1000, // Maximum iterations
preventOverlap: true,
nodeSize: 30,
strictRadial: true // Strict radial positioning
};const concentricLayout = {
type: 'concentric',
center: [400, 300],
nodeSize: 30,
minNodeSpacing: 10, // Minimum space between nodes
preventOverlap: true,
sweep: Math.PI * 2, // Angular sweep
equidistant: false, // Equal distances between levels
startAngle: 0, // Starting angle
clockwise: true,
maxLevelDiff: undefined, // Maximum level difference
sortBy: 'degree' // Sorting criteria
};import type { GridLayoutOptions } from '@antv/g6';
const gridLayout = {
type: 'grid',
rows: 5, // Number of rows
cols: 4, // Number of columns
center: [400, 300], // Grid center
width: 600, // Grid width
height: 400, // Grid height
preventOverlap: true,
nodeSize: 30,
sortBy: 'degree', // 'degree' | 'clustering' | custom function
condense: false, // Condense to smaller area
position: (node, i) => { // Custom positioning function
return [i % 4 * 100, Math.floor(i / 4) * 100];
}
};const randomLayout = {
type: 'random',
center: [400, 300],
width: 600,
height: 400,
preventOverlap: true,
nodeSize: 30
};const mdsLayout = {
type: 'mds',
center: [400, 300],
linkDistance: 200, // Desired edge length
preventOverlap: true,
nodeSize: 30
};const fruchtermanLayout = {
type: 'fruchterman',
center: [400, 300],
maxIteration: 1000,
gravity: 10, // Gravity strength
speed: 5, // Speed multiplier
clustering: true, // Enable clustering
clusterGravity: 10 // Cluster gravity strength
};// Fishbone Layout (for cause-effect diagrams)
const fishboneLayout = {
type: 'fishbone',
direction: 'LR',
main: 'main-branch', // Main branch node
hGap: 50, // Horizontal gap
vGap: 30 // Vertical gap
};
// Snake Layout
const snakeLayout = {
type: 'snake',
direction: 'horizontal', // 'horizontal' | 'vertical'
cols: 5,
colGap: 20,
rowGap: 20
};const comboCombinedLayout = {
type: 'comboCombined',
center: [400, 300],
// Inner layout for nodes within combos
innerLayout: {
type: 'grid',
cols: 3
},
// Outer layout for combos themselves
outerLayout: {
type: 'circular',
radius: 200
},
spacing: [30, 30], // [combo spacing, node spacing]
preventOverlap: true
};// Progressive layout with progress tracking
await graph.layout({
type: 'd3-force',
animation: true,
onTick: (progress) => {
console.log(`Progress: ${progress}`);
updateProgressBar(progress);
},
onLayoutEnd: () => {
console.log('Layout completed');
}
});
// Stop layout execution
graph.stopLayout();
// Check if layout is running
const isLayoutRunning = graph.isLayoutRunning?.();// Sequential layout execution
await graph.layout({ type: 'grid' }); // Initial positioning
await graph.layout({ type: 'force' }); // Refinement with force
// Conditional layouts based on graph size
const nodeCount = graph.getNodeData().length;
const layoutType = nodeCount > 100 ? 'grid' : 'force';
await graph.layout({
type: layoutType,
animation: nodeCount < 50
});import { invokeLayoutMethod } from '@antv/g6';
// Invoke layout utility functions
const layoutResult = invokeLayoutMethod(
{ type: 'circular', radius: 200 },
graph.getData()
);
// Apply custom positioning logic
const customPositions = graph.getNodeData().map((node, i) => ({
id: node.id,
x: Math.cos(i * 0.5) * 200,
y: Math.sin(i * 0.5) * 200
}));
await graph.updateNodeData(customPositions);interface LayoutOptions {
type: string; // Layout algorithm type
animation?: boolean | {
duration?: number;
easing?: string;
};
onLayoutStart?: () => void;
onLayoutEnd?: () => void;
onTick?: (progress: number) => void;
[key: string]: any; // Algorithm-specific options
}
interface BaseLayoutOptions {
center?: [number, number]; // Layout center point
nodeSize?: number | number[] | ((node: NodeData) => number | number[]);
preventOverlap?: boolean; // Prevent node overlapping
animation?: boolean | AnimationConfig;
}
interface ForceLayoutOptions extends BaseLayoutOptions {
type: 'force' | 'd3-force' | 'forceAtlas2';
nodeStrength?: number | ((node: NodeData) => number);
edgeStrength?: number | ((edge: EdgeData) => number);
linkDistance?: number | ((edge: EdgeData) => number);
alpha?: number;
alphaDecay?: number;
velocityDecay?: number;
}
interface HierarchicalLayoutOptions extends BaseLayoutOptions {
type: 'dagre' | 'compactBox' | 'dendrogram' | 'indented' | 'mindmap';
direction?: 'TB' | 'BT' | 'LR' | 'RL';
rankSep?: number;
nodeSep?: number;
ranksep?: number;
nodesep?: number;
}
interface CircularLayoutOptions extends BaseLayoutOptions {
type: 'circular' | 'radial' | 'concentric';
radius?: number;
startRadius?: number;
endRadius?: number;
focusNode?: string;
unitRadius?: number;
clockwise?: boolean;
}
interface GridLayoutOptions extends BaseLayoutOptions {
type: 'grid';
rows?: number;
cols?: number;
width?: number;
height?: number;
sortBy?: 'degree' | 'clustering' | ((a: NodeData, b: NodeData) => number);
}