G6's plugin system provides extensible UI components and visual enhancements for graph visualizations. Plugins can add interactive widgets, visual effects, and utility features with comprehensive configuration options.
import { Graph } from '@antv/g6';
import type { PluginOptions, UpdatePluginOption } from '@antv/g6';
// Simple plugin configuration
const graph = new Graph({
plugins: [
{ key: 'minimap', type: 'minimap' },
{ key: 'tooltip', type: 'tooltip' },
{ key: 'toolbar', type: 'toolbar' }
]
});
// Advanced plugin configuration
const graph = new Graph({
plugins: [
{
key: 'custom-minimap',
type: 'minimap',
size: [300, 200],
position: 'top-right',
padding: 10
}
]
});// Update plugins at runtime
graph.setPlugins([
{ key: 'minimap', type: 'minimap', size: [200, 120] },
{ key: 'grid', type: 'grid-line' }
]);
// Update specific plugin
graph.updatePlugin({
key: 'minimap',
size: [250, 150],
position: 'bottom-left'
});
// Get current plugins
const plugins = graph.getPlugins();
// Get plugin instance for direct manipulation
const minimap = graph.getPluginInstance<MinimapPlugin>('minimap');
minimap.show();
minimap.hide();import type {
MinimapOptions,
Placement,
Padding,
ElementType
} from '@antv/g6';
// Minimap Plugin
interface MinimapOptions {
size?: [number, number]; // Width and height [240, 160]
padding?: Padding; // Internal padding (10)
position?: Placement; // Position relative to canvas
filter?: (id: string, elementType: ElementType) => boolean;
shape?: 'key' | ((id: string, elementType: ElementType) => DisplayObject);
className?: string; // Canvas class name
container?: HTMLElement | string; // Mount container
refresh?: boolean; // Auto refresh on graph changes
}
const minimapPlugin = {
key: 'minimap',
type: 'minimap',
size: [200, 120],
position: 'right-bottom',
padding: 8,
filter: (id, elementType) => {
// Only show nodes in minimap
return elementType === 'node';
},
shape: 'key' // Use simplified shapes
};
// Fullscreen Plugin
const fullscreenPlugin = {
key: 'fullscreen',
type: 'fullscreen',
className: 'g6-fullscreen-button',
container: document.getElementById('toolbar')
};
// Camera Setting Plugin
const cameraSettingPlugin = {
key: 'camera-setting',
type: 'camera-setting',
projectionMode: '3d', // '2d' | '3d'
trackballControls: true,
enableRotate: true,
enableZoom: true,
enablePan: true
};import type {
TooltipOptions,
ContextmenuOptions,
ToolbarOptions
} from '@antv/g6';
// Tooltip Plugin
interface TooltipOptions {
enable?: boolean | ((event: any) => boolean);
trigger?: 'hover' | 'click'; // Activation trigger
enterable?: boolean; // Allow mouse enter tooltip
offset?: [number, number]; // Position offset
placement?: Placement; // Tooltip placement
getContent?: (event: any) => string | HTMLElement;
className?: string; // CSS class name
style?: Record<string, any>; // Inline styles
}
const tooltipPlugin = {
key: 'tooltip',
type: 'tooltip',
trigger: 'hover',
enterable: true,
placement: 'top',
getContent: (event) => {
const { target } = event;
if (target.elementType === 'node') {
return `<div>Node: ${target.id}</div>`;
}
return null;
},
style: {
backgroundColor: '#fff',
border: '1px solid #ccc',
borderRadius: '4px',
padding: '8px'
}
};
// Context Menu Plugin
const contextmenuPlugin = {
key: 'contextmenu',
type: 'contextmenu',
enable: true,
trigger: 'contextmenu',
getItems: (event) => {
const { target, targetType } = event;
if (targetType === 'node') {
return [
{ key: 'delete', label: 'Delete Node' },
{ key: 'edit', label: 'Edit Node' },
{ key: 'focus', label: 'Focus' }
];
}
return [];
},
onItemClick: (key, event) => {
console.log(`Menu item ${key} clicked`, event);
}
};
// Toolbar Plugin
const toolbarPlugin = {
key: 'toolbar',
type: 'toolbar',
position: 'top-left',
items: [
{
key: 'zoom-in',
type: 'button',
icon: 'zoom-in',
tooltip: 'Zoom In',
onClick: () => graph.zoomBy(1.2)
},
{
key: 'zoom-out',
type: 'button',
icon: 'zoom-out',
tooltip: 'Zoom Out',
onClick: () => graph.zoomBy(0.8)
},
{
key: 'fit-view',
type: 'button',
icon: 'fit-view',
tooltip: 'Fit View',
onClick: () => graph.fitView()
}
]
};// Background Plugin
const backgroundPlugin = {
key: 'background',
type: 'background',
background: '#f5f5f5', // Background color
backgroundImage: 'url(pattern.png)', // Background image
backgroundSize: 'cover', // 'cover' | 'contain' | 'auto'
backgroundRepeat: 'no-repeat', // 'repeat' | 'no-repeat'
backgroundPosition: 'center' // Background position
};
// Grid Line Plugin
const gridLinePlugin = {
key: 'grid-line',
type: 'grid-line',
follow: true, // Follow viewport transform
line: {
stroke: '#e8e8e8',
lineWidth: 1
},
size: 20, // Grid size
visible: true // Initial visibility
};
// Watermark Plugin
const watermarkPlugin = {
key: 'watermark',
type: 'watermark',
text: 'G6 Graph', // Watermark text
textStyle: {
fontSize: 16,
fill: '#000',
opacity: 0.1
},
position: 'bottom-right',
offset: [20, 20],
rotate: -Math.PI / 12 // Rotation angle
};
// Legend Plugin
const legendPlugin = {
key: 'legend',
type: 'legend',
position: 'top-left',
orientation: 'vertical', // 'horizontal' | 'vertical'
data: [
{ label: 'Type A', color: '#1890FF', marker: 'circle' },
{ label: 'Type B', color: '#52C41A', marker: 'rect' },
{ label: 'Type C', color: '#FAAD14', marker: 'diamond' }
],
onItemClick: (item, index) => {
console.log('Legend item clicked:', item);
}
};// Hull Plugin (Contour/Convex Hull)
const hullPlugin = {
key: 'hull',
type: 'hull',
members: ['node1', 'node2', 'node3'], // Hull members
type: 'round-convex', // 'convex' | 'round-convex' | 'smooth-convex'
style: {
fill: '#1890FF',
fillOpacity: 0.1,
stroke: '#1890FF',
strokeOpacity: 0.5,
lineWidth: 1
},
padding: 10, // Padding around members
animate: true, // Animate hull changes
duration: 300
};
// Bubble Sets Plugin
const bubbleSetsPlugin = {
key: 'bubble-sets',
type: 'bubble-sets',
groups: [
{
key: 'group1',
members: ['node1', 'node2'],
style: {
fill: '#1890FF',
fillOpacity: 0.2
}
}
],
nonMemberInfluence: 10, // Influence on non-members
memberInfluence: 40, // Influence on members
threshold: 6 // Threshold for bubble generation
};
// Fisheye Plugin
const fisheyePlugin = {
key: 'fisheye',
type: 'fisheye',
trigger: 'mousemove', // 'mousemove' | 'click' | 'drag'
d: 1.5, // Distortion factor
range: 200, // Effect range
showLabel: 2, // Show labels at zoom level
scaleRBy: 'radius' // Scale by radius or unzoom
};
// Edge Bundling Plugin
const edgeBundlingPlugin = {
key: 'edge-bundling',
type: 'edge-bundling',
K: 100, // Bundling strength
lambda: 0.1, // Edge length factor
divisions: 1, // Number of division points
cycles: 6, // Number of cycles
iterations: 90, // Iterations per cycle
iterationsRate: 0.6666667 // Iteration rate decay
};
// Edge Filter Lens Plugin
const edgeFilterLensPlugin = {
key: 'edge-filter-lens',
type: 'edge-filter-lens',
trigger: 'mousemove',
r1: 60, // Inner radius
r2: 120, // Outer radius
showLabel: true
};// History Plugin (Undo/Redo)
const historyPlugin = {
key: 'history',
type: 'history',
stackSize: 20, // Maximum history stack size
enableStack: true, // Enable history stack
ignoreAdd: false, // Ignore add operations
ignoreRemove: false, // Ignore remove operations
ignoreUpdate: false, // Ignore update operations
ignoreStateChange: true, // Ignore state changes
ignoreLayoutChange: false // Ignore layout changes
};
// Access history operations
const history = graph.getPluginInstance('history');
history.undo(); // Undo last operation
history.redo(); // Redo last undone operation
history.clear(); // Clear history
const canUndo = history.canUndo(); // Check if can undo
const canRedo = history.canRedo(); // Check if can redo
// Snapline Plugin
const snaplinePlugin = {
key: 'snapline',
type: 'snapline',
enable: true,
line: {
stroke: '#1890FF',
lineWidth: 1,
lineDash: [5, 5]
},
tolerance: 5, // Snap tolerance in pixels
autoSnap: true, // Auto snap when close
itemAlignType: true // Align to item centers
};
// Timebar Plugin
const timebarPlugin = {
key: 'timebar',
type: 'timebar',
position: 'bottom',
height: 50,
range: [0, 100], // Time range
trend: {
data: timeData, // Time series data
smooth: true,
isArea: false
},
controllerStyle: {
fill: '#1890FF',
fillOpacity: 0.2
},
onTimeChange: (time) => {
console.log('Time changed:', time);
// Update graph based on time
}
};import { BasePlugin } from '@antv/g6';
import type { PluginOptions } from '@antv/g6';
class CustomPlugin extends BasePlugin {
static defaultOptions: Partial<PluginOptions> = {
position: 'top-right',
size: [100, 50],
className: 'custom-plugin'
};
private container: HTMLDivElement;
constructor(options: PluginOptions) {
super(options);
}
onEnable(): void {
// Create plugin UI
this.container = document.createElement('div');
this.container.className = this.options.className;
this.container.style.position = 'absolute';
this.container.innerHTML = '<button>Custom Action</button>';
// Position the plugin
this.positionContainer();
// Add to graph container
const graphContainer = this.context.graph.getCanvas().getContainer();
graphContainer.appendChild(this.container);
// Bind events
this.bindEvents();
}
onDisable(): void {
// Remove UI and cleanup
if (this.container && this.container.parentNode) {
this.container.parentNode.removeChild(this.container);
}
this.unbindEvents();
}
onResize(): void {
// Handle container resize
this.positionContainer();
}
private positionContainer(): void {
const [width, height] = this.context.graph.getSize();
const [pluginWidth, pluginHeight] = this.options.size;
// Position based on options.position
switch (this.options.position) {
case 'top-right':
this.container.style.top = '10px';
this.container.style.right = '10px';
break;
// Add other positioning logic
}
}
private bindEvents(): void {
const button = this.container.querySelector('button');
button?.addEventListener('click', this.handleButtonClick);
// Listen to graph events
this.context.graph.on('node:click', this.handleNodeClick);
}
private unbindEvents(): void {
this.context.graph.off('node:click', this.handleNodeClick);
}
private handleButtonClick = (): void => {
console.log('Custom plugin button clicked');
// Custom logic here
};
private handleNodeClick = (event: any): void => {
console.log('Node clicked in custom plugin:', event.target.id);
};
}
// Register custom plugin
import { register } from '@antv/g6';
register('plugin', 'custom-plugin', CustomPlugin);
// Use custom plugin
const graph = new Graph({
plugins: [
{
key: 'my-custom-plugin',
type: 'custom-plugin',
position: 'top-left',
size: [120, 60]
}
]
});abstract class BasePlugin {
protected options: PluginOptions;
protected context: { graph: Graph };
// Lifecycle methods to override
abstract onEnable(): void; // Plugin enabled
abstract onDisable(): void; // Plugin disabled
onResize?(): void; // Container resized
onDestroy?(): void; // Plugin destroyed
// Utility methods available
protected getGraphSize(): [number, number];
protected getContainer(): HTMLElement;
protected emit(event: string, data?: any): void;
}// Plugin-to-plugin communication via graph events
class PublisherPlugin extends BasePlugin {
onEnable(): void {
// Emit custom events
this.context.graph.emit('publisher:action', { data: 'hello' });
}
}
class SubscriberPlugin extends BasePlugin {
onEnable(): void {
// Listen to custom events
this.context.graph.on('publisher:action', this.handlePublisherAction);
}
private handlePublisherAction = (event: any): void => {
console.log('Received from publisher:', event.data);
};
}// Plugins can listen to these graph events
interface PluginEvents {
// Graph lifecycle
'render': () => void;
'destroy': () => void;
'resize': (size: [number, number]) => void;
// Data changes
'data:change': (event: { type: string, data: any }) => void;
'node:add': (event: { nodes: NodeData[] }) => void;
'edge:add': (event: { edges: EdgeData[] }) => void;
// User interactions
'node:click': (event: IElementEvent) => void;
'canvas:click': (event: IPointerEvent) => void;
'viewport:transform': (event: IViewportEvent) => void;
// Custom events
[key: string]: (...args: any[]) => void;
}interface PluginOptions {
key?: string; // Unique plugin key
type: string; // Plugin type
[key: string]: any; // Additional options
}
interface UpdatePluginOption {
key: string; // Plugin key to update
[key: string]: any; // Options to update
}
type Placement =
| 'top' | 'top-left' | 'top-right'
| 'bottom' | 'bottom-left' | 'bottom-right'
| 'left' | 'left-top' | 'left-bottom'
| 'right' | 'right-top' | 'right-bottom'
| 'center';
abstract class BasePlugin {
protected options: PluginOptions;
protected context: { graph: Graph };
abstract onEnable(): void;
abstract onDisable(): void;
onResize?(): void;
onDestroy?(): void;
}
interface MinimapPlugin extends BasePlugin {
show(): void;
hide(): void;
refresh(): void;
}
interface HistoryPlugin extends BasePlugin {
undo(): void;
redo(): void;
clear(): void;
canUndo(): boolean;
canRedo(): boolean;
}