12 KiB
title | order |
---|---|
Custom Edge | 3 |
G6 provides abundant Built-in Edges. Besides, the custom machanism allows the users to design their own type of edges. An edge with complex graphics shapes, complex interactions, fantastic animations can be implemented easily.
You are able to custom an edge type by G6.registerEdge(typeName: string, edgeDefinition: object, extendedEdgeType?: string)
, where:
typeName
: the name of the new edge type;extendedEdgeType
: The name of the existing type that will be extended, which can be a built-in edge type, or an existing custom edge type. When it is not assigned, the custom edge will not extend any existing edge type;edgeDefinition
: The definition of the new edge type. The required options can be found at Custom Mechanism API. When theextendedEdgeType
is assigned, the functions which are not rewritten will extend from the type with nameextendedEdgeType
.
Noted that if the extendedEdgeType
is assigned, the required functions such as draw
, update
, and setState
will extend from extendedEdgeType
unless they are rewritten in edgeDefinition
. Due to this mechanism, a question is often fed back:
- Q: when the custom edge/node is updated, the re-draw logic is not the same as
draw
ordrawShape
function defined inedgeDefinition
. e.g., some shapes are not updated as expected, and some text shapes show up. - A: Since the
extendedEdgeType
is assigned, and theupdate
is not implemented inextendedEdgeType
, theupdate
of the extended edge type will be called when updating the edge/node, whose logic might be different from thedraw
ordrawShape
defined by yourself. To avoid this problem, you can override theupdate
byundefined
inedgeDefinition
. Whenupdate
isundefined
, thedraw
ordrawShape
will be called when updating the edge/node.
In this document, we will introduce the custom edge by four examples:
1. Register a brand new edge;
2. Register an edge by extending a built-in edge;
2. Register an edge with an extra shape;
4. Register an edge with interactions and styles;
5. Register an edge with custom arrow.
1. Register a Brand New Edge
Now, we are goint to register a perpendicular polyline:
(Left) Straight line edge. (Center) A custom polyline edge. (Right) The result after modifying the link points of the end nodes.
Register a Custom Edge
G6.registerEdge('hvh', {
draw(cfg, group) {
const startPoint = cfg.startPoint;
const endPoint = cfg.endPoint;
const shape = group.addShape('path', {
attrs: {
stroke: '#333',
path: [
['M', startPoint.x, startPoint.y],
['L', endPoint.x / 3 + (2 / 3) * startPoint.x, startPoint.y], // 1/3
['L', endPoint.x / 3 + (2 / 3) * startPoint.x, endPoint.y], // 2/3
['L', endPoint.x, endPoint.y],
],
},
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
name: 'path-shape',
});
return shape;
},
});
Now, we have registered a custom edge named 'hvh'
whose result is shown in the center of the figure above. The default startPoint
and endPoint
in the custom edge are the intersection of the edge and the end nodes.
To achieve the result shown in the right of the figure, we modify the anchorPoints (link points) of the end nodes to change the positions of startPoint
and endPoint
.
Modify the anchorPoints in Data
Now, we modify anchorPoints
in the node data, and then assign shape
to 'hvh'
in edge data as shown below.
const data = {
nodes: [
{
id: 'node1',
x: 100,
y: 200,
anchorPoints: [
[0, 0.5],
[1, 0.5],
],
},
{
id: 'node2',
x: 200,
y: 100,
anchorPoints: [
[0, 0.5],
[1, 0.5],
],
},
{
id: 'node3',
x: 200,
y: 300,
anchorPoints: [
[0, 0.5],
[1, 0.5],
],
},
],
edges: [
{
id: 'edge1',
target: 'node2',
source: 'node1',
type: 'hvh',
},
{
id: 'edge2',
target: 'node3',
source: 'node1',
type: 'hvh',
},
],
};
2. Extend the Built-in Edge
In this section, we add animation to a built-in edge by afterDraw
.
G6.registerEdge(
'line-growth',
{
afterDraw(cfg, group) {
const shape = group.get('children')[0];
const length = shape.getTotalLength();
shape.animate(
(ratio) => {
const startLen = ratio * length;
const cfg = {
lineDash: [startLen, length - startLen],
};
return cfg;
},
{
repeat: true,
duration: 2000,
},
);
},
},
'cubic',
);
3. Adding an Extra Shape
Adding an extra shape on an arbitrary position on the path of the edge can be implemented by afterDraw
. shape.getPoint(ratio)
helps us to find the coordiante of an arbitrary point on the path
.
G6.registerEdge(
'mid-point-edge',
{
afterDraw(cfg, group) {
// get the first shape in the graphics group of this edge, it is the path of the edge here
const shape = group.get('children')[0];
// get the coordinate of the mid point on the path
const midPoint = shape.getPoint(0.5);
// add a rect on the mid point of the path. note that the origin of a rect shape is on its lefttop
group.addShape('rect', {
attrs: {
width: 10,
height: 10,
fill: '#f00',
// x and y should be minus width / 2 and height / 2 respectively to translate the center of the rect to the midPoint
x: midPoint.x - 5,
y: midPoint.y - 5,
},
name: 'mid-point-edge-rect', // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
});
},
update: undefined,
},
'cubic',
);
4. Custom Edge with Interaction Styles
In this section, we implement a type of edge with the interaction styles below:
- Widen the edge by clicking. Restore it by clicking again;
- Turn to red by mouse hovering. Restore it by mouse leaving.
The result:
⚠️Attention: when the edge is too thin to be hitted by mouse, set `lineAppendWidth` to enlarge the hitting area.
// Extend a new type of edge by extending line edge
G6.registerEdge(
'custom-edge',
{
// Response the states change
setState(name, value, item) {
const group = item.getContainer();
const shape = group.get('children')[0]; // The order is determined by the ordering of been draw
if (name === 'active') {
if (value) {
shape.attr('stroke', 'red');
} else {
shape.attr('stroke', '#333');
}
}
if (name === 'selected') {
if (value) {
shape.attr('lineWidth', 3);
} else {
shape.attr('lineWidth', 2);
}
}
},
},
'line',
);
// Select by clicking, cancel by clicking again
graph.on('edge:click', (ev) => {
const edge = ev.item;
graph.setItemState(edge, 'selected', !edge.hasState('selected')); // Switch the 'selected' state
});
graph.on('edge:mouseenter', (ev) => {
const edge = ev.item;
graph.setItemState(edge, 'active', true);
});
graph.on('edge:mouseleave', (ev) => {
const edge = ev.item;
graph.setItemState(edge, 'active', false);
});
5. Custom Arrow
G6 (v3.5.8 and later versions) provides default arrow and built-in arrows for edges. The default end arrows might not meet the requirements sometimes. You can register an edge with a custom arrow by the custom mechanism of G6.
(Left) Built-in arrow of G6. (Right) A custom edge with custom arrow.
⚠️ Attention: The coordinate system of custom arrow is changed by G6 3.4.1. In the figure below, the left one is the demonstration of the custom arrow before v3.4.1, and the right one illustates v3.4.1 and its later versions. The pointed direction is changed from negative direction to positive direction of x-axis. In the same time, the dirrection of the offset d
is changed. In both versions, the origin of the self coordinate system of the custom arrow is on the end point of the corresponding edge or path, and the slope of the arrow is the same as the slope of edge or path at the end point.
(Left) Illustration for the coordinate system of custom arrow before v3.4.1. (Right) Illustration for v3.4.1 and its later versions.
There are three ways to configure a custom arrow to an edge in G6:
- Configuring on the graph to global edges;
- Configuring in data for single edge;
- Configuring in a custom edge type.
1. Global Configuration
const graph = new Graph({
// ... Other configurations for graph
defaultEdge: {
style: {
endArrow: {
// The custom arrow is a path points at (0, 0), and its tail points to the positive direction of x-axis
path: 'M 0,0 L 20,10 L 20,-10 Z',
// the offset of the arrow, nagtive value means the arrow is moved alone the positive direction of x-axis
// d: -10
// styles are supported after v3.4.1
fill: '#333',
stroke: '#666',
opacity: 0.8,
// ...
},
},
},
});
2. Configuring in Data
const data = {
nodes: [
{ id: 'node1' },
{ id: 'node2' },
// ... other nodes
],
edges: [
{
source: 'node1',
target: 'node2',
style: {
endArrow: {
// The custom arrow is a path points at (0, 0), and its tail points to the positive direction of x-axis
path: 'M 0,0 L 20,10 L 20,-10 Z',
// the offset of the arrow, nagtive value means the arrow is moved alone the positive direction of x-axis
// d: -10
// styles are supported after v3.4.1
fill: '#333',
stroke: '#666',
opacity: 0.8,
// ...
},
},
},
//... other edges
],
};
3. Configuring in a Custom Edge
G6.registerEdge('line-arrow', {
draw(cfg, group) {
const { startPoint, endPoint } = cfg;
const keyShape = group.addShape('path', {
attrs: {
path: [
['M', startPoint.x, startPoint.y],
['L', endPoint.x, endPoint.y],
],
stroke: 'steelblue',
lineWidth: 3,
startArrow: {
// The custom arrow is a path points at (0, 0), and its tail points to the positive direction of x-axis
path: 'M 0,0 L 20,10 L 20,-10 Z',
// the offset of the arrow, nagtive value means the arrow is moved alone the positive direction of x-axis
// d: -10
},
endArrow: {
// The custom arrow is a path points at (0, 0), and its tail points to the positive direction of x-axis
path: 'M 0,0 L 20,10 L 20,-10 Z',
// the offset of the arrow, nagtive value means the arrow is moved alone the positive direction of x-axis
// d: -10
},
},
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
name: 'path-shape',
});
return keyShape;
},
});