DragElementForce
Overview
DragElementForce is a built-in behavior in G6 for implementing node dragging under d3-force and d3-force-3d layouts. During dragging, the layout is recalculated in real-time, allowing the graph layout to dynamically adjust to accommodate the new position of the nodes.
Basic Usage
Add this behavior in the graph configuration:
1. Quick Configuration (Static)
Declare directly using a string form. This method is simple but only supports default configuration and cannot be dynamically modified after configuration:
const graph = new Graph({
// Other configurations...
behaviors: ['drag-element-force'],
});
2. Object Configuration (Recommended)
Configure using an object form, supporting custom parameters, and can dynamically update the configuration at runtime:
const graph = new Graph({
// Other configurations...
behaviors: [
{
type: 'drag-element-force',
key: 'drag-element-force-1',
fixed: true, // Fix node position after dragging
},
],
});
Configuration Options
| Option | Description | Type | Default | Required |
|---|---|---|---|---|
| type | Behavior type name, set type: 'drag-element-force' to enable this behavior |
string | drag-element-force |
✓ |
| key | Unique identifier for the behavior, used for subsequent operations | string | - | |
| fixed | Whether to keep the node position fixed after dragging ends, boolean values represent: - true: After dragging ends, the node’s position will remain fixed and not be affected by the layout algorithm - false: After dragging ends, the node’s position will continue to be affected by the layout algorithm |
boolean | false | |
| enable | Whether to enable the drag function, by default nodes and combos can be dragged | boolean | ((event: IElementDragEvent) => boolean) | ['node', 'combo'].includes(event.targetType) |
|
| state | Identifier for the selected state of nodes, when multi-selection is enabled, it will find the selected nodes based on this state | string | selected |
|
| hideEdge | Controls the display state of edges during dragging, optional values are: - none: Do not hide any edges - out: Hide edges with the current node as the source node - in: Hide edges with the current node as the target node - both: Hide all edges related to the current node - all: Hide all edges in the graph ⚠️ Note: When shadow (ghost node) is enabled, the hideEdge configuration will not take effect. |
none | all | in | out | both |
none |
|
| cursor | Customize the mouse style during dragging, example | { default?: Cursor; grab: Cursor; grabbing: Cursor } | - | |
| trigger | Press this shortcut key in combination with mouse perform drag element Key reference: MDN Key Values. If set to an empty array, it means drag element can be performed with mouse without pressing other keys ⚠️ Note, setting trigger to ['drag'] will cause the drag-canvas behavior to fail. The two cannot be configured simultaneously. |
string[] | (Control | Shift| Alt | ......)[] |
[shift] |
cursor
cursor is used to customize the mouse pointer style during dragging:
default: Pointer style in default stategrab: Pointer style when hovering over a draggable elementgrabbing: Pointer style when dragging
Optional values are: auto | default | none | context-menu | help | pointer | progress | wait | cell | crosshair | text | vertical-text | alias | copy | move | no-drop | not-allowed | grab | grabbing | all-scroll | col-resize | row-resize | n-resize | e-resize | s-resize | w-resize | ne-resize | nw-resize | se-resize | sw-resize | ew-resize | ns-resize | nesw-resize | nwse-resize | zoom-in | zoom-out
Example configuration:
cursor: {
default: 'default', // Use normal pointer by default
grab: 'grab', // Show grab pointer when draggable
grabbing: 'grabbing' // Show grabbing pointer when dragging
}
FAQ
1. What is the difference between DragElementForce and DragElement?
DragElementForceis specifically used ford3-forceord3-force-3dlayouts, and recalculates the layout in real-time during draggingDragElementis a general drag interaction and does not trigger layout recalculation
Practical Example
Mesh Effect
import { addPanel } from '/demo-runtime.ts';
import { Graph } from '@antv/g6';
function getData(size = 10) {
const nodes = Array.from({ length: size * size }, (_, i) => ({ id: `${i}` }));
const edges = [];
for (let y = 0; y < size; ++y) {
for (let x = 0; x < size; ++x) {
if (y > 0) edges.push({ source: `${(y - 1) * size + x}`, target: `${y * size + x}` });
if (x > 0) edges.push({ source: `${y * size + (x - 1)}`, target: `${y * size + x}` });
}
}
return { nodes, edges };
}
const graph = new Graph({
data: getData(),
layout: {
type: 'd3-force',
manyBody: {
strength: -30,
},
link: {
strength: 1,
distance: 20,
iterations: 10,
},
},
node: {
style: {
size: 10,
fill: '#000',
},
},
edge: {
style: {
stroke: '#000',
},
},
behaviors: [{ type: 'drag-element-force' }, 'zoom-canvas'],
});
graph.render();
addPanel((gui) => {
gui.add({ msg: 'Try to drag nodes' }, 'msg').name('Tips').disable();
});
import { Graph } from '@antv/g6';
import GUI from 'lil-gui';
export function addPanel(renderPanel: (gui: GUI) => void) {
const gui = new GUI({ container: document.body });
gui.title('Control');
Object.assign(gui.domElement.style, { position: 'absolute', top: '0', right: '0', zIndex: '10' });
renderPanel(gui);
addEventListener('pagehide', () => gui.destroy(), { once: true });
}
export async function createGraph(
options: ConstructorParameters<typeof Graph>[0],
size: { width?: number; height?: number } = {},
renderPanel?: (gui: GUI, graph: Graph) => void,
) {
const container = document.createElement('div');
Object.assign(container.style, {
width: '100%',
maxWidth: `${size.width || 600}px`,
height: `${size.height || 400}px`,
});
document.getElementById('container')!.append(container);
const graph = new Graph({ ...size, ...options, width: container.clientWidth, container, autoResize: true });
addEventListener('pagehide', () => graph.destroy(), { once: true });
await graph.render();
if (renderPanel) addPanel((gui) => renderPanel(gui, graph));
return container;
}Fix Dragged Nodes
import { Graph } from '@antv/g6';
const data = {
nodes: new Array(10).fill(0).map((_, i) => ({ id: `${i}`, label: `${i}` })),
edges: [
{ source: '0', target: '1' },
{ source: '0', target: '2' },
{ source: '0', target: '3' },
{ source: '0', target: '4' },
{ source: '0', target: '5' },
{ source: '0', target: '7' },
{ source: '0', target: '8' },
{ source: '0', target: '9' },
{ source: '2', target: '3' },
{ source: '4', target: '5' },
{ source: '4', target: '6' },
{ source: '5', target: '6' },
],
};
const graph = new Graph({
container: 'container',
data,
node: {
style: {
labelText: (d) => d.label,
labelPlacement: 'middle',
labelFill: '#fff',
},
},
layout: {
type: 'd3-force',
link: {
distance: 100,
strength: 2,
},
collide: {
radius: 40,
},
},
behaviors: [
{
type: 'drag-element-force',
fixed: true,
},
],
});
graph.render();
import { Graph } from '@antv/g6';
import GUI from 'lil-gui';
export function addPanel(renderPanel: (gui: GUI) => void) {
const gui = new GUI({ container: document.body });
gui.title('Control');
Object.assign(gui.domElement.style, { position: 'absolute', top: '0', right: '0', zIndex: '10' });
renderPanel(gui);
addEventListener('pagehide', () => gui.destroy(), { once: true });
}
export async function createGraph(
options: ConstructorParameters<typeof Graph>[0],
size: { width?: number; height?: number } = {},
renderPanel?: (gui: GUI, graph: Graph) => void,
) {
const container = document.createElement('div');
Object.assign(container.style, {
width: '100%',
maxWidth: `${size.width || 600}px`,
height: `${size.height || 400}px`,
});
document.getElementById('container')!.append(container);
const graph = new Graph({ ...size, ...options, width: container.clientWidth, container, autoResize: true });
addEventListener('pagehide', () => graph.destroy(), { once: true });
await graph.render();
if (renderPanel) addPanel((gui) => renderPanel(gui, graph));
return container;
}Shadow Style
shadow{BaseStyleProps}
An expression like icon{TextStyleProps} indicates that properties of the TextStyleProps type are prefixed with icon in camelCase format.
TextStyleProps includes the following properties:
- fill
- fontSize
- fontWeight
- …
icon{TextStyleProps} means you need to use the following property names:
- iconFill
- iconFontSize
- iconFontWeight
- …