Interaction behavior system allowing registration of custom behaviors and event handling for graph interactions. Behaviors provide reusable interaction patterns that can be combined and configured to create complex user experiences.
Register custom behaviors that can be applied to graphs for interaction handling.
/**
* Register custom behavior for graph interactions
* @param type - Behavior type name
* @param behavior - Behavior implementation object
*/
function registerBehavior(type: string, behavior: BehaviorOption): void;Usage Example:
import { registerBehavior } from "@antv/g6-core";
// Register custom hover behavior
registerBehavior('custom-hover', {
getEvents() {
return {
'node:mouseenter': 'onNodeMouseEnter',
'node:mouseleave': 'onNodeMouseLeave',
'edge:mouseenter': 'onEdgeMouseEnter',
'edge:mouseleave': 'onEdgeMouseLeave'
};
},
onNodeMouseEnter(e) {
const item = e.item;
this.graph.setItemState(item, 'hover', true);
},
onNodeMouseLeave(e) {
const item = e.item;
this.graph.setItemState(item, 'hover', false);
},
onEdgeMouseEnter(e) {
const item = e.item;
this.graph.setItemState(item, 'hover', true);
},
onEdgeMouseLeave(e) {
const item = e.item;
this.graph.setItemState(item, 'hover', false);
}
});
// Use behavior in graph modes
const graph = new MyGraph({
modes: {
default: ['custom-hover', 'drag-canvas', 'zoom-canvas']
}
});Complete interface for implementing custom behaviors with lifecycle hooks and event management.
/**
* Behavior implementation interface
*/
interface BehaviorOption {
/**
* Define event to method mappings (required)
* @returns Object mapping G6 events to method names
*/
getEvents(): { [key in G6Event]?: string };
/**
* Get default configuration for behavior (optional)
* @returns Default configuration object
*/
getDefaultCfg?(): object;
/**
* Determine if behavior should begin (optional)
* @param e - Graph event object
* @returns True if behavior should start
*/
shouldBegin?(e?: IG6GraphEvent): boolean;
/**
* Determine if behavior should update during execution (optional)
* @param e - Graph event object
* @returns True if behavior should continue updating
*/
shouldUpdate?(e?: IG6GraphEvent): boolean;
/**
* Determine if behavior should end (optional)
* @param e - Graph event object
* @returns True if behavior should end
*/
shouldEnd?(e?: IG6GraphEvent): boolean;
/**
* Bind behavior to graph (optional)
* @param graph - Graph instance
*/
bind?(graph: IAbstractGraph): void;
/**
* Unbind behavior from graph (optional)
* @param graph - Graph instance
*/
unbind?(graph: IAbstractGraph): void;
// Custom event handler methods
[key: string]: any;
}Comprehensive enumeration of all available G6 events for behavior event mapping.
/**
* Complete enumeration of G6 events
*/
enum G6Event {
// Mouse events
CLICK = 'click',
DBLCLICK = 'dblclick',
MOUSEDOWN = 'mousedown',
MOUSEUP = 'mouseup',
MOUSEMOVE = 'mousemove',
MOUSEENTER = 'mouseenter',
MOUSELEAVE = 'mouseleave',
MOUSEOVER = 'mouseover',
MOUSEOUT = 'mouseout',
CONTEXTMENU = 'contextmenu',
// Touch events
TOUCHSTART = 'touchstart',
TOUCHMOVE = 'touchmove',
TOUCHEND = 'touchend',
// Drag events
DRAGSTART = 'dragstart',
DRAG = 'drag',
DRAGEND = 'dragend',
DRAGENTER = 'dragenter',
DRAGLEAVE = 'dragleave',
DRAGOVER = 'dragover',
DROP = 'drop',
// Node events
NODE_CLICK = 'node:click',
NODE_DBLCLICK = 'node:dblclick',
NODE_MOUSEDOWN = 'node:mousedown',
NODE_MOUSEUP = 'node:mouseup',
NODE_MOUSEMOVE = 'node:mousemove',
NODE_MOUSEENTER = 'node:mouseenter',
NODE_MOUSELEAVE = 'node:mouseleave',
NODE_MOUSEOVER = 'node:mouseover',
NODE_MOUSEOUT = 'node:mouseout',
NODE_CONTEXTMENU = 'node:contextmenu',
NODE_DRAGSTART = 'node:dragstart',
NODE_DRAG = 'node:drag',
NODE_DRAGEND = 'node:dragend',
NODE_DRAGENTER = 'node:dragenter',
NODE_DRAGLEAVE = 'node:dragleave',
NODE_DRAGOVER = 'node:dragover',
NODE_DROP = 'node:drop',
// Edge events
EDGE_CLICK = 'edge:click',
EDGE_DBLCLICK = 'edge:dblclick',
EDGE_MOUSEDOWN = 'edge:mousedown',
EDGE_MOUSEUP = 'edge:mouseup',
EDGE_MOUSEMOVE = 'edge:mousemove',
EDGE_MOUSEENTER = 'edge:mouseenter',
EDGE_MOUSELEAVE = 'edge:mouseleave',
EDGE_MOUSEOVER = 'edge:mouseover',
EDGE_MOUSEOUT = 'edge:mouseout',
EDGE_CONTEXTMENU = 'edge:contextmenu',
// Combo events
COMBO_CLICK = 'combo:click',
COMBO_DBLCLICK = 'combo:dblclick',
COMBO_MOUSEDOWN = 'combo:mousedown',
COMBO_MOUSEUP = 'combo:mouseup',
COMBO_MOUSEMOVE = 'combo:mousemove',
COMBO_MOUSEENTER = 'combo:mouseenter',
COMBO_MOUSELEAVE = 'combo:mouseleave',
COMBO_MOUSEOVER = 'combo:mouseover',
COMBO_MOUSEOUT = 'combo:mouseout',
COMBO_CONTEXTMENU = 'combo:contextmenu',
COMBO_DRAGSTART = 'combo:dragstart',
COMBO_DRAG = 'combo:drag',
COMBO_DRAGEND = 'combo:dragend',
COMBO_DRAGENTER = 'combo:dragenter',
COMBO_DRAGLEAVE = 'combo:dragleave',
COMBO_DRAGOVER = 'combo:dragover',
COMBO_DROP = 'combo:drop',
// Canvas events
CANVAS_CLICK = 'canvas:click',
CANVAS_DBLCLICK = 'canvas:dblclick',
CANVAS_MOUSEDOWN = 'canvas:mousedown',
CANVAS_MOUSEUP = 'canvas:mouseup',
CANVAS_MOUSEMOVE = 'canvas:mousemove',
CANVAS_MOUSEENTER = 'canvas:mouseenter',
CANVAS_MOUSELEAVE = 'canvas:mouseleave',
CANVAS_CONTEXTMENU = 'canvas:contextmenu',
CANVAS_DRAGSTART = 'canvas:dragstart',
CANVAS_DRAG = 'canvas:drag',
CANVAS_DRAGEND = 'canvas:dragend',
// Timing events
BEFORERENDER = 'beforerender',
AFTERRENDER = 'afterrender',
BEFOREPAINT = 'beforepaint',
AFTERPAINT = 'afterpaint',
BEFOREADDITEM = 'beforeadditem',
AFTERADDITEM = 'afteradditem',
BEFOREREMOVEITEM = 'beforeremoveitem',
AFTERREMOVEITEM = 'afterremoveitem',
BEFOREUPDATEITEM = 'beforeupdateitem',
AFTERUPDATEITEM = 'afterupdateitem',
BEFOREITEMVISIBILITYCHANGE = 'beforeitemvisibilitychange',
AFTERITEMVISIBILITYCHANGE = 'afteritemvisibilitychange',
BEFOREITEMSTATECHANGE = 'beforeitemstatechange',
AFTERITEMSTATECHANGE = 'afteritemstatechange',
BEFOREITEMREFRESH = 'beforeitemrefresh',
AFTERITEMREFRESH = 'afteritemrefresh',
BEFORELAYOUT = 'beforelayout',
AFTERLAYOUT = 'afterlayout',
// Viewport events
VIEWPORTCHANGE = 'viewportchange',
BEFORECREATEEDGE = 'beforecreateedge',
AFTERCREATEEDGE = 'aftercreateedge',
// Keyboard events
KEYDOWN = 'keydown',
KEYUP = 'keyup',
// Wheel event
WHEEL = 'wheel'
}Graph event object structure containing interaction details and target information.
/**
* G6 graph event object interface
*/
interface IG6GraphEvent {
item: Item | null; // Target item (node, edge, combo)
canvasX: number; // Canvas X coordinate
canvasY: number; // Canvas Y coordinate
clientX: number; // Client X coordinate
clientY: number; // Client Y coordinate
x: number; // Graph X coordinate
y: number; // Graph Y coordinate
wheelDelta: number; // Mouse wheel delta
detail: number; // Event detail
key?: string; // Keyboard key
target: IShapeBase; // Target shape element
originalEvent?: Event; // Original DOM event
bubbles?: boolean; // Whether event bubbles
defaultPrevented?: boolean; // Whether default prevented
name: string; // Event name
timeStamp: number; // Event timestamp
type: string; // Event type
}Extended event class for G6-specific event handling.
/**
* G6 graph event class extending base GraphEvent
*/
class G6GraphEvent {
public item: Item;
public canvasX: number;
public canvasY: number;
public clientX: number;
public clientY: number;
public wheelDelta: number;
public detail: number;
public target: IShapeBase;
/**
* Create G6 graph event
* @param type - Event type
* @param event - Source event object
*/
constructor(type: string, event: IG6GraphEvent);
}Complete examples of common behavior patterns:
// Drag node behavior
registerBehavior('drag-node', {
getDefaultCfg() {
return {
updateEdge: true,
delegate: true,
delegateStyle: {}
};
},
getEvents() {
return {
'node:dragstart': 'onDragStart',
'node:drag': 'onDrag',
'node:dragend': 'onDragEnd'
};
},
shouldBegin(e) {
// Only allow dragging if node is not locked
return !e.item.hasLocked();
},
onDragStart(e) {
this.origin = { x: e.x, y: e.y };
this.item = e.item;
if (this.delegate) {
// Create drag delegate
this.createDelegate(e);
}
},
onDrag(e) {
if (!this.origin) return;
const dx = e.x - this.origin.x;
const dy = e.y - this.origin.y;
if (this.delegate) {
this.updateDelegate(e, dx, dy);
} else {
this.updateItem(e, dx, dy);
}
},
onDragEnd(e) {
if (!this.origin) return;
const dx = e.x - this.origin.x;
const dy = e.y - this.origin.y;
if (this.delegate) {
this.removeDelegate();
}
this.updateItem(e, dx, dy);
this.item = null;
this.origin = null;
},
updateItem(e, dx, dy) {
const model = this.item.getModel();
this.graph.updateItem(this.item, {
x: model.x + dx,
y: model.y + dy
});
}
});
// Selection behavior
registerBehavior('click-select', {
getDefaultCfg() {
return {
multiple: true,
trigger: 'shift'
};
},
getEvents() {
return {
'node:click': 'onItemClick',
'edge:click': 'onItemClick',
'canvas:click': 'onCanvasClick'
};
},
onItemClick(e) {
const item = e.item;
const selected = item.hasState('selected');
if (this.multiple && e.originalEvent[this.trigger + 'Key']) {
// Multi-select with modifier key
this.graph.setItemState(item, 'selected', !selected);
} else {
// Single select - clear others first
this.clearSelected();
this.graph.setItemState(item, 'selected', true);
}
},
onCanvasClick(e) {
this.clearSelected();
},
clearSelected() {
const selectedNodes = this.graph.findAllByState('node', 'selected');
const selectedEdges = this.graph.findAllByState('edge', 'selected');
[...selectedNodes, ...selectedEdges].forEach(item => {
this.graph.setItemState(item, 'selected', false);
});
}
});
// Zoom canvas behavior
registerBehavior('zoom-canvas', {
getDefaultCfg() {
return {
sensitivity: 2,
minZoom: 0.1,
maxZoom: 10
};
},
getEvents() {
return {
wheel: 'onWheel'
};
},
onWheel(e) {
e.originalEvent.preventDefault();
const graph = this.graph;
const zoom = graph.getZoom();
const ratio = zoom - (e.wheelDelta * this.sensitivity) / 100;
if (ratio > this.maxZoom || ratio < this.minZoom) {
return;
}
graph.zoomTo(ratio, { x: e.clientX, y: e.clientY });
}
});Check and retrieve registered behaviors.
/**
* Static behavior management class
*/
class Behavior {
/**
* Check if behavior type is registered
* @param type - Behavior type name
* @returns True if behavior exists
*/
static hasBehavior(type: string): boolean;
/**
* Get registered behavior implementation
* @param type - Behavior type name
* @returns Behavior implementation object
*/
static getBehavior(type: string): any;
}Usage Examples:
import { Behavior } from "@antv/g6-core";
// Check if behavior exists before using
if (Behavior.hasBehavior('drag-node')) {
// Use the behavior
graph.addBehaviors(['drag-node'], 'default');
}
// Get behavior implementation for inspection
const dragBehavior = Behavior.getBehavior('drag-node');interface IBehavior {
getEvents(): { [key in G6Event]?: string };
getDefaultCfg?(): object;
shouldBegin?(e?: IG6GraphEvent): boolean;
shouldUpdate?(e?: IG6GraphEvent): boolean;
shouldEnd?(e?: IG6GraphEvent): boolean;
bind?(graph: IAbstractGraph): void;
unbind?(graph: IAbstractGraph): void;
}
interface ModeOption {
type: string;
[key: string]: any;
}
type ModeType = string | ModeOption;
interface Modes {
[modeName: string]: ModeType[];
}
type Item = INode | IEdge | ICombo;
interface IAbstractGraph {
// Graph interface methods for behavior access
setItemState(item: Item | string, state: string, value: string | boolean): void;
addBehaviors(behaviors: string | ModeOption | ModeType[], modes: string | string[]): IAbstractGraph;
removeBehaviors(behaviors: string | ModeOption | ModeType[], modes: string | string[]): IAbstractGraph;
updateItem(item: Item | string, cfg: any, stack?: boolean): void;
findAllByState<T extends Item>(type: string, state: string): T[];
getZoom(): number;
zoomTo(ratio: number, center?: { x: number; y: number }): void;
// ... other graph methods
}