mirror of
https://gitee.com/antv/g6.git
synced 2024-11-29 18:28:19 +08:00
Merge branch 'v5' into feat/timebar
This commit is contained in:
commit
1f46b96685
@ -2,7 +2,7 @@ import type { ID, IG6GraphEvent } from '../../types';
|
||||
import { Behavior } from '../../types/behavior';
|
||||
|
||||
const KEYBOARD_TRIGGERS = ['shift', 'ctrl', 'alt', 'meta'] as const;
|
||||
const MOUSE_TRIGGERS = ['mouseenter', 'click'] as const;
|
||||
const MOUSE_TRIGGERS = ['pointerenter', 'click'] as const;
|
||||
|
||||
type Trigger = (typeof MOUSE_TRIGGERS)[number];
|
||||
|
||||
@ -78,12 +78,12 @@ export class ActivateRelations extends Behavior {
|
||||
|
||||
getEvents = () => {
|
||||
const { trigger } = this.options;
|
||||
if (trigger === 'mouseenter') {
|
||||
if (trigger === 'pointerenter') {
|
||||
return {
|
||||
'node:mouseenter': this.setAllItemStates,
|
||||
'combo:mouseenter': this.setAllItemStates,
|
||||
'node:mouseleave': this.clearActiveState,
|
||||
'combo:mouseleave': this.clearActiveState,
|
||||
'node:pointerenter': this.setAllItemStates,
|
||||
'combo:pointerenter': this.setAllItemStates,
|
||||
'node:pointerleave': this.clearActiveState,
|
||||
'combo:pointerleave': this.clearActiveState,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -90,6 +90,7 @@ const {
|
||||
|
||||
import lassoSelector from './selector/lasso';
|
||||
import rectSelector from './selector/rect';
|
||||
import Hull from './plugin/hull';
|
||||
|
||||
const stdLib = {
|
||||
transforms: {
|
||||
@ -275,6 +276,7 @@ const Extensions = {
|
||||
Fisheye,
|
||||
Legend,
|
||||
Timebar,
|
||||
Hull
|
||||
};
|
||||
|
||||
export default registery;
|
||||
|
@ -76,15 +76,15 @@ export abstract class BaseEdge {
|
||||
// the tag of first rendering
|
||||
firstRender: boolean;
|
||||
} = {
|
||||
hiddenShape: {},
|
||||
balanceRatio: 1,
|
||||
zoom: 1,
|
||||
zoomLevel: 0,
|
||||
levelShapes: {},
|
||||
wordWrapWidth: 50,
|
||||
animateConfig: DEFAULT_ANIMATE_CFG.zoom,
|
||||
firstRender: true,
|
||||
};
|
||||
hiddenShape: {},
|
||||
balanceRatio: 1,
|
||||
zoom: 1,
|
||||
zoomLevel: 0,
|
||||
levelShapes: {},
|
||||
wordWrapWidth: 50,
|
||||
animateConfig: DEFAULT_ANIMATE_CFG.zoom,
|
||||
firstRender: true,
|
||||
};
|
||||
constructor(props) {
|
||||
const { themeStyles, lodStrategy, zoom } = props;
|
||||
if (themeStyles) this.themeStyles = themeStyles;
|
||||
@ -178,14 +178,14 @@ export abstract class BaseEdge {
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw all elements related to the edge.
|
||||
* You should call `drawKeyShape` and `drawAnchorShape`,`drawLabelShape`,`drawIconShape`...as you like.
|
||||
* @param model The displayed model of this edge, only for drawing and not received by users.
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the edge.
|
||||
* @param diffData An object that contains previous and current data.
|
||||
* @param diffState An object that contains previous and current edge's state.
|
||||
* @returns An object containing the keyShape and optional labelShape, iconShape, and some otherShapes properties
|
||||
*/
|
||||
* Draw all elements related to the edge.
|
||||
* You should call `drawKeyShape` and `drawAnchorShape`,`drawLabelShape`,`drawIconShape`...as you like.
|
||||
* @param model The displayed model of this edge, only for drawing and not received by users.
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the edge.
|
||||
* @param diffData An object that contains previous and current data.
|
||||
* @param diffState An object that contains previous and current edge's state.
|
||||
* @returns An object containing the keyShape and optional labelShape, iconShape, and some otherShapes properties
|
||||
*/
|
||||
abstract draw(
|
||||
model: EdgeDisplayModel,
|
||||
sourcePoint: Point,
|
||||
@ -201,12 +201,12 @@ export abstract class BaseEdge {
|
||||
};
|
||||
|
||||
/**
|
||||
* Perform additional drawing operations or add custom shapes after drawing edge.
|
||||
* @param model The displayed model of this edge, only for drawing and not received by users.
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the edge.
|
||||
* @param shapesChanged An array of shape IDs that have changed and need to be updated.
|
||||
* @returns An object that contains some new shapes to be added to the edge.
|
||||
*/
|
||||
* Perform additional drawing operations or add custom shapes after drawing edge.
|
||||
* @param model The displayed model of this edge, only for drawing and not received by users.
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the edge.
|
||||
* @param shapesChanged An array of shape IDs that have changed and need to be updated.
|
||||
* @returns An object that contains some new shapes to be added to the edge.
|
||||
*/
|
||||
public afterDraw(
|
||||
model: EdgeDisplayModel,
|
||||
shapeMap: { [shapeId: string]: DisplayObject },
|
||||
@ -221,7 +221,7 @@ export abstract class BaseEdge {
|
||||
* @param state state name
|
||||
* @param value state value
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the edge.
|
||||
*/
|
||||
*/
|
||||
public setState: (
|
||||
name: string,
|
||||
value: boolean,
|
||||
@ -230,7 +230,7 @@ export abstract class BaseEdge {
|
||||
|
||||
/**
|
||||
* Draw the label shape of the edge
|
||||
* @param model The displayed model of this edge, only for drawing and not received by users.
|
||||
* @param model The displayed model of this edge, only for drawing and not received by users.
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the edge.
|
||||
* @param diffData An object that contains previous and current data.
|
||||
* @param diffState An object that contains previous and current edge's state.
|
||||
@ -344,7 +344,7 @@ export abstract class BaseEdge {
|
||||
|
||||
/**
|
||||
* Draw the label background shape of the edge
|
||||
* @param model The displayed model of this edge, only for drawing and not received by users.
|
||||
* @param model The displayed model of this edge, only for drawing and not received by users.
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the edge.
|
||||
* @param diffData An object that contains previous and current data.
|
||||
* @param diffState An object that contains previous and current edge's state.
|
||||
@ -380,8 +380,9 @@ export abstract class BaseEdge {
|
||||
};
|
||||
if (labelShapeStyle.position === 'start') {
|
||||
if (isRevert) {
|
||||
bgStyle.transformOrigin = `${bgStyle.width - padding[1]} ${bgStyle.height / 2
|
||||
}`;
|
||||
bgStyle.transformOrigin = `${bgStyle.width - padding[1]} ${
|
||||
bgStyle.height / 2
|
||||
}`;
|
||||
} else {
|
||||
bgStyle.transformOrigin = `${padding[3]} ${bgStyle.height / 2}`;
|
||||
}
|
||||
@ -389,12 +390,14 @@ export abstract class BaseEdge {
|
||||
if (isRevert) {
|
||||
bgStyle.transformOrigin = `${padding[3]} ${bgStyle.height / 2}`;
|
||||
} else {
|
||||
bgStyle.transformOrigin = `${bgStyle.width - padding[1]} ${bgStyle.height / 2
|
||||
}`;
|
||||
bgStyle.transformOrigin = `${bgStyle.width - padding[1]} ${
|
||||
bgStyle.height / 2
|
||||
}`;
|
||||
}
|
||||
} else {
|
||||
bgStyle.transformOrigin = `${textWidth / 2 + padding[3]} ${textHeight / 2 + padding[0]
|
||||
}`;
|
||||
bgStyle.transformOrigin = `${textWidth / 2 + padding[3]} ${
|
||||
textHeight / 2 + padding[0]
|
||||
}`;
|
||||
}
|
||||
|
||||
return this.upsertShape(
|
||||
@ -408,7 +411,7 @@ export abstract class BaseEdge {
|
||||
|
||||
/**
|
||||
* Draw the icon shape of the edge
|
||||
* @param model The displayed model of this edge, only for drawing and not received by users.
|
||||
* @param model The displayed model of this edge, only for drawing and not received by users.
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the edge.
|
||||
* @param diffData An object that contains previous and current data.
|
||||
* @param diffState An object that contains previous and current edge's state.
|
||||
@ -472,14 +475,16 @@ export abstract class BaseEdge {
|
||||
if (referTransform) {
|
||||
shapeStyle.transform = referTransform;
|
||||
if (labelAlign === 'right') {
|
||||
shapeStyle.transformOrigin = `${referWidth / 2 - w / 2 + 4 + referHalExtents[0] - offsetX
|
||||
} ${h / 2 - offsetY}`;
|
||||
shapeStyle.transformOrigin = `${
|
||||
referWidth / 2 - w / 2 + 4 + referHalExtents[0] - offsetX
|
||||
} ${h / 2 - offsetY}`;
|
||||
} else if (labelAlign === 'left') {
|
||||
shapeStyle.transformOrigin = `${w + 4 - offsetX} ${h / 2 - offsetY}`;
|
||||
} else {
|
||||
// labelShape align 'center'
|
||||
shapeStyle.transformOrigin = `${(w + referWidth) / 2 - offsetX} ${h / 2 - offsetY
|
||||
}`;
|
||||
shapeStyle.transformOrigin = `${(w + referWidth) / 2 - offsetX} ${
|
||||
h / 2 - offsetY
|
||||
}`;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -500,7 +505,7 @@ export abstract class BaseEdge {
|
||||
|
||||
/**
|
||||
* Draw the halo shape of the edge
|
||||
* @param model The displayed model of this edge, only for drawing and not received by users.
|
||||
* @param model The displayed model of this edge, only for drawing and not received by users.
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the edge.
|
||||
* @param diffData An object that contains previous and current data.
|
||||
* @param diffState An object that contains previous and current edge's state.
|
||||
@ -529,6 +534,15 @@ export abstract class BaseEdge {
|
||||
);
|
||||
}
|
||||
|
||||
public drawOtherShapes(
|
||||
model: EdgeDisplayModel,
|
||||
shapeMap: EdgeShapeMap,
|
||||
diffData?: { previous: EdgeModelData; current: EdgeModelData },
|
||||
diffState?: { previous: State[]; current: State[] },
|
||||
): { [id: string]: DisplayObject } {
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* The listener for graph zooming.
|
||||
* 1. show / hide some shapes while zoom level changed;
|
||||
@ -577,7 +591,7 @@ export abstract class BaseEdge {
|
||||
id,
|
||||
shapeMap[id],
|
||||
this.mergedStyles[id] ||
|
||||
this.mergedStyles[id.replace('Background', '')],
|
||||
this.mergedStyles[id.replace('Background', '')],
|
||||
hiddenShape,
|
||||
animateConfig,
|
||||
),
|
||||
@ -643,7 +657,6 @@ export abstract class BaseEdge {
|
||||
this.nodeMap = nodeMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds or updates an arrow marker on the specified position of an edge.
|
||||
* @param position - The position where the arrow marker should be added or updated. Can be either 'start' or 'end'.
|
||||
@ -702,7 +715,7 @@ export abstract class BaseEdge {
|
||||
* @param type shape's type
|
||||
* @param id unique string to indicates the shape
|
||||
* @param style style to be updated
|
||||
* @param shapeMap the shape map of a edge
|
||||
* @param shapeMap the shape map of a edge
|
||||
* @param model data model of the edge
|
||||
* @returns The display object representing the shape.
|
||||
*/
|
||||
|
@ -35,7 +35,7 @@ export class LineEdge extends BaseEdge {
|
||||
): EdgeShapeMap {
|
||||
const { data = {} } = model;
|
||||
|
||||
const shapes: EdgeShapeMap = { keyShape: undefined };
|
||||
let shapes: EdgeShapeMap = { keyShape: undefined };
|
||||
|
||||
shapes.keyShape = this.drawKeyShape(
|
||||
model,
|
||||
@ -66,7 +66,13 @@ export class LineEdge extends BaseEdge {
|
||||
shapes.iconShape = this.drawIconShape(model, shapeMap, diffData);
|
||||
}
|
||||
|
||||
// TODO: other shapes
|
||||
// otherShapes
|
||||
if (data.otherShapes) {
|
||||
shapes = {
|
||||
...shapes,
|
||||
...this.drawOtherShapes(model, shapeMap, diffData),
|
||||
};
|
||||
}
|
||||
|
||||
return shapes;
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
} from '../../../types/edge';
|
||||
import { State } from '../../../types/item';
|
||||
import { getPolylinePath } from '../../../util/polyline';
|
||||
import { pathFinder } from '../../../util/router';
|
||||
import { RouterCfg, pathFinder } from '../../../util/router';
|
||||
import Node from '../../../item/node';
|
||||
import { LineEdge } from './line';
|
||||
export class PolylineEdge extends LineEdge {
|
||||
@ -74,7 +74,7 @@ export class PolylineEdge extends LineEdge {
|
||||
|
||||
return shapes;
|
||||
}
|
||||
private getControlPoints(
|
||||
public getControlPoints(
|
||||
model: EdgeDisplayModel,
|
||||
sourcePoint: Point,
|
||||
targetPoint: Point,
|
||||
@ -95,16 +95,18 @@ export class PolylineEdge extends LineEdge {
|
||||
model: EdgeDisplayModel,
|
||||
points: Point[],
|
||||
radius: number,
|
||||
routeCfg?: Record<string, any>,
|
||||
routeCfg?: RouterCfg,
|
||||
auto?: boolean,
|
||||
): string {
|
||||
const { id: edgeId, source: sourceNodeId, target: targetNodeId } = model;
|
||||
|
||||
// Draw a polyline with control points
|
||||
if (!auto) return getPolylinePath(edgeId, points, radius);
|
||||
if (!auto && routeCfg.name !== 'er')
|
||||
return getPolylinePath(edgeId, points, radius);
|
||||
|
||||
// Find the shortest path computed by A* routing algorithm
|
||||
const polylinePoints = pathFinder(
|
||||
points,
|
||||
sourceNodeId,
|
||||
targetNodeId,
|
||||
this.nodeMap as unknown as Map<ID, Node>,
|
||||
@ -136,7 +138,7 @@ export class PolylineEdge extends LineEdge {
|
||||
|
||||
const routeCfg = mix(
|
||||
{},
|
||||
{ offset: keyShapeStyle.offset },
|
||||
{ offset: keyShapeStyle.offset, name: 'orth' },
|
||||
keyShapeStyle.routeCfg,
|
||||
);
|
||||
|
||||
|
@ -96,7 +96,6 @@ export abstract class BaseNode {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get merged styles from `getMergedStyles` and assigns the merged styles to the 'mergedStyles' property.
|
||||
* @param model - The NodeDisplayModel or ComboDisplayModel to merge the styles from.
|
||||
@ -199,12 +198,12 @@ export abstract class BaseNode {
|
||||
/**
|
||||
* Draw all elements related to the node.
|
||||
* You should call `drawKeyShape` and `drawAnchorShape`,`drawLabelShape`,`drawIconShape`...as you like.
|
||||
* @param model The displayed model of this node, only for drawing and not received by users.
|
||||
* @param model The displayed model of this node, only for drawing and not received by users.
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the node.
|
||||
* @param diffData An object that contains previous and current data.
|
||||
* @param diffState An object that contains previous and current node's state.
|
||||
* @returns An object containing the keyShape and optional labelShape, iconShape, and some otherShapes properties
|
||||
*/
|
||||
*/
|
||||
abstract draw(
|
||||
model: NodeDisplayModel | ComboDisplayModel,
|
||||
shapeMap: { [shapeId: string]: DisplayObject },
|
||||
@ -222,11 +221,11 @@ export abstract class BaseNode {
|
||||
|
||||
/**
|
||||
* Perform additional drawing operations or add custom shapes after drawing node.
|
||||
* @param model The displayed model of this node, only for drawing and not received by users.
|
||||
* @param model The displayed model of this node, only for drawing and not received by users.
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the node.
|
||||
* @param shapesChanged An array of shape IDs that have changed and need to be updated.
|
||||
* @returns An object that contains some new shapes to be added to the node.
|
||||
*/
|
||||
*/
|
||||
public afterDraw(
|
||||
model: NodeDisplayModel | ComboDisplayModel,
|
||||
shapeMap: { [shapeId: string]: DisplayObject },
|
||||
@ -241,7 +240,7 @@ export abstract class BaseNode {
|
||||
* @param state state name
|
||||
* @param value state value
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the node.
|
||||
*/
|
||||
*/
|
||||
public setState: (
|
||||
name: string,
|
||||
value: boolean,
|
||||
@ -251,7 +250,7 @@ export abstract class BaseNode {
|
||||
/**
|
||||
* The key function of drawing shape.
|
||||
* Draw the key shape of the node based on the provided model and shape map.
|
||||
* @param model The displayed model of this node, only for drawing and not received by users.
|
||||
* @param model The displayed model of this node, only for drawing and not received by users.
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the node.
|
||||
* @param diffData An object that contains previous and current data.
|
||||
* @param diffState An object that contains previous and current node's state.
|
||||
@ -269,7 +268,7 @@ export abstract class BaseNode {
|
||||
|
||||
/**
|
||||
* Draw the label shape of the node
|
||||
* @param model The displayed model of this node, only for drawing and not received by users.
|
||||
* @param model The displayed model of this node, only for drawing and not received by users.
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the node.
|
||||
* @param diffData An object that contains previous and current data.
|
||||
* @param diffState An object that contains previous and current node's state.
|
||||
@ -377,7 +376,7 @@ export abstract class BaseNode {
|
||||
|
||||
/**
|
||||
* Draw the label background shape of the node
|
||||
* @param model The displayed model of this node, only for drawing and not received by users.
|
||||
* @param model The displayed model of this node, only for drawing and not received by users.
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the node.
|
||||
* @param diffData An object that contains previous and current data.
|
||||
* @param diffState An object that contains previous and current node's state.
|
||||
@ -434,7 +433,7 @@ export abstract class BaseNode {
|
||||
|
||||
/**
|
||||
* Draw the icon shape of the node
|
||||
* @param model The displayed model of this node, only for drawing and not received by users.
|
||||
* @param model The displayed model of this node, only for drawing and not received by users.
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the node.
|
||||
* @param diffData An object that contains previous and current data.
|
||||
* @param diffState An object that contains previous and current node's state.
|
||||
@ -485,7 +484,7 @@ export abstract class BaseNode {
|
||||
|
||||
/**
|
||||
* Draw the halo shape of the node
|
||||
* @param model The displayed model of this node, only for drawing and not received by users.
|
||||
* @param model The displayed model of this node, only for drawing and not received by users.
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the node.
|
||||
* @param diffData An object that contains previous and current data.
|
||||
* @param diffState An object that contains previous and current node's state.
|
||||
@ -525,7 +524,7 @@ export abstract class BaseNode {
|
||||
|
||||
/**
|
||||
* Draw the anchors shape of the node
|
||||
* @param model The displayed model of this node, only for drawing and not received by users.
|
||||
* @param model The displayed model of this node, only for drawing and not received by users.
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the node.
|
||||
* @param diffData An object that contains previous and current data.
|
||||
* @param diffState An object that contains previous and current node's state.
|
||||
@ -606,8 +605,8 @@ export abstract class BaseNode {
|
||||
/**
|
||||
* Configures the anchor positions based on the provided keyShapeStyle and returns the configuration.
|
||||
* e.g for a CircleNode, it returns: `{"right":keyShapeStyle.x+keyShapeStyle.r, keyShapeStyle.y}`
|
||||
* @param keyShapeStyle - The keyShapeStyle object that contains the style information of the key shape.
|
||||
* @returns The anchor position configuration as an IAnchorPositionMap object.
|
||||
* @param keyShapeStyle - The keyShapeStyle object that contains the style information of the key shape.
|
||||
* @returns The anchor position configuration as an IAnchorPositionMap object.
|
||||
*/
|
||||
public calculateAnchorPosition(keyShapeStyle: any): IAnchorPositionMap {
|
||||
const x = convertToNumber(keyShapeStyle.x);
|
||||
@ -623,7 +622,7 @@ export abstract class BaseNode {
|
||||
|
||||
/**
|
||||
* Draw the badges shape of the node
|
||||
* @param model The displayed model of this node, only for drawing and not received by users.
|
||||
* @param model The displayed model of this node, only for drawing and not received by users.
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the node.
|
||||
* @param diffData An object that contains previous and current data.
|
||||
* @param diffState An object that contains previous and current node's state.
|
||||
@ -786,7 +785,7 @@ export abstract class BaseNode {
|
||||
|
||||
/**
|
||||
* Draw other shapes(such as preRect,stateIcon) of the node
|
||||
* @param model The displayed model of this node, only for drawing and not received by users.
|
||||
* @param model The displayed model of this node, only for drawing and not received by users.
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the node.
|
||||
* @param diffData An object that contains previous and current data.
|
||||
* @param diffState An object that contains previous and current node's state.
|
||||
@ -976,14 +975,14 @@ export abstract class BaseNode {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create (if does not exit in shapeMap) or update the shape according to the configurations.
|
||||
* @param type shape's type
|
||||
* @param id unique string to indicates the shape
|
||||
* @param style style to be updated
|
||||
* @param shapeMap the shape map of a node / combo
|
||||
* @param model data model of the node / combo
|
||||
* @returns The display object representing the shape.
|
||||
*/
|
||||
* Create (if does not exit in shapeMap) or update the shape according to the configurations.
|
||||
* @param type shape's type
|
||||
* @param id unique string to indicates the shape
|
||||
* @param style style to be updated
|
||||
* @param shapeMap the shape map of a node / combo
|
||||
* @param model data model of the node / combo
|
||||
* @returns The display object representing the shape.
|
||||
*/
|
||||
public upsertShape(
|
||||
type: SHAPE_TYPE | SHAPE_TYPE_3D,
|
||||
id: string,
|
||||
|
@ -28,7 +28,7 @@ export abstract class BaseNode3D extends BaseNode {
|
||||
|
||||
/**
|
||||
* Draw the label shape of the 3D node
|
||||
* @param model The displayed model of this 3D node, only for drawing and not received by users.
|
||||
* @param model The displayed model of this 3D node, only for drawing and not received by users.
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the 3D node.
|
||||
* @param diffData An object that contains previous and current data.
|
||||
* @param diffState An object that contains previous and current 3D node's state.
|
||||
@ -111,7 +111,7 @@ export abstract class BaseNode3D extends BaseNode {
|
||||
|
||||
/**
|
||||
* Draw the icon shape of the 3D node
|
||||
* @param model The displayed model of this 3D node, only for drawing and not received by users.
|
||||
* @param model The displayed model of this 3D node, only for drawing and not received by users.
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the 3D node.
|
||||
* @param diffData An object that contains previous and current data.
|
||||
* @param diffState An object that contains previous and current 3D node's state.
|
||||
@ -206,5 +206,5 @@ export abstract class BaseNode3D extends BaseNode {
|
||||
* @param shapeMap The shape map that contains all of the elements to show on the node.
|
||||
* @param zoom The zoom level of the graph.
|
||||
*/
|
||||
public onZoom = (shapeMap: NodeShapeMap, zoom: number) => { };
|
||||
public onZoom = (shapeMap: NodeShapeMap, zoom: number) => {};
|
||||
}
|
||||
|
@ -9,6 +9,11 @@ import {
|
||||
} from '../../../types/node';
|
||||
import { BaseNode } from './base';
|
||||
|
||||
const offsetAngleMap = {
|
||||
horizontal: 0,
|
||||
vertical: Math.PI / 2,
|
||||
};
|
||||
|
||||
export class HexagonNode extends BaseNode {
|
||||
override defaultStyles = {
|
||||
keyShape: {
|
||||
@ -119,9 +124,9 @@ export class HexagonNode extends BaseNode {
|
||||
private getHexagonPoints(r: number, direction: string): [number, number][] {
|
||||
const angleIncrement = Math.PI / 3; //The angle increment between vertex.
|
||||
const v = [];
|
||||
const offsetAngle = direction === 'horizontal' ? 0 : Math.PI / 2;
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const angle = i * angleIncrement + offsetAngle;
|
||||
const angle = i * angleIncrement + offsetAngleMap[direction];
|
||||
const vx = r * Math.cos(angle);
|
||||
const vy = r * Math.sin(angle);
|
||||
v.push([vx, vy]);
|
||||
@ -153,8 +158,7 @@ export class HexagonNode extends BaseNode {
|
||||
? anchorPositionHorizontal
|
||||
: anchorPositionVertical;
|
||||
const angleIncrement = Math.PI / 3; //The angle increment between vertex.
|
||||
const offsetAngle =
|
||||
keyShapeStyle.direction === 'horizontal' ? 0 : Math.PI / 2;
|
||||
const offsetAngle = offsetAngleMap[keyShapeStyle.direction];
|
||||
const r = keyShapeStyle.r;
|
||||
const anchorPositionMap = {};
|
||||
for (let i = 0; i < 6; i++) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { deepMix, map } from '@antv/util';
|
||||
import { PolyPoint } from 'types/common';
|
||||
import { ID } from 'types';
|
||||
import { ID } from '../types';
|
||||
import { Point, PolyPoint } from '../types/common';
|
||||
import Node from '../item/node';
|
||||
import { simplePathFinder, SortedArray } from './polyline';
|
||||
import {
|
||||
@ -14,6 +14,7 @@ import { getLineIntersect } from './shape';
|
||||
import { eulerDist, manhattanDist } from './math';
|
||||
|
||||
export interface RouterCfg {
|
||||
name: 'orth' | 'er';
|
||||
/** Spacing between lines and points */
|
||||
offset?: number;
|
||||
/** Grid size */
|
||||
@ -46,6 +47,7 @@ export interface RouterCfg {
|
||||
}
|
||||
|
||||
const defaultCfg: RouterCfg = {
|
||||
name: 'orth',
|
||||
obstacleAvoidance: false,
|
||||
offset: 20,
|
||||
maxAllowedDirectionChange: Math.PI / 2,
|
||||
@ -73,6 +75,7 @@ const straightPath = (start: PolyPoint, end: PolyPoint): PolyPoint[] => {
|
||||
};
|
||||
|
||||
export const octolinearCfg: RouterCfg = {
|
||||
name: 'er',
|
||||
maxAllowedDirectionChange: Math.PI / 4,
|
||||
directions: [
|
||||
{ stepX: 1, stepY: 0 },
|
||||
@ -85,7 +88,7 @@ export const octolinearCfg: RouterCfg = {
|
||||
{ stepX: 1, stepY: -1 },
|
||||
],
|
||||
distFunc: eulerDist,
|
||||
fallbackRoute: straightPath,
|
||||
fallbackRoute: simplePathFinder,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -368,6 +371,7 @@ const getControlPoints = (
|
||||
|
||||
/** Find the shortest path computed by A* routing algorithm */
|
||||
export const pathFinder = (
|
||||
points: Point[],
|
||||
sourceNodeId: ID,
|
||||
targetNodeId: ID,
|
||||
nodeMap: Map<ID, Node>,
|
||||
@ -376,11 +380,16 @@ export const pathFinder = (
|
||||
const startNode = nodeMap.get(sourceNodeId);
|
||||
const endNode = nodeMap.get(targetNodeId);
|
||||
|
||||
const startPoint: PolyPoint = startNode.getPosition();
|
||||
const endPoint: PolyPoint = endNode.getPosition();
|
||||
const startPoint: PolyPoint = startNode?.getPosition() || points[0];
|
||||
const endPoint: PolyPoint =
|
||||
endNode?.getPosition() || points[points.length - 1];
|
||||
|
||||
if (isNaN(startPoint.x) || isNaN(endPoint.x)) return [];
|
||||
const cfg: RouterCfg = deepMix(defaultCfg, routerCfg);
|
||||
|
||||
const defaultCfgs =
|
||||
routerCfg.name === 'orth' ? defaultCfg : deepMix(defaultCfg, octolinearCfg);
|
||||
|
||||
const cfg: RouterCfg = deepMix(defaultCfgs, routerCfg);
|
||||
|
||||
if (!cfg.obstacleAvoidance) {
|
||||
return cfg.fallbackRoute(startPoint, endPoint, startNode, endNode, cfg);
|
||||
|
@ -240,13 +240,7 @@ export default () => {
|
||||
data: defaultData,
|
||||
modes: {
|
||||
// supported behavior
|
||||
default: ['activate-relations'],
|
||||
},
|
||||
node: {
|
||||
anchorPoints: [
|
||||
[0.5, 0],
|
||||
[0.5, 1],
|
||||
],
|
||||
default: ['activate-relations', 'drag-node'],
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -53,7 +53,7 @@ const defaultData = {
|
||||
data: {
|
||||
type: 'loop-edge',
|
||||
keyShape: {
|
||||
endArrow: {},
|
||||
endArrow: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -64,7 +64,7 @@ const defaultData = {
|
||||
data: {
|
||||
type: 'loop-edge',
|
||||
keyShape: {
|
||||
endArrow: {},
|
||||
endArrow: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -75,7 +75,7 @@ const defaultData = {
|
||||
data: {
|
||||
type: 'loop-edge',
|
||||
keyShape: {
|
||||
endArrow: {},
|
||||
endArrow: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1,4 +1,5 @@
|
||||
// @ts-nocheck
|
||||
import { Extensions, Graph, IGraph, extend } from '../../../../src/index';
|
||||
import { TestCaseContext } from '../../interface';
|
||||
import { Graph, Extensions, extend } from '../../../../src/index';
|
||||
let graph: IGraph;
|
||||
@ -348,11 +349,19 @@ export default (context: TestCaseContext) => {
|
||||
data: defaultData,
|
||||
modes: {
|
||||
// supported behavior
|
||||
default: ['activate-relations', 'drag-node'],
|
||||
default: ['drag-node'],
|
||||
},
|
||||
edge: (edgeInnerModel: any) => {
|
||||
const { id, data } = edgeInnerModel;
|
||||
return { id, data };
|
||||
return {
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
keyShape: {
|
||||
...data.keyShape,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import G6, { ID, stdLib } from '../../../src/index';
|
||||
import G6, { ID, Extensions } from '../../../src/index';
|
||||
import { ShapeStyle } from '../../../src/types/item';
|
||||
export default (
|
||||
context,
|
||||
@ -15,7 +15,7 @@ export default (
|
||||
style,
|
||||
nonMembers = [],
|
||||
} = options;
|
||||
const hullPlugin = new stdLib.plugins.hull({
|
||||
const hullPlugin = new Extensions.Hull({
|
||||
key: 'hull-plugin1',
|
||||
style,
|
||||
hulls: [
|
||||
|
@ -1,180 +1,175 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Extensions, Graph, stdLib, extend } from '@antv/g6';
|
||||
|
||||
/**
|
||||
* The demo shows customing a combo type by extending the built-in circle combo
|
||||
* by Shiwu
|
||||
*
|
||||
*/
|
||||
|
||||
// The symbols for the marker inside the combo
|
||||
const collapseIcon = (x, y, r) => {
|
||||
return [
|
||||
['M', x - r, y],
|
||||
['a', r, r, 0, 1, 0, r * 2, 0],
|
||||
['a', r, r, 0, 1, 0, -r * 2, 0],
|
||||
['M', x - r + 4, y],
|
||||
['L', x - r + 2 * r - 4, y],
|
||||
];
|
||||
};
|
||||
const expandIcon = (x, y, r) => {
|
||||
return [
|
||||
['M', x - r, y],
|
||||
['a', r, r, 0, 1, 0, r * 2, 0],
|
||||
['a', r, r, 0, 1, 0, -r * 2, 0],
|
||||
['M', x - r + 4, y],
|
||||
['L', x - r + 2 * r - 4, y],
|
||||
['M', x - r + r, y - r + 4],
|
||||
['L', x, y + r - 4],
|
||||
];
|
||||
};
|
||||
|
||||
G6.registerCombo(
|
||||
'cCircle',
|
||||
{
|
||||
drawShape: function draw(cfg, group) {
|
||||
const self = this;
|
||||
// Get the shape style, where the style.r corresponds to the R in the Illustration of Built-in Rect Combo
|
||||
const style = self.getShapeStyle(cfg);
|
||||
// Add a circle shape as keyShape which is the same as the extended 'circle' type Combo
|
||||
const circle = group.addShape('circle', {
|
||||
attrs: {
|
||||
...style,
|
||||
x: 0,
|
||||
y: 0,
|
||||
r: style.r,
|
||||
},
|
||||
draggable: true,
|
||||
// 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: 'combo-keyShape',
|
||||
});
|
||||
// Add the marker on the bottom
|
||||
const marker = group.addShape('marker', {
|
||||
attrs: {
|
||||
...style,
|
||||
fill: '#fff',
|
||||
opacity: 1,
|
||||
x: 0,
|
||||
y: style.r,
|
||||
r: 10,
|
||||
symbol: collapseIcon,
|
||||
},
|
||||
draggable: true,
|
||||
// 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: 'combo-marker-shape',
|
||||
});
|
||||
|
||||
return circle;
|
||||
},
|
||||
// Define the updating logic for the marker
|
||||
afterUpdate: function afterUpdate(cfg, combo) {
|
||||
const self = this;
|
||||
// Get the shape style, where the style.r corresponds to the R in the Illustration of Built-in Rect Combo
|
||||
const style = self.getShapeStyle(cfg);
|
||||
const group = combo.get('group');
|
||||
// Find the marker shape in the graphics group of the Combo
|
||||
const marker = group.find((ele) => ele.get('name') === 'combo-marker-shape');
|
||||
// Update the marker shape
|
||||
marker.attr({
|
||||
x: 0,
|
||||
y: style.r,
|
||||
// The property 'collapsed' in the combo data represents the collapsing state of the Combo
|
||||
// Update the symbol according to 'collapsed'
|
||||
symbol: cfg.collapsed ? expandIcon : collapseIcon,
|
||||
});
|
||||
},
|
||||
},
|
||||
'circle',
|
||||
);
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{ id: 'node1', x: 250, y: 200, comboId: 'combo1' },
|
||||
{ id: 'node2', x: 300, y: 200, comboId: 'combo1' },
|
||||
{ id: 'node3', x: 100, y: 200, comboId: 'combo3' },
|
||||
],
|
||||
combos: [
|
||||
{ id: 'combo1', label: 'Combo 1', parentId: 'combo2' },
|
||||
{ id: 'combo2', label: 'Combo 2' },
|
||||
{ id: 'combo3', label: 'Combo 3', collapsed: true },
|
||||
],
|
||||
};
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const descriptionDiv = document.createElement('div');
|
||||
descriptionDiv.innerHTML = 'Click the bottom marker to collapse/expand the combo.';
|
||||
const container = document.getElementById('container');
|
||||
container.appendChild(descriptionDiv);
|
||||
|
||||
const width = container.scrollWidth;
|
||||
const height = (container.scrollHeight || 500) - 20;
|
||||
const graph = new G6.Graph({
|
||||
class CCircleCombo extends Extensions.CircleCombo {
|
||||
drawOtherShapes(model, shapeMap, diffData) {
|
||||
const { data } = model;
|
||||
const keyShapeBBox = shapeMap.keyShape.getLocalBounds();
|
||||
const otherShapes = {
|
||||
markerShape: this.upsertShape(
|
||||
'path',
|
||||
'markerShape',
|
||||
{
|
||||
cursor: 'pointer',
|
||||
stroke: '#666',
|
||||
lineWidth: 1,
|
||||
fill: '#fff',
|
||||
path: data.collapsed
|
||||
? stdLib.markers.expand(keyShapeBBox.center[0], keyShapeBBox.max[1], 8)
|
||||
: stdLib.markers.collapse(keyShapeBBox.center[0], keyShapeBBox.max[1], 8),
|
||||
},
|
||||
shapeMap,
|
||||
model,
|
||||
),
|
||||
};
|
||||
return otherShapes;
|
||||
}
|
||||
}
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
combos: {
|
||||
'c-circle-combo': CCircleCombo,
|
||||
},
|
||||
behaviors: {
|
||||
'hover-activate': Extensions.HoverActivate,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// Set groupByTypes to false to get rendering result with reasonable visual zIndex for combos
|
||||
groupByTypes: false,
|
||||
// Configure the combos globally
|
||||
defaultCombo: {
|
||||
// The type of the combos. You can also assign type in the data of combos
|
||||
type: 'cCircle',
|
||||
labelCfg: {
|
||||
refY: 2,
|
||||
},
|
||||
// ... Other global configurations for combos
|
||||
stackCfg: {
|
||||
ignoreStateChange: true,
|
||||
},
|
||||
comboStateStyles: {
|
||||
dragenter: {
|
||||
lineWidth: 4,
|
||||
stroke: '#FE9797',
|
||||
node: {
|
||||
labelShape: {
|
||||
position: 'center',
|
||||
text: {
|
||||
fields: ['id'],
|
||||
formatter: (model) => model.id,
|
||||
},
|
||||
},
|
||||
animates: {
|
||||
update: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
combo: (model) => {
|
||||
return {
|
||||
id: model.id,
|
||||
data: {
|
||||
type: 'c-circle-combo',
|
||||
...model.data,
|
||||
keyShape: {
|
||||
padding: [10, 20, 30, 40],
|
||||
r: 50,
|
||||
},
|
||||
labelShape: {
|
||||
text: model.data.label,
|
||||
position: 'top',
|
||||
},
|
||||
otherShapes: {},
|
||||
animates: {
|
||||
buildIn: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 500,
|
||||
delay: 500 + Math.random() * 500,
|
||||
},
|
||||
],
|
||||
buildOut: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 200,
|
||||
},
|
||||
],
|
||||
update: [
|
||||
{
|
||||
fields: ['lineWidth', 'r'],
|
||||
shapeId: 'keyShape',
|
||||
},
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 350,
|
||||
y: 200,
|
||||
parentId: 'combo1',
|
||||
},
|
||||
},
|
||||
{ id: 'node2', data: { x: 350, y: 250, parentId: 'combo1' } },
|
||||
{ id: 'node3', data: { x: 100, y: 200, parentId: 'combo3' } },
|
||||
],
|
||||
edges: [
|
||||
{ id: 'edge1', source: 'node1', target: 'node2' },
|
||||
{ id: 'edge2', source: 'node1', target: 'node3' },
|
||||
{ id: 'edge3', source: 'combo1', target: 'node3' },
|
||||
],
|
||||
combos: [
|
||||
{ id: 'combo1', data: { label: 'Combo 1', parentId: 'combo2' } },
|
||||
{ id: 'combo2', data: { label: 'Combo 2' } },
|
||||
{ id: 'combo3', data: { label: 'Combo 3', collapsed: true } },
|
||||
],
|
||||
},
|
||||
modes: {
|
||||
default: ['drag-combo', 'drag-node', 'drag-canvas', 'click-select'],
|
||||
default: [
|
||||
{
|
||||
type: 'drag-node',
|
||||
enableTransient: false,
|
||||
updateComboStructure: false,
|
||||
},
|
||||
'drag-canvas',
|
||||
{
|
||||
type: 'click-select',
|
||||
itemTypes: ['node', 'edge', 'combo'],
|
||||
},
|
||||
{
|
||||
type: 'drag-combo',
|
||||
enableTransient: true,
|
||||
updateComboStructure: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
// collapse/expand when click the marker
|
||||
graph.on('combo:click', (e) => {
|
||||
if (e.target.get('name') === 'combo-marker-shape') {
|
||||
// graph.collapseExpandCombo(e.item.getModel().id);
|
||||
graph.collapseExpandCombo(e.item);
|
||||
if (graph.get('layout')) graph.layout();
|
||||
else graph.refreshPositions();
|
||||
/** Click the bottom marker to collapse/expand the combo. */
|
||||
graph.on('combo:click', (event) => {
|
||||
const { itemId, target } = event;
|
||||
if (target.id === 'markerShape') {
|
||||
const model = graph.getComboData(itemId);
|
||||
if (model.data.collapsed) {
|
||||
graph.expandCombo(itemId);
|
||||
} else {
|
||||
graph.collapseCombo(itemId);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
graph.on('combo:dragend', (e) => {
|
||||
graph.getCombos().forEach((combo) => {
|
||||
graph.setItemState(combo, 'dragenter', false);
|
||||
});
|
||||
});
|
||||
graph.on('node:dragend', (e) => {
|
||||
graph.getCombos().forEach((combo) => {
|
||||
graph.setItemState(combo, 'dragenter', false);
|
||||
});
|
||||
});
|
||||
|
||||
graph.on('combo:dragenter', (e) => {
|
||||
graph.setItemState(e.item, 'dragenter', true);
|
||||
});
|
||||
graph.on('combo:dragleave', (e) => {
|
||||
graph.setItemState(e.item, 'dragenter', false);
|
||||
});
|
||||
|
||||
graph.on('combo:mouseenter', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', true);
|
||||
});
|
||||
|
||||
graph.on('combo:mouseleave', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', false);
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight - 20);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,178 +1,174 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Extensions, Graph, stdLib, extend } from '@antv/g6';
|
||||
|
||||
/**
|
||||
* The demo shows customing a combo type by extending the built-in circle combo
|
||||
* by Shiwu
|
||||
*
|
||||
*/
|
||||
|
||||
// The symbols for the marker inside the combo
|
||||
const collapseIcon = (x, y, r) => {
|
||||
return [
|
||||
['M', x - r, y],
|
||||
['a', r, r, 0, 1, 0, r * 2, 0],
|
||||
['a', r, r, 0, 1, 0, -r * 2, 0],
|
||||
['M', x - r + 4, y],
|
||||
['L', x - r + 2 * r - 4, y],
|
||||
];
|
||||
};
|
||||
const expandIcon = (x, y, r) => {
|
||||
return [
|
||||
['M', x - r, y],
|
||||
['a', r, r, 0, 1, 0, r * 2, 0],
|
||||
['a', r, r, 0, 1, 0, -r * 2, 0],
|
||||
['M', x - r + 4, y],
|
||||
['L', x - r + 2 * r - 4, y],
|
||||
['M', x - r + r, y - r + 4],
|
||||
['L', x, y + r - 4],
|
||||
];
|
||||
};
|
||||
|
||||
G6.registerCombo(
|
||||
'cRect',
|
||||
{
|
||||
drawShape: function drawShape(cfg, group) {
|
||||
const self = this;
|
||||
// Get the padding from the configuration
|
||||
cfg.padding = cfg.padding || [50, 20, 20, 20];
|
||||
// Get the shape's style, where the style.width and style.height correspond to the width and height in the figure of Illustration of Built-in Rect Combo
|
||||
const style = self.getShapeStyle(cfg);
|
||||
// Add a rect shape as the keyShape which is the same as the extended rect Combo
|
||||
const rect = group.addShape('rect', {
|
||||
attrs: {
|
||||
...style,
|
||||
x: -style.width / 2 - (cfg.padding[3] - cfg.padding[1]) / 2,
|
||||
y: -style.height / 2 - (cfg.padding[0] - cfg.padding[2]) / 2,
|
||||
width: style.width,
|
||||
height: style.height,
|
||||
},
|
||||
draggable: true,
|
||||
// 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: 'combo-keyShape',
|
||||
});
|
||||
// Add the circle on the right
|
||||
group.addShape('marker', {
|
||||
attrs: {
|
||||
...style,
|
||||
fill: '#fff',
|
||||
opacity: 1,
|
||||
// cfg.style.width and cfg.style.heigth correspond to the innerWidth and innerHeight in the figure of Illustration of Built-in Rect Combo
|
||||
x: cfg.style.width / 2 + cfg.padding[1],
|
||||
y: (cfg.padding[2] - cfg.padding[0]) / 2,
|
||||
r: 10,
|
||||
symbol: collapseIcon,
|
||||
},
|
||||
draggable: true,
|
||||
// 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: 'combo-marker-shape',
|
||||
});
|
||||
return rect;
|
||||
},
|
||||
// Define the updating logic of the right circle
|
||||
afterUpdate: function afterUpdate(cfg, combo) {
|
||||
const group = combo.get('group');
|
||||
// Find the circle shape in the graphics group of the Combo by name
|
||||
const marker = group.find((ele) => ele.get('name') === 'combo-marker-shape');
|
||||
// Update the position of the right circle
|
||||
marker.attr({
|
||||
// cfg.style.width and cfg.style.heigth correspond to the innerWidth and innerHeight in the figure of Illustration of Built-in Rect Combo
|
||||
x: cfg.style.width / 2 + cfg.padding[1],
|
||||
y: (cfg.padding[2] - cfg.padding[0]) / 2,
|
||||
// The property 'collapsed' in the combo data represents the collapsing state of the Combo
|
||||
// Update the symbol according to 'collapsed'
|
||||
symbol: cfg.collapsed ? expandIcon : collapseIcon,
|
||||
});
|
||||
},
|
||||
},
|
||||
'rect',
|
||||
);
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{ id: 'node1', x: 250, y: 200, comboId: 'combo1' },
|
||||
{ id: 'node2', x: 300, y: 200, comboId: 'combo1' },
|
||||
{ id: 'node3', x: 100, y: 200, comboId: 'combo3' },
|
||||
],
|
||||
combos: [
|
||||
{ id: 'combo1', label: 'Combo 1', parentId: 'combo2' },
|
||||
{ id: 'combo2', label: 'Combo 2' },
|
||||
{ id: 'combo3', label: 'Combo 3', collapsed: true },
|
||||
],
|
||||
};
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const descriptionDiv = document.createElement('div');
|
||||
descriptionDiv.innerHTML = 'Click the right marker to collapse/expand the combo.';
|
||||
const container = document.getElementById('container');
|
||||
descriptionDiv.innerHTML = 'Click the bottom marker to collapse/expand the combo.';
|
||||
container.appendChild(descriptionDiv);
|
||||
|
||||
const width = container.scrollWidth;
|
||||
const height = (container.scrollHeight || 500) - 20;
|
||||
const graph = new G6.Graph({
|
||||
class CRectCombo extends Extensions.RectCombo {
|
||||
drawOtherShapes(model, shapeMap, diffData) {
|
||||
const { data } = model;
|
||||
const keyShapeBBox = shapeMap.keyShape.getLocalBounds();
|
||||
const otherShapes = {
|
||||
markerShape: this.upsertShape(
|
||||
'path',
|
||||
'markerShape',
|
||||
{
|
||||
cursor: 'pointer',
|
||||
stroke: '#666',
|
||||
lineWidth: 1,
|
||||
fill: '#fff',
|
||||
path: data.collapsed
|
||||
? stdLib.markers.expand(keyShapeBBox.center[0], keyShapeBBox.max[1], 8)
|
||||
: stdLib.markers.collapse(keyShapeBBox.center[0], keyShapeBBox.max[1], 8),
|
||||
},
|
||||
shapeMap,
|
||||
model,
|
||||
),
|
||||
};
|
||||
return otherShapes;
|
||||
}
|
||||
}
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
combos: {
|
||||
'c-rect-combo': CRectCombo,
|
||||
},
|
||||
// behaviors: {
|
||||
// 'hover-activate': Extensions.HoverActivate,
|
||||
// },
|
||||
});
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// Set groupByTypes to false to get rendering result with reasonable visual zIndex for combos
|
||||
groupByTypes: false,
|
||||
// Configure the combos globally
|
||||
defaultCombo: {
|
||||
// The type of the combos. You can also assign type in the data of combos
|
||||
type: 'cRect',
|
||||
// ... Other global configurations for combos
|
||||
stackCfg: {
|
||||
ignoreStateChange: true,
|
||||
},
|
||||
comboStateStyles: {
|
||||
dragenter: {
|
||||
lineWidth: 4,
|
||||
stroke: '#FE9797',
|
||||
node: {
|
||||
labelShape: {
|
||||
position: 'center',
|
||||
text: {
|
||||
fields: ['id'],
|
||||
formatter: (model) => model.id,
|
||||
},
|
||||
},
|
||||
animates: {
|
||||
update: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
combo: (model) => {
|
||||
return {
|
||||
id: model.id,
|
||||
data: {
|
||||
type: 'c-rect-combo',
|
||||
...model.data,
|
||||
keyShape: {
|
||||
padding: [10, 20, 30, 40],
|
||||
r: 50,
|
||||
},
|
||||
labelShape: {
|
||||
text: model.data.label,
|
||||
position: 'top',
|
||||
},
|
||||
otherShapes: {},
|
||||
animates: {
|
||||
buildIn: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 500,
|
||||
delay: 500 + Math.random() * 500,
|
||||
},
|
||||
],
|
||||
buildOut: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 200,
|
||||
},
|
||||
],
|
||||
update: [
|
||||
{
|
||||
fields: ['lineWidth', 'r'],
|
||||
shapeId: 'keyShape',
|
||||
},
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 350,
|
||||
y: 200,
|
||||
parentId: 'combo1',
|
||||
},
|
||||
},
|
||||
{ id: 'node2', data: { x: 350, y: 250, parentId: 'combo1' } },
|
||||
{ id: 'node3', data: { x: 100, y: 200, parentId: 'combo3' } },
|
||||
],
|
||||
edges: [
|
||||
{ id: 'edge1', source: 'node1', target: 'node2' },
|
||||
{ id: 'edge2', source: 'node1', target: 'node3' },
|
||||
],
|
||||
combos: [
|
||||
{ id: 'combo1', data: { label: 'Combo 1', parentId: 'combo2' } },
|
||||
{ id: 'combo2', data: { label: 'Combo 2' } },
|
||||
{ id: 'combo3', data: { label: 'Combo 3', collapsed: true } },
|
||||
],
|
||||
},
|
||||
modes: {
|
||||
default: ['drag-combo', 'drag-node', 'drag-canvas', 'click-select'],
|
||||
default: [
|
||||
{
|
||||
type: 'drag-node',
|
||||
enableTransient: false,
|
||||
updateComboStructure: false,
|
||||
},
|
||||
'drag-canvas',
|
||||
{
|
||||
type: 'click-select',
|
||||
itemTypes: ['node', 'edge', 'combo'],
|
||||
},
|
||||
{
|
||||
type: 'drag-combo',
|
||||
enableTransient: true,
|
||||
updateComboStructure: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
// collapse/expand when click the marker
|
||||
graph.on('combo:click', (e) => {
|
||||
if (e.target.get('name') === 'combo-marker-shape') {
|
||||
// graph.collapseExpandCombo(e.item.getModel().id);
|
||||
graph.collapseExpandCombo(e.item);
|
||||
if (graph.get('layout')) graph.layout();
|
||||
else graph.refreshPositions();
|
||||
/** Click the bottom marker to collapse/expand the combo. */
|
||||
graph.on('combo:click', (event) => {
|
||||
const { itemId, target } = event;
|
||||
if (target.id === 'markerShape') {
|
||||
const model = graph.getComboData(itemId);
|
||||
if (model.data.collapsed) {
|
||||
graph.expandCombo(itemId);
|
||||
} else {
|
||||
graph.collapseCombo(itemId);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
graph.on('combo:dragend', (e) => {
|
||||
graph.getCombos().forEach((combo) => {
|
||||
graph.setItemState(combo, 'dragenter', false);
|
||||
});
|
||||
});
|
||||
graph.on('node:dragend', (e) => {
|
||||
graph.getCombos().forEach((combo) => {
|
||||
graph.setItemState(combo, 'dragenter', false);
|
||||
});
|
||||
});
|
||||
|
||||
graph.on('combo:dragenter', (e) => {
|
||||
graph.setItemState(e.item, 'dragenter', true);
|
||||
});
|
||||
graph.on('combo:dragleave', (e) => {
|
||||
graph.setItemState(e.item, 'dragenter', false);
|
||||
});
|
||||
|
||||
graph.on('combo:mouseenter', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', true);
|
||||
});
|
||||
|
||||
graph.on('combo:mouseleave', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', false);
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight - 20);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,91 +1,134 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Graph } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{ id: 'node1', x: 350, y: 200, comboId: 'combo1' },
|
||||
{ id: 'node2', x: 350, y: 250, comboId: 'combo1' },
|
||||
{ id: 'node3', x: 100, y: 200, comboId: 'combo3' },
|
||||
],
|
||||
edges: [
|
||||
{ source: 'node1', target: 'node2' },
|
||||
{ source: 'node1', target: 'node3' },
|
||||
{ source: 'combo1', target: 'node3' },
|
||||
],
|
||||
combos: [
|
||||
{ id: 'combo1', label: 'Combo 1', parentId: 'combo2' },
|
||||
{ id: 'combo2', label: 'Combo 2' },
|
||||
{ id: 'combo3', label: 'Combo 3', collapsed: true },
|
||||
],
|
||||
};
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const descriptionDiv = document.createElement('div');
|
||||
descriptionDiv.innerHTML =
|
||||
'Double click the combo to collapse/expand it. Drag the node or combo to change the hierarchy.';
|
||||
const container = document.getElementById('container');
|
||||
container.appendChild(descriptionDiv);
|
||||
|
||||
const width = container.scrollWidth;
|
||||
const height = (container.scrollHeight || 500) - 20;
|
||||
const graph = new G6.Graph({
|
||||
const graph = new Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// Set groupByTypes to false to get rendering result with reasonable visual zIndex for combos
|
||||
groupByTypes: false,
|
||||
defaultCombo: {
|
||||
type: 'circle',
|
||||
style: {
|
||||
lineWidth: 1,
|
||||
stackCfg: {
|
||||
ignoreStateChange: true,
|
||||
},
|
||||
node: {
|
||||
labelShape: {
|
||||
position: 'center',
|
||||
text: {
|
||||
fields: ['id'],
|
||||
formatter: (model) => model.id,
|
||||
},
|
||||
},
|
||||
labelCfg: {
|
||||
refY: 15,
|
||||
position: 'bottom',
|
||||
animates: {
|
||||
update: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
comboStateStyles: {
|
||||
dragenter: {
|
||||
lineWidth: 4,
|
||||
stroke: '#FE9797',
|
||||
},
|
||||
combo: (model) => {
|
||||
return {
|
||||
id: model.id,
|
||||
data: {
|
||||
type: 'circle-combo',
|
||||
...model.data,
|
||||
keyShape: {
|
||||
padding: [10, 20, 30, 40],
|
||||
r: 50,
|
||||
},
|
||||
labelShape: {
|
||||
text: model.data.label,
|
||||
position: 'top',
|
||||
},
|
||||
|
||||
animates: {
|
||||
buildIn: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 500,
|
||||
delay: 500 + Math.random() * 500,
|
||||
},
|
||||
],
|
||||
buildOut: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 200,
|
||||
},
|
||||
],
|
||||
update: [
|
||||
{
|
||||
fields: ['lineWidth', 'r'],
|
||||
shapeId: 'keyShape',
|
||||
},
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 350,
|
||||
y: 200,
|
||||
parentId: 'combo1',
|
||||
},
|
||||
},
|
||||
{ id: 'node2', data: { x: 350, y: 250, parentId: 'combo1' } },
|
||||
{ id: 'node3', data: { x: 100, y: 200, parentId: 'combo3' } },
|
||||
],
|
||||
edges: [
|
||||
{ id: 'edge1', source: 'node1', target: 'node2' },
|
||||
{ id: 'edge2', source: 'node1', target: 'node3' },
|
||||
{ id: 'edge3', source: 'combo1', target: 'node3' },
|
||||
],
|
||||
combos: [
|
||||
{ id: 'combo1', data: { label: 'Combo 1', parentId: 'combo2' } },
|
||||
{ id: 'combo2', data: { label: 'Combo 2' } },
|
||||
{ id: 'combo3', data: { label: 'Combo 3', collapsed: true } },
|
||||
],
|
||||
},
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node', 'drag-combo', 'collapse-expand-combo', 'click-select'],
|
||||
default: [
|
||||
'collapse-expand-combo',
|
||||
{
|
||||
type: 'drag-node',
|
||||
enableTransient: false,
|
||||
updateComboStructure: false,
|
||||
},
|
||||
'drag-canvas',
|
||||
{
|
||||
type: 'click-select',
|
||||
itemTypes: ['node', 'edge', 'combo'],
|
||||
},
|
||||
{
|
||||
type: 'hover-activate',
|
||||
itemTypes: ['node', 'edge', 'combo'],
|
||||
},
|
||||
{
|
||||
type: 'drag-combo',
|
||||
enableTransient: true,
|
||||
updateComboStructure: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('combo:dragend', (e) => {
|
||||
graph.getCombos().forEach((combo) => {
|
||||
graph.setItemState(combo, 'dragenter', false);
|
||||
});
|
||||
});
|
||||
graph.on('node:dragend', (e) => {
|
||||
graph.getCombos().forEach((combo) => {
|
||||
graph.setItemState(combo, 'dragenter', false);
|
||||
});
|
||||
});
|
||||
graph.on('combo:dragenter', (e) => {
|
||||
graph.setItemState(e.item, 'dragenter', true);
|
||||
});
|
||||
graph.on('combo:dragleave', (e) => {
|
||||
graph.setItemState(e.item, 'dragenter', false);
|
||||
});
|
||||
|
||||
graph.on('combo:mouseenter', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', true);
|
||||
});
|
||||
|
||||
graph.on('combo:mouseleave', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', false);
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight - 20);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,94 +1,133 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Graph } from '@antv/g6';
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{ id: 'node1', x: 350, y: 200, comboId: 'combo1' },
|
||||
{ id: 'node2', x: 350, y: 250, comboId: 'combo1' },
|
||||
{ id: 'node3', x: 100, y: 200, comboId: 'combo3' },
|
||||
],
|
||||
edges: [
|
||||
{ source: 'node1', target: 'node2' },
|
||||
{ source: 'node1', target: 'node3' },
|
||||
{ source: 'combo1', target: 'node3' },
|
||||
],
|
||||
combos: [
|
||||
{ id: 'combo1', label: 'Combo 1', parentId: 'combo2' },
|
||||
{ id: 'combo2', label: 'Combo 2' },
|
||||
{ id: 'combo3', label: 'Combo 3' },
|
||||
],
|
||||
};
|
||||
const descriptionDiv = document.createElement('div');
|
||||
descriptionDiv.innerHTML =
|
||||
'Double click the combo to collapse/expand it. Drag the node or combo to change the hierarchy.';
|
||||
const container = document.getElementById('container');
|
||||
container.appendChild(descriptionDiv);
|
||||
|
||||
const width = container.scrollWidth;
|
||||
const height = (container.scrollHeight || 500) - 20;
|
||||
const graph = new G6.Graph({
|
||||
const graph = new Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// Set groupByTypes to false to get rendering result with reasonable visual zIndex for combos
|
||||
groupByTypes: false,
|
||||
defaultCombo: {
|
||||
type: 'rect',
|
||||
size: [40, 10], // The minimum size of the Combo
|
||||
padding: [30, 20, 10, 20],
|
||||
style: {
|
||||
lineWidth: 1,
|
||||
stackCfg: {
|
||||
ignoreStateChange: true,
|
||||
},
|
||||
node: {
|
||||
labelShape: {
|
||||
position: 'center',
|
||||
text: {
|
||||
fields: ['id'],
|
||||
formatter: (model) => model.id,
|
||||
},
|
||||
},
|
||||
labelCfg: {
|
||||
refY: 10,
|
||||
refX: 20,
|
||||
position: 'top',
|
||||
animates: {
|
||||
update: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
comboStateStyles: {
|
||||
dragenter: {
|
||||
lineWidth: 4,
|
||||
stroke: '#FE9797',
|
||||
},
|
||||
combo: (model) => {
|
||||
return {
|
||||
id: model.id,
|
||||
data: {
|
||||
type: 'rect-combo',
|
||||
...model.data,
|
||||
keyShape: {
|
||||
padding: [10, 20, 30, 40],
|
||||
r: 50,
|
||||
},
|
||||
labelShape: {
|
||||
text: model.data.label,
|
||||
position: 'top',
|
||||
},
|
||||
|
||||
animates: {
|
||||
buildIn: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 500,
|
||||
delay: 500 + Math.random() * 500,
|
||||
},
|
||||
],
|
||||
buildOut: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 200,
|
||||
},
|
||||
],
|
||||
update: [
|
||||
{
|
||||
fields: ['lineWidth', 'r'],
|
||||
shapeId: 'keyShape',
|
||||
},
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 350,
|
||||
y: 200,
|
||||
parentId: 'combo1',
|
||||
},
|
||||
},
|
||||
{ id: 'node2', data: { x: 350, y: 250, parentId: 'combo1' } },
|
||||
{ id: 'node3', data: { x: 100, y: 200, parentId: 'combo3' } },
|
||||
],
|
||||
edges: [
|
||||
{ id: 'edge1', source: 'node1', target: 'node2' },
|
||||
{ id: 'edge2', source: 'node1', target: 'node3' },
|
||||
],
|
||||
combos: [
|
||||
{ id: 'combo1', data: { label: 'Combo 1', parentId: 'combo2' } },
|
||||
{ id: 'combo2', data: { label: 'Combo 2' } },
|
||||
{ id: 'combo3', data: { label: 'Combo 3', collapsed: true } },
|
||||
],
|
||||
},
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node', 'drag-combo', 'collapse-expand-combo', 'click-select'],
|
||||
default: [
|
||||
'collapse-expand-combo',
|
||||
{
|
||||
type: 'drag-node',
|
||||
enableTransient: false,
|
||||
updateComboStructure: false,
|
||||
},
|
||||
'drag-canvas',
|
||||
{
|
||||
type: 'click-select',
|
||||
itemTypes: ['node', 'edge', 'combo'],
|
||||
},
|
||||
{
|
||||
type: 'hover-activate',
|
||||
itemTypes: ['node', 'edge', 'combo'],
|
||||
},
|
||||
{
|
||||
type: 'drag-combo',
|
||||
enableTransient: true,
|
||||
updateComboStructure: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('combo:dragend', (e) => {
|
||||
graph.getCombos().forEach((combo) => {
|
||||
graph.setItemState(combo, 'dragenter', false);
|
||||
});
|
||||
});
|
||||
graph.on('node:dragend', (e) => {
|
||||
graph.getCombos().forEach((combo) => {
|
||||
graph.setItemState(combo, 'dragenter', false);
|
||||
});
|
||||
});
|
||||
|
||||
graph.on('combo:dragenter', (e) => {
|
||||
graph.setItemState(e.item, 'dragenter', true);
|
||||
});
|
||||
graph.on('combo:dragleave', (e) => {
|
||||
graph.setItemState(e.item, 'dragenter', false);
|
||||
});
|
||||
|
||||
graph.on('combo:mouseenter', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', true);
|
||||
});
|
||||
|
||||
graph.on('combo:mouseleave', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', false);
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight - 20);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,11 +0,0 @@
|
||||
---
|
||||
title: API
|
||||
---
|
||||
|
||||
# Built-in Behaviors
|
||||
|
||||
Refer to [Built-in Behavior](/en/docs/manual/middle/states/defaultBehavior).
|
||||
|
||||
# Custom Behavior
|
||||
|
||||
Refer to [Custom Behavior](/en/docs/manual/middle/states/custom-behavior).
|
@ -1,11 +0,0 @@
|
||||
---
|
||||
title: API
|
||||
---
|
||||
|
||||
# 复合交互
|
||||
|
||||
请参考教程 [内置 Behavior](/en/docs/manual/middle/states/defaultBehavior)。
|
||||
|
||||
# 自定义复合交互
|
||||
|
||||
请参考教程 [自定义 Behavior](/en/docs/manual/middle/states/custom-behavior)。
|
@ -1,34 +0,0 @@
|
||||
import G6 from '@antv/g6';
|
||||
|
||||
const container = document.getElementById('container');
|
||||
|
||||
const tipDiv = document.createElement('div');
|
||||
tipDiv.innerHTML = `Press both the keys 'control' and '1' to call graph.fitView. The keys and the called function can be configured.【ATTENTION】: make sure the focus is on the canvas when you pressing keys
|
||||
<br /> 按住 'control' 并按下 '1' 键,将会调用 graph.fitView。组合按键及被调用的函数及其参数均可被配置。【注意】:使用组合件调用函数时,请保证当前焦点在画布上`;
|
||||
container.appendChild(tipDiv);
|
||||
|
||||
const width = container.scrollWidth;
|
||||
const height = (container.scrollHeight || 500) - 100;
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
fitCenter: true,
|
||||
modes: {
|
||||
default: ['shortcuts-call'],
|
||||
},
|
||||
});
|
||||
fetch('https://gw.alipayobjects.com/os/bmw-prod/b0ca4b15-bd0c-43ec-ae41-c810374a1d55.json')
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
graph.zoom(2);
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight - 100);
|
||||
};
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"title": {
|
||||
"zh": "中文分类",
|
||||
"en": "Category"
|
||||
},
|
||||
"demos": [
|
||||
{
|
||||
"filename": "fitView.js",
|
||||
"title": "使用快捷键适配画布",
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*FALhS5MhAzAAAAAAAAAAAAAAARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "moveTo.js",
|
||||
"title": "使用快捷键移动到左上角",
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*7RPkSJpwzrQAAAAAAAAAAAAAARQnAQ"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
import G6 from '@antv/g6';
|
||||
|
||||
const container = document.getElementById('container');
|
||||
|
||||
const tipDiv = document.createElement('div');
|
||||
tipDiv.innerHTML = `Press both the keys 'control' and 'm' to call graph.fitView. The keys and the called function can be configured.【ATTENTION】: make sure the focus is on the canvas when you pressing keys
|
||||
<br /> 按住 'control' 并按下 'm' 键,将会调用 graph.moveTo。组合按键及被调用的函数及其参数均可被配置。【注意】:使用组合件调用函数时,请保证当前焦点在画布上`;
|
||||
container.appendChild(tipDiv);
|
||||
|
||||
const width = container.scrollWidth;
|
||||
const height = (container.scrollHeight || 500) - 100;
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
fitCenter: true,
|
||||
modes: {
|
||||
default: [
|
||||
{
|
||||
type: 'shortcuts-call',
|
||||
functionName: 'moveTo',
|
||||
functionParams: [0, 0],
|
||||
combinedKey: 'm',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
fetch('https://gw.alipayobjects.com/os/bmw-prod/b0ca4b15-bd0c-43ec-ae41-c810374a1d55.json')
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
graph.zoom(2);
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight - 100);
|
||||
};
|
@ -1,12 +0,0 @@
|
||||
---
|
||||
title: Fit View with Shortcuts
|
||||
order: 15
|
||||
---
|
||||
|
||||
Allow the end-user to call a function of Graph with shortcuts keys.
|
||||
|
||||
允许终端用户使用键盘组合键调用 Graph 的函数。
|
||||
|
||||
## Usage
|
||||
|
||||
E.g. press down 'control' and '1' on keyboard to make the graph fit the canvas. Attention: make sure the focus is on the canvas when the end-user is pressing keys to call the function. For more information, please refer to [shortcuts-call](/en/docs/manual/middle/states/defaultBehavior/shortcuts-call).
|
@ -1,10 +0,0 @@
|
||||
---
|
||||
title: 快捷键适应画布
|
||||
order: 15
|
||||
---
|
||||
|
||||
允许终端用户使用键盘组合键调用 Graph 的函数。
|
||||
|
||||
## 如何使用
|
||||
|
||||
例如按下键盘上的 control 与 1,对图进行适应画布。注意:终端用户使用该功能时焦点必须在画布上才能够正确触发。详细内容参考文档 [shortcuts-call](/zh/docs/manual/middle/states/defaultBehavior/shortcuts-call)。
|
@ -1,5 +1,4 @@
|
||||
import G6 from '@antv/g6';
|
||||
import insertCss from 'insert-css';
|
||||
import { Graph, extend, Extensions } from '@antv/g6';
|
||||
|
||||
insertCss(`
|
||||
.g6-component-tooltip {
|
||||
@ -13,89 +12,78 @@ insertCss(`
|
||||
}
|
||||
`);
|
||||
|
||||
const tooltip = new G6.Tooltip({
|
||||
offsetX: 10,
|
||||
offsetY: 10,
|
||||
fixToNode: [1, 0.5],
|
||||
// the types of items that allow the tooltip show up
|
||||
// 允许出现 tooltip 的 item 类型
|
||||
itemTypes: ['node', 'edge'],
|
||||
// custom the tooltip's content
|
||||
// 自定义 tooltip 内容
|
||||
getContent: (e) => {
|
||||
const outDiv = document.createElement('div');
|
||||
outDiv.style.width = 'fit-content';
|
||||
outDiv.style.height = 'fit-content';
|
||||
const model = e.item.getModel();
|
||||
if (e.item.getType() === 'node') {
|
||||
outDiv.innerHTML = `${model.name}`;
|
||||
} else {
|
||||
const source = e.item.getSource();
|
||||
const target = e.item.getTarget();
|
||||
outDiv.innerHTML = `来源:${source.getModel().name}<br/>去向:${target.getModel().name}`;
|
||||
}
|
||||
return outDiv;
|
||||
},
|
||||
});
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
layout: {
|
||||
type: 'force',
|
||||
edgeStrength: 0.7,
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
plugins: {
|
||||
tooltip: Extensions.Tooltip,
|
||||
},
|
||||
plugins: [tooltip],
|
||||
modes: {
|
||||
default: ['drag-canvas', 'activate-relations'],
|
||||
behaviors: {
|
||||
'activate-relations': Extensions.ActivateRelations,
|
||||
},
|
||||
defaultNode: {
|
||||
size: [10, 10],
|
||||
/* style for the keyShape */
|
||||
// style: {
|
||||
// lineWidth: 2,
|
||||
// fill: '#DEE9FF',
|
||||
// stroke: '#5B8FF9',
|
||||
// },
|
||||
},
|
||||
defaultEdge: {
|
||||
/* style for the keyShape */
|
||||
style: {
|
||||
stroke: '#aaa',
|
||||
lineAppendWidth: 2,
|
||||
opacity: 0.3,
|
||||
},
|
||||
},
|
||||
/* styles for different states, there are built-in styles for states: active, inactive, selected, highlight, disable */
|
||||
// nodeStateStyles: {
|
||||
// active: {
|
||||
// opacity: 1,
|
||||
// },
|
||||
// inactive: {
|
||||
// opacity: 0.2,
|
||||
// },
|
||||
// },
|
||||
// edgeStateStyles: {
|
||||
// active: {
|
||||
// stroke: '#999',
|
||||
// },
|
||||
// },
|
||||
});
|
||||
|
||||
fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/xiaomi.json')
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
});
|
||||
console.log('data', data);
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
autoFit: 'view',
|
||||
layout: {
|
||||
type: 'force',
|
||||
preventOverlap: true,
|
||||
nodeSize: 32,
|
||||
workerEnabled: true,
|
||||
},
|
||||
plugins: [
|
||||
{
|
||||
key: 'tooltip1',
|
||||
type: 'tooltip',
|
||||
trigger: 'pointerenter',
|
||||
getContent: (e) => {
|
||||
let innerHTML;
|
||||
const getNodeNameById = (id) => data.nodes.find((node) => node.id === id).name;
|
||||
if (e.itemType === 'node') {
|
||||
innerHTML = getNodeNameById(e.itemId);
|
||||
} else {
|
||||
const { source, target } = data.edges.find((edge) => edge.id === e.itemId);
|
||||
innerHTML = `来源:${getNodeNameById(source)}<br/>去向:${getNodeNameById(target)}`;
|
||||
}
|
||||
return `
|
||||
<div class='g6-component-tooltip'>
|
||||
${innerHTML}
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
},
|
||||
],
|
||||
modes: {
|
||||
default: [{ type: 'activate-relations', trigger: 'pointerenter' }, 'drag-canvas'],
|
||||
},
|
||||
data,
|
||||
node: {
|
||||
keyShape: {
|
||||
r: 10,
|
||||
},
|
||||
},
|
||||
edge: {
|
||||
keyShape: {
|
||||
stroke: '#aaa',
|
||||
lineAppendWidth: 2,
|
||||
opacity: 0.3,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight);
|
||||
};
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
});
|
||||
|
@ -1,5 +1,4 @@
|
||||
import G6 from '@antv/g6';
|
||||
import insertCss from 'insert-css';
|
||||
import { Graph, extend, Extensions } from '@antv/g6';
|
||||
|
||||
insertCss(`
|
||||
.g6-component-tooltip {
|
||||
@ -13,135 +12,144 @@ insertCss(`
|
||||
}
|
||||
`);
|
||||
|
||||
const tooltip = new G6.Tooltip({
|
||||
offsetX: 10,
|
||||
offsetY: 10,
|
||||
fixToNode: [1, 0.5],
|
||||
// the types of items that allow the tooltip show up
|
||||
// 允许出现 tooltip 的 item 类型
|
||||
itemTypes: ['node', 'edge'],
|
||||
// custom the tooltip's content
|
||||
// 自定义 tooltip 内容
|
||||
getContent: (e) => {
|
||||
const outDiv = document.createElement('div');
|
||||
outDiv.style.width = 'fit-content';
|
||||
outDiv.style.height = 'fit-content';
|
||||
const model = e.item.getModel();
|
||||
if (e.item.getType() === 'node') {
|
||||
outDiv.innerHTML = `${model.name}`;
|
||||
} else {
|
||||
const source = e.item.getSource();
|
||||
const target = e.item.getTarget();
|
||||
outDiv.innerHTML = `来源:${source.getModel().name}<br/>去向:${target.getModel().name}`;
|
||||
}
|
||||
return outDiv;
|
||||
},
|
||||
});
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
plugins: [tooltip],
|
||||
layout: {
|
||||
type: 'force',
|
||||
edgeStrength: 0.7,
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
plugins: {
|
||||
tooltip: Extensions.Tooltip,
|
||||
},
|
||||
modes: {
|
||||
default: ['drag-canvas'],
|
||||
},
|
||||
defaultNode: {
|
||||
size: [10, 10],
|
||||
style: {
|
||||
lineWidth: 2,
|
||||
fill: '#DEE9FF',
|
||||
stroke: '#5B8FF9',
|
||||
},
|
||||
},
|
||||
defaultEdge: {
|
||||
size: 1,
|
||||
style: {
|
||||
stroke: '#e2e2e2',
|
||||
lineAppendWidth: 2,
|
||||
},
|
||||
},
|
||||
nodeStateStyles: {
|
||||
highlight: {
|
||||
opacity: 1,
|
||||
},
|
||||
dark: {
|
||||
opacity: 0.2,
|
||||
},
|
||||
},
|
||||
edgeStateStyles: {
|
||||
highlight: {
|
||||
stroke: '#999',
|
||||
},
|
||||
behaviors: {
|
||||
'activate-relations': Extensions.ActivateRelations,
|
||||
},
|
||||
});
|
||||
|
||||
function clearAllStats() {
|
||||
graph.setAutoPaint(false);
|
||||
graph.getNodes().forEach(function (node) {
|
||||
graph.clearItemStates(node);
|
||||
});
|
||||
graph.getEdges().forEach(function (edge) {
|
||||
graph.clearItemStates(edge);
|
||||
});
|
||||
graph.paint();
|
||||
graph.setAutoPaint(true);
|
||||
}
|
||||
|
||||
graph.on('node:mouseenter', function (e) {
|
||||
const item = e.item;
|
||||
graph.setAutoPaint(false);
|
||||
graph.getNodes().forEach(function (node) {
|
||||
graph.clearItemStates(node);
|
||||
graph.setItemState(node, 'dark', true);
|
||||
});
|
||||
graph.setItemState(item, 'dark', false);
|
||||
graph.setItemState(item, 'highlight', true);
|
||||
graph.getEdges().forEach(function (edge) {
|
||||
if (edge.getSource() === item) {
|
||||
graph.setItemState(edge.getTarget(), 'dark', false);
|
||||
graph.setItemState(edge.getTarget(), 'highlight', true);
|
||||
graph.setItemState(edge, 'highlight', true);
|
||||
edge.toFront();
|
||||
} else if (edge.getTarget() === item) {
|
||||
graph.setItemState(edge.getSource(), 'dark', false);
|
||||
graph.setItemState(edge.getSource(), 'highlight', true);
|
||||
graph.setItemState(edge, 'highlight', true);
|
||||
edge.toFront();
|
||||
} else {
|
||||
graph.setItemState(edge, 'highlight', false);
|
||||
}
|
||||
});
|
||||
graph.paint();
|
||||
graph.setAutoPaint(true);
|
||||
});
|
||||
graph.on('node:mouseleave', clearAllStats);
|
||||
graph.on('canvas:click', clearAllStats);
|
||||
|
||||
fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/xiaomi.json')
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
graph.data({
|
||||
nodes: data.nodes,
|
||||
edges: data.edges.map(function (edge, i) {
|
||||
edge.id = 'edge' + i;
|
||||
return Object.assign({}, edge);
|
||||
}),
|
||||
console.log('data', data);
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
autoFit: 'view',
|
||||
layout: {
|
||||
type: 'force',
|
||||
preventOverlap: true,
|
||||
nodeSize: 32,
|
||||
workerEnabled: true,
|
||||
},
|
||||
plugins: [
|
||||
{
|
||||
key: 'tooltip1',
|
||||
type: 'tooltip',
|
||||
trigger: 'pointerenter',
|
||||
getContent: (e) => {
|
||||
let innerHTML;
|
||||
const getNodeNameById = (id) => data.nodes.find((node) => node.id === id).name;
|
||||
if (e.itemType === 'node') {
|
||||
innerHTML = getNodeNameById(e.itemId);
|
||||
} else {
|
||||
const { source, target } = data.edges.find((edge) => edge.id === e.itemId);
|
||||
innerHTML = `来源:${getNodeNameById(source)}<br/>去向:${getNodeNameById(target)}`;
|
||||
}
|
||||
return `
|
||||
<div class='g6-component-tooltip'>
|
||||
${innerHTML}
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
},
|
||||
],
|
||||
modes: {
|
||||
default: ['drag-canvas'],
|
||||
},
|
||||
data,
|
||||
node: {
|
||||
keyShape: {
|
||||
r: 10,
|
||||
},
|
||||
highlight: {
|
||||
keyShape: {
|
||||
fill: '#0f0',
|
||||
},
|
||||
},
|
||||
dark: {
|
||||
keyShape: {
|
||||
opacity: 0.2,
|
||||
},
|
||||
},
|
||||
},
|
||||
nodeState: {
|
||||
highlight: {
|
||||
keyShape: {
|
||||
opacity: 1,
|
||||
fill: '#0f0',
|
||||
},
|
||||
},
|
||||
dark: {
|
||||
keyShape: {
|
||||
opacity: 0.2,
|
||||
},
|
||||
},
|
||||
},
|
||||
edge: {
|
||||
keyShape: {
|
||||
stroke: '#aaa',
|
||||
lineAppendWidth: 2,
|
||||
opacity: 0.5,
|
||||
},
|
||||
},
|
||||
edgeState: {
|
||||
highlight: {
|
||||
keyShape: {
|
||||
stroke: '#999',
|
||||
},
|
||||
},
|
||||
dark: {
|
||||
keyshape: {
|
||||
stroke: '#aaa',
|
||||
opacity: 0.3,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
graph.render();
|
||||
});
|
||||
graph.on('node:pointerenter', function (e) {
|
||||
const item = e.itemId;
|
||||
const allNodesId = graph.getAllNodesData().map((node) => node.id);
|
||||
graph.clearItemState(allNodesId);
|
||||
graph.setItemState(allNodesId, 'dark', true);
|
||||
graph.setItemState(item, 'dark', false);
|
||||
graph.setItemState(item, 'highlight', true);
|
||||
graph.getAllEdgesData().forEach(function (edge) {
|
||||
const sourceId = edge.source;
|
||||
const targetId = edge.target;
|
||||
if (sourceId === item) {
|
||||
graph.setItemState(targetId, 'dark', false);
|
||||
graph.setItemState(targetId, 'highlight', true);
|
||||
graph.setItemState(edge.id, 'highlight', true);
|
||||
} else if (targetId === item) {
|
||||
graph.setItemState(sourceId, 'dark', false);
|
||||
graph.setItemState(sourceId, 'highlight', true);
|
||||
graph.setItemState(edge.id, 'highlight', true);
|
||||
} else {
|
||||
graph.setItemState(edge.id, 'highlight', false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight);
|
||||
};
|
||||
graph.on('node:pointerleave', function (e) {
|
||||
const allNodesIds = graph.getAllNodesData().map((node) => node.id);
|
||||
const allEdgesIds = graph.getAllEdgesData().map((edge) => edge.id);
|
||||
graph.clearItemState([...allNodesIds, ...allEdgesIds]);
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
});
|
||||
|
@ -1,132 +1,161 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Graph, Extensions, extend } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: '1',
|
||||
label: '公司1',
|
||||
group: 1,
|
||||
data: {
|
||||
label: '公司1',
|
||||
group: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
label: '公司2',
|
||||
group: 1,
|
||||
data: {
|
||||
label: '公司2',
|
||||
group: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
label: '公司3',
|
||||
group: 1,
|
||||
data: {
|
||||
label: '公司3',
|
||||
group: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
label: '公司4',
|
||||
group: 1,
|
||||
data: {
|
||||
label: '公司4',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
label: '公司5',
|
||||
group: 2,
|
||||
data: {
|
||||
label: '公司5',
|
||||
group: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
label: '公司6',
|
||||
group: 2,
|
||||
data: {
|
||||
label: '公司6',
|
||||
group: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
label: '公司7',
|
||||
group: 2,
|
||||
data: {
|
||||
label: '公司7',
|
||||
group: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
label: '公司8',
|
||||
group: 2,
|
||||
data: {
|
||||
label: '公司8',
|
||||
group: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '9',
|
||||
label: '公司9',
|
||||
group: 2,
|
||||
data: {
|
||||
label: '公司9',
|
||||
group: 2,
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: '1',
|
||||
target: '1',
|
||||
type: 'loop',
|
||||
data: { type: 'loop-edge' },
|
||||
},
|
||||
{
|
||||
id: 'edge2',
|
||||
source: '2',
|
||||
target: '2',
|
||||
type: 'loop',
|
||||
data: {
|
||||
type: 'loop-edge',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge3',
|
||||
source: '1',
|
||||
target: '2',
|
||||
data: {
|
||||
type: 'A',
|
||||
dataType: 'A',
|
||||
amount: '100,000 元',
|
||||
date: '2019-08-03',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge4',
|
||||
source: '1',
|
||||
target: '3',
|
||||
data: {
|
||||
type: 'B',
|
||||
dataType: 'B',
|
||||
amount: '100,000 元',
|
||||
date: '2019-08-03',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge5',
|
||||
source: '2',
|
||||
target: '5',
|
||||
data: {
|
||||
type: 'C',
|
||||
dataType: 'C',
|
||||
amount: '100,000 元',
|
||||
date: '2019-08-03',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge6',
|
||||
source: '5',
|
||||
target: '6',
|
||||
data: {
|
||||
type: 'B',
|
||||
dataType: 'B',
|
||||
amount: '100,000 元',
|
||||
date: '2019-08-03',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge7',
|
||||
source: '3',
|
||||
target: '4',
|
||||
data: {
|
||||
type: 'C',
|
||||
dataType: 'C',
|
||||
amount: '100,000 元',
|
||||
date: '2019-08-03',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge8',
|
||||
source: '4',
|
||||
target: '7',
|
||||
data: {
|
||||
type: 'B',
|
||||
dataType: 'B',
|
||||
amount: '100,000 元',
|
||||
date: '2019-08-03',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge9',
|
||||
source: '1',
|
||||
target: '8',
|
||||
data: {
|
||||
type: 'B',
|
||||
dataType: 'B',
|
||||
amount: '100,000 元',
|
||||
date: '2019-08-03',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge10',
|
||||
source: '1',
|
||||
target: '9',
|
||||
data: {
|
||||
type: 'C',
|
||||
dataType: 'C',
|
||||
amount: '100,000 元',
|
||||
date: '2019-08-03',
|
||||
},
|
||||
@ -137,76 +166,114 @@ const data = {
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
|
||||
const hullPlugin = new Extensions.Hull({
|
||||
key: 'hull-plugin',
|
||||
});
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
edges: {
|
||||
'loop-edge': Extensions.LoopEdge,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
plugins: [hullPlugin],
|
||||
modes: {
|
||||
default: ['drag-canvas', 'zoom-canvas', 'drag-node'],
|
||||
},
|
||||
fitView: true,
|
||||
autoFit: 'view',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
data,
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
const hull1 = graph.createHull({
|
||||
hullPlugin.addHull({
|
||||
id: 'hull1',
|
||||
type: 'smooth-convex',
|
||||
padding: 15,
|
||||
members: graph.getNodes().filter((node) => node.getModel().group === 1),
|
||||
type: 'smooth-convex', //'bubble' | 'round-convex' | 'smooth-convex';
|
||||
members: graph
|
||||
.getAllNodesData()
|
||||
.filter((model) => model.data.group === 1)
|
||||
.map((node) => node.id),
|
||||
labelShape: {
|
||||
text: 'Group1',
|
||||
position: 'left',
|
||||
offsetY: -2,
|
||||
},
|
||||
});
|
||||
const hull2 = graph.createHull({
|
||||
|
||||
hullPlugin.addHull({
|
||||
id: 'hull2',
|
||||
members: graph.getNodes().filter((node) => node.getModel().group === 2),
|
||||
padding: 15,
|
||||
type: 'bubble',
|
||||
type: 'round-convex',
|
||||
members: graph
|
||||
.getAllNodesData()
|
||||
.filter((model) => model.data.group === 2)
|
||||
.map((node) => node.id),
|
||||
labelShape: {
|
||||
text: 'Group2',
|
||||
position: 'left',
|
||||
offsetY: -2,
|
||||
},
|
||||
style: {
|
||||
fill: 'pink',
|
||||
stroke: 'red',
|
||||
},
|
||||
update: 'drag',
|
||||
});
|
||||
|
||||
graph.on('canvas:contextmenu', (ev) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
const item = graph.addItem('node', {
|
||||
x: ev.x,
|
||||
y: ev.y,
|
||||
id: Math.random(),
|
||||
group: 2,
|
||||
let memberAdded = false;
|
||||
let nonMemberAdded = false;
|
||||
|
||||
const updateActions = [
|
||||
{
|
||||
name: 'Add/Delete Member',
|
||||
action: () => {
|
||||
if (!memberAdded) {
|
||||
hullPlugin.addHullMember('hull1', ['4']);
|
||||
} else {
|
||||
hullPlugin.removeHullMember('hull1', ['4']);
|
||||
}
|
||||
memberAdded = !memberAdded;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Update Config',
|
||||
action: () => {
|
||||
hullPlugin.updateHull({
|
||||
id: 'hull1',
|
||||
style: { fill: '#ff0' },
|
||||
labelShape: { text: 'updated-label', position: 'top' },
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const btnContainer = document.createElement('div');
|
||||
btnContainer.style.position = 'absolute';
|
||||
container.appendChild(btnContainer);
|
||||
const tip = document.createElement('span');
|
||||
tip.innerHTML = `👉 update:`;
|
||||
btnContainer.appendChild(tip);
|
||||
updateActions.forEach((item, i) => {
|
||||
const btn = document.createElement('a');
|
||||
btn.innerHTML = item.name;
|
||||
btn.style.backgroundColor = 'rgba(255, 255, 255, 0.8)';
|
||||
btn.style.padding = '4px';
|
||||
btn.style.marginLeft = i > 0 ? '24px' : '8px';
|
||||
btnContainer.appendChild(btn);
|
||||
btn.addEventListener('click', () => {
|
||||
item.action();
|
||||
});
|
||||
hull2.addMember(item);
|
||||
});
|
||||
|
||||
graph.on('afterupdateitem', (e) => {
|
||||
if (hull1.members.indexOf(e.item) > -1 || hull1.nonMembers.indexOf(e.item) > -1) {
|
||||
hull1.updateData(hull1.members);
|
||||
}
|
||||
});
|
||||
|
||||
graph.on('node:dragend', (e) => {
|
||||
const item = e.item;
|
||||
const memberIdx = hull2.members.indexOf(item);
|
||||
if (memberIdx > -1) {
|
||||
// 如果移出原hull范围,则去掉
|
||||
if (!hull2.contain(item)) {
|
||||
hull2.removeMember(item);
|
||||
} else {
|
||||
hull2.updateData(hull2.members);
|
||||
}
|
||||
} else {
|
||||
if (hull2.contain(item)) hull2.addMember(item);
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,46 +1,44 @@
|
||||
import G6 from '@antv/g6';
|
||||
import G6, { Extensions } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{ id: 'node0', size: 50 },
|
||||
{ id: 'node1', size: 30 },
|
||||
{ id: 'node2', size: 30 },
|
||||
{ id: 'node3', size: 30 },
|
||||
{ id: 'node4', size: 30, isLeaf: true },
|
||||
{ id: 'node5', size: 30, isLeaf: true },
|
||||
{ id: 'node6', size: 15, isLeaf: true },
|
||||
{ id: 'node7', size: 15, isLeaf: true },
|
||||
{ id: 'node8', size: 15, isLeaf: true },
|
||||
{ id: 'node9', size: 15, isLeaf: true },
|
||||
{ id: 'node10', size: 15, isLeaf: true },
|
||||
{ id: 'node11', size: 15, isLeaf: true },
|
||||
{ id: 'node12', size: 15, isLeaf: true },
|
||||
{ id: 'node13', size: 15, isLeaf: true },
|
||||
{ id: 'node14', size: 15, isLeaf: true },
|
||||
{ id: 'node15', size: 15, isLeaf: true },
|
||||
{ id: 'node16', size: 15, isLeaf: true },
|
||||
{ id: 'node0', data: { size: 50 } },
|
||||
{ id: 'node1', data: { size: 30 } },
|
||||
{ id: 'node2', data: { size: 30 } },
|
||||
{ id: 'node3', data: { size: 30 } },
|
||||
{ id: 'node4', data: { size: 30, isLeaf: true } },
|
||||
{ id: 'node5', data: { size: 30, isLeaf: true } },
|
||||
{ id: 'node6', data: { size: 15, isLeaf: true } },
|
||||
{ id: 'node7', data: { size: 15, isLeaf: true } },
|
||||
{ id: 'node8', data: { size: 15, isLeaf: true } },
|
||||
{ id: 'node9', data: { size: 15, isLeaf: true } },
|
||||
{ id: 'node10', data: { size: 15, isLeaf: true } },
|
||||
{ id: 'node11', data: { size: 15, isLeaf: true } },
|
||||
{ id: 'node12', data: { size: 15, isLeaf: true } },
|
||||
{ id: 'node13', data: { size: 15, isLeaf: true } },
|
||||
{ id: 'node14', data: { size: 15, isLeaf: true } },
|
||||
{ id: 'node15', data: { size: 15, isLeaf: true } },
|
||||
{ id: 'node16', data: { size: 15, isLeaf: true } },
|
||||
],
|
||||
edges: [
|
||||
{ source: 'node0', target: 'node1' },
|
||||
{ source: 'node0', target: 'node2' },
|
||||
{ source: 'node0', target: 'node3' },
|
||||
{ source: 'node0', target: 'node4' },
|
||||
{ source: 'node0', target: 'node5' },
|
||||
{ source: 'node1', target: 'node6' },
|
||||
{ source: 'node1', target: 'node7' },
|
||||
{ source: 'node2', target: 'node8' },
|
||||
{ source: 'node2', target: 'node9' },
|
||||
{ source: 'node2', target: 'node10' },
|
||||
{ source: 'node2', target: 'node11' },
|
||||
{ source: 'node2', target: 'node12' },
|
||||
{ source: 'node2', target: 'node13' },
|
||||
{ source: 'node3', target: 'node14' },
|
||||
{ source: 'node3', target: 'node15' },
|
||||
{ source: 'node3', target: 'node16' },
|
||||
{ id: 'edge1', source: 'node0', target: 'node1' },
|
||||
{ id: 'edge2', source: 'node0', target: 'node2' },
|
||||
{ id: 'edge3', source: 'node0', target: 'node3' },
|
||||
{ id: 'edge4', source: 'node0', target: 'node4' },
|
||||
{ id: 'edge5', source: 'node0', target: 'node5' },
|
||||
{ id: 'edge6', source: 'node1', target: 'node6' },
|
||||
{ id: 'edge7', source: 'node1', target: 'node7' },
|
||||
{ id: 'edge8', source: 'node2', target: 'node8' },
|
||||
{ id: 'edge9', source: 'node2', target: 'node9' },
|
||||
{ id: 'edge10', source: 'node2', target: 'node10' },
|
||||
{ id: 'edge11', source: 'node2', target: 'node11' },
|
||||
{ id: 'edge12', source: 'node2', target: 'node12' },
|
||||
{ id: 'edge13', source: 'node2', target: 'node13' },
|
||||
{ id: 'edge14', source: 'node3', target: 'node14' },
|
||||
{ id: 'edge15', source: 'node3', target: 'node15' },
|
||||
{ id: 'edge16', source: 'node3', target: 'node16' },
|
||||
],
|
||||
};
|
||||
const nodes = data.nodes;
|
||||
|
||||
const descriptionDiv = document.createElement('div');
|
||||
descriptionDiv.innerHTML = 'Wait for the layout to complete...';
|
||||
const container = document.getElementById('container');
|
||||
@ -49,86 +47,91 @@ container.appendChild(descriptionDiv);
|
||||
const width = container.scrollWidth;
|
||||
const height = (container.scrollHeight || 500) - 20;
|
||||
|
||||
const hullPlugin = new Extensions.Hull({
|
||||
key: 'hull-plugin1',
|
||||
});
|
||||
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
plugins: [hullPlugin],
|
||||
modes: {
|
||||
default: ['drag-canvas', 'zoom-canvas', 'drag-node', 'lasso-select'],
|
||||
},
|
||||
layout: {
|
||||
type: 'force',
|
||||
preventOverlap: true,
|
||||
workerEnabled: true,
|
||||
linkDistance: (d) => {
|
||||
if (d.source.id === 'node0') {
|
||||
if (d.source === 'node0') {
|
||||
return 300;
|
||||
}
|
||||
return 60;
|
||||
},
|
||||
nodeStrength: (d) => {
|
||||
if (d.isLeaf) {
|
||||
if (d.data.isLeaf) {
|
||||
return -50;
|
||||
}
|
||||
return -10;
|
||||
},
|
||||
edgeStrength: (d) => {
|
||||
if (d.source.id === 'node1' || d.source.id === 'node2' || d.source.id === 'node3') {
|
||||
if (d.source === 'node1' || d.source === 'node2' || d.source === 'node3') {
|
||||
return 0.7;
|
||||
}
|
||||
return 0.1;
|
||||
},
|
||||
},
|
||||
data,
|
||||
node: {
|
||||
keyShape: {
|
||||
r: {
|
||||
fields: ['size'],
|
||||
formatter: (model) => model.data.size,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
graph.data({
|
||||
nodes,
|
||||
edges: data.edges.map(function (edge, i) {
|
||||
edge['id'] = 'edge' + i;
|
||||
return Object.assign({}, edge);
|
||||
}),
|
||||
});
|
||||
graph.render();
|
||||
|
||||
let centerNodes = graph.getNodes().filter((node) => !node.getModel().isLeaf);
|
||||
let centerNodes = graph
|
||||
.getAllNodesData()
|
||||
.filter((model) => !model.data.isLeaf)
|
||||
.map((node) => node.id);
|
||||
|
||||
graph.on('afterlayout', () => {
|
||||
descriptionDiv.innerHTML = '';
|
||||
const hull1 = graph.createHull({
|
||||
hullPlugin.addHull({
|
||||
id: 'centerNode-hull',
|
||||
type: 'bubble',
|
||||
members: centerNodes,
|
||||
padding: 10,
|
||||
labelShape: {
|
||||
text: 'centerNode-hull',
|
||||
position: 'left',
|
||||
offsetY: -2,
|
||||
},
|
||||
});
|
||||
|
||||
const hull2 = graph.createHull({
|
||||
hullPlugin.addHull({
|
||||
id: 'leafNode-hull1',
|
||||
members: ['node6', 'node7'],
|
||||
padding: 10,
|
||||
style: {
|
||||
fill: 'lightgreen',
|
||||
stroke: 'green',
|
||||
},
|
||||
});
|
||||
|
||||
const hull3 = graph.createHull({
|
||||
hullPlugin.addHull({
|
||||
id: 'leafNode-hull2',
|
||||
members: ['node8', 'node9', 'node10', 'node11', 'node12', 'node13'],
|
||||
padding: 10,
|
||||
style: {
|
||||
fill: 'lightgreen',
|
||||
stroke: 'green',
|
||||
},
|
||||
});
|
||||
|
||||
graph.on('afterupdateitem', (e) => {
|
||||
hull1.updateData(hull1.members);
|
||||
hull2.updateData(hull2.members);
|
||||
hull3.updateData(hull3.members);
|
||||
});
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight - 20);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,114 +1,108 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Graph } from '@antv/g6';
|
||||
|
||||
/**
|
||||
* 本示例演示以下功能:
|
||||
* 1、如何使用图片作为节点背景;
|
||||
* 2、点击切换节点背景图片。
|
||||
*
|
||||
*/
|
||||
|
||||
const img = new Image();
|
||||
img.src = 'https://gw.alipayobjects.com/os/s/prod/antv/assets/image/logo-with-text-73b8a.svg';
|
||||
|
||||
// 点击图片节点,切换背景图片
|
||||
const img2 = new Image();
|
||||
img2.src = 'https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*wAmHQJbNVdwAAAAAAAAAAABkARQnAQ';
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
x: 150,
|
||||
y: 100,
|
||||
type: 'circleNode',
|
||||
label: 'circle',
|
||||
id: 'node1',
|
||||
labelCfg: {
|
||||
position: 'center',
|
||||
id: 'circle',
|
||||
data: {
|
||||
x: 100,
|
||||
y: 150,
|
||||
labelShape: {
|
||||
position: 'center',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
x: 350,
|
||||
y: 100,
|
||||
type: 'image',
|
||||
id: 'node2',
|
||||
img: img.src,
|
||||
size: [120, 60],
|
||||
label: 'avatar',
|
||||
style: {
|
||||
cursor: 'pointer',
|
||||
},
|
||||
labelCfg: {
|
||||
position: 'bottom',
|
||||
id: 'avatar',
|
||||
data: {
|
||||
x: 350,
|
||||
y: 100,
|
||||
labelShape: {
|
||||
text: 'label before been hovered',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge-1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
label: 'line',
|
||||
labelCfg: {
|
||||
refY: 10,
|
||||
data: {
|
||||
labelShape: {
|
||||
text: 'label before been hovered',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
// 避免拖动过程中闪烁:使用加载已经LOAD好的图片
|
||||
img.onload = function () {
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
defaultNode: {
|
||||
style: {
|
||||
fill: '#DEE9FF',
|
||||
stroke: '#5B8FF9',
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const descriptionDiv = document.createElement('div');
|
||||
descriptionDiv.innerHTML =
|
||||
'Mouse hover over node to update node style and its label text. <br/> Mouse hover over edge to update edge style and its label text';
|
||||
container.appendChild(descriptionDiv);
|
||||
|
||||
const graph = new Graph({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
modes: {
|
||||
default: ['drag-node'],
|
||||
},
|
||||
node: {
|
||||
keyShape: {
|
||||
r: {
|
||||
fields: ['size'],
|
||||
formatter: (model) => model.data.size / 2,
|
||||
},
|
||||
},
|
||||
defaultEdge: {
|
||||
color: '#e2e2e2',
|
||||
labelShape: {
|
||||
position: 'center',
|
||||
text: {
|
||||
fields: ['label'],
|
||||
formatter: (model) => model.data.label,
|
||||
},
|
||||
},
|
||||
modes: {
|
||||
default: [
|
||||
'drag-node',
|
||||
{
|
||||
type: 'drag-node',
|
||||
},
|
||||
],
|
||||
labelBackgroundShape: {},
|
||||
otherShapes: {},
|
||||
},
|
||||
data,
|
||||
});
|
||||
|
||||
// 节点上的点击事件
|
||||
graph.on('node:pointerenter', (event) => {
|
||||
const { itemId } = event;
|
||||
graph.updateData('node', {
|
||||
id: itemId,
|
||||
data: {
|
||||
labelShape: {
|
||||
text: `after been hovered ${itemId}`,
|
||||
fill: '#003a8c',
|
||||
},
|
||||
},
|
||||
});
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
});
|
||||
|
||||
graph.on('node:click', function (evt) {
|
||||
const target = evt.target;
|
||||
|
||||
const type = target.get('type');
|
||||
const hasChangeBg = target.get('hasChangeBg');
|
||||
if (type === 'image') {
|
||||
if (!hasChangeBg) {
|
||||
// 点击图片节点时,切换背景图片
|
||||
target.attr('img', img2);
|
||||
target.attr('imgSrc', 'http://seopic.699pic.com/photo/50055/5642.jpg_wh1200.jpg');
|
||||
target.set('hasChangeBg', true);
|
||||
} else {
|
||||
target.attr('img', img);
|
||||
target.attr(
|
||||
'imgSrc',
|
||||
'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1566553535233&di=b0b17eeea7bd7356a6f42ebfd48e9441&imgtype=0&src=http%3A%2F%2Fa2.att.hudong.com%2F64%2F29%2F01300543361379145388299988437_s.jpg',
|
||||
);
|
||||
target.set('hasChangeBg', false);
|
||||
}
|
||||
graph.paint();
|
||||
}
|
||||
graph.on('node:pointerleave', (event) => {
|
||||
const { itemId } = event;
|
||||
graph.updateData('node', {
|
||||
id: itemId,
|
||||
data: {
|
||||
labelShape: {
|
||||
text: 'label before been hovered',
|
||||
fill: '#000',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight);
|
||||
};
|
||||
};
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -11,14 +11,6 @@
|
||||
"en": "Update the Label"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*NJm8S4sfgp4AAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "changeImg.js",
|
||||
"title": {
|
||||
"zh": "切换节点背景图片",
|
||||
"en": "Change the Image"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*kwp1S6PJIVUAAAAAAAAAAABkARQnAQ"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,33 +1,31 @@
|
||||
import G6 from '@antv/g6';
|
||||
/**
|
||||
* 本示例演示以下功能:
|
||||
* 鼠标 hover 节点更新节点样式及其标签文本
|
||||
* 鼠标 hover 边更新边样式及其标签文本
|
||||
* by 十吾
|
||||
*/
|
||||
import { Graph } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
x: 100,
|
||||
y: 100,
|
||||
label: 'label before\nbeen hovered',
|
||||
data: {
|
||||
x: 100,
|
||||
y: 150,
|
||||
label: 'label before been hovered',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
x: 400,
|
||||
y: 100,
|
||||
label: 'label before\nbeen hovered',
|
||||
data: {
|
||||
x: 300,
|
||||
y: 150,
|
||||
label: 'label before been hovered',
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
label: 'label before\nbeen hovered',
|
||||
labelCfg: {
|
||||
refY: 10,
|
||||
data: {
|
||||
label: 'label before been hovered',
|
||||
},
|
||||
},
|
||||
],
|
||||
@ -36,75 +34,97 @@ const data = {
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
|
||||
const graph = new Graph({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
defaultEdge: {
|
||||
color: '#e2e2e2',
|
||||
lineAppendWidth: 3,
|
||||
modes: {
|
||||
default: ['drag-node'],
|
||||
},
|
||||
data,
|
||||
node: {
|
||||
labelShape: {
|
||||
position: 'center',
|
||||
text: {
|
||||
fields: ['label'],
|
||||
formatter: (model) => model.data.label,
|
||||
},
|
||||
maxLines: 4,
|
||||
},
|
||||
},
|
||||
edge: {
|
||||
labelShape: {
|
||||
text: {
|
||||
fields: ['label'],
|
||||
formatter: (model) => model.data.label,
|
||||
},
|
||||
maxLines: 2,
|
||||
},
|
||||
},
|
||||
});
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('node:mouseenter', function (evt) {
|
||||
const node = evt.item;
|
||||
const model = node.getModel();
|
||||
model.oriLabel = model.label;
|
||||
graph.updateItem(node, {
|
||||
label: `after been hovered ${model.id}`,
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: '#003a8c',
|
||||
},
|
||||
// 节点上的点击事件
|
||||
graph.on('node:pointerenter', (event) => {
|
||||
const { itemId } = event;
|
||||
graph.updateData('node', {
|
||||
id: itemId,
|
||||
data: {
|
||||
label: `after been hovered ${itemId}`,
|
||||
labelShape: {
|
||||
fill: '#0f0',
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
graph.on('node:mouseleave', function (evt) {
|
||||
const node = evt.item;
|
||||
const model = node.getModel();
|
||||
graph.updateItem(node, {
|
||||
label: model.oriLabel,
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: '#555',
|
||||
},
|
||||
graph.on('edge:pointerenter', (event) => {
|
||||
const { itemId } = event;
|
||||
graph.updateData('edge', {
|
||||
id: itemId,
|
||||
data: {
|
||||
label: `after been hovered ${itemId}`,
|
||||
labelShape: {
|
||||
fill: '#0f0',
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
graph.on('edge:mouseenter', function (evt) {
|
||||
const edge = evt.item;
|
||||
const model = edge.getModel();
|
||||
model.oriLabel = model.label;
|
||||
graph.updateItem(edge, {
|
||||
label: 'after been hovered',
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: '#003a8c',
|
||||
},
|
||||
graph.on('node:pointerleave', (event) => {
|
||||
const { itemId } = event;
|
||||
graph.updateData('node', {
|
||||
id: itemId,
|
||||
data: {
|
||||
label: 'label before been hovered',
|
||||
labelShape: {
|
||||
fill: '#000',
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
graph.on('edge:mouseleave', function (evt) {
|
||||
const edge = evt.item;
|
||||
graph.setItemState(edge, 'hover', false);
|
||||
graph.updateItem(edge, {
|
||||
label: 'label before \n been hovered',
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: '#555',
|
||||
},
|
||||
graph.on('edge:pointerleave', (event) => {
|
||||
const { itemId } = event;
|
||||
graph.updateData('edge', {
|
||||
id: itemId,
|
||||
data: {
|
||||
label: 'label before been hovered',
|
||||
labelShape: {
|
||||
fill: '#000',
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const descriptionDiv = document.createElement('div');
|
||||
descriptionDiv.innerHTML =
|
||||
'Mouse hover over node to update node style and its label text. <br/> Mouse hover over edge to update edge style and its label text';
|
||||
container.appendChild(descriptionDiv);
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,39 +1,45 @@
|
||||
import G6 from '@antv/g6';
|
||||
/**
|
||||
* Focus a node
|
||||
* by Changzhe
|
||||
*/
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
x: 150,
|
||||
y: 50,
|
||||
label: 'node1',
|
||||
data: {
|
||||
x: 150,
|
||||
y: 50,
|
||||
label: 'node1',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
x: 200,
|
||||
y: 150,
|
||||
label: 'node2',
|
||||
data: {
|
||||
x: 200,
|
||||
y: 150,
|
||||
label: 'node2',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node3',
|
||||
x: 100,
|
||||
y: 150,
|
||||
label: 'node3',
|
||||
data: {
|
||||
x: 100,
|
||||
y: 150,
|
||||
label: 'node3',
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
},
|
||||
{
|
||||
id: 'edge2',
|
||||
source: 'node2',
|
||||
target: 'node3',
|
||||
},
|
||||
{
|
||||
id: 'edge3',
|
||||
source: 'node3',
|
||||
target: 'node1',
|
||||
},
|
||||
@ -43,38 +49,37 @@ const data = {
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
fitView: false,
|
||||
defaultNode: {
|
||||
style: {
|
||||
fill: '#DEE9FF',
|
||||
stroke: '#5B8FF9',
|
||||
data,
|
||||
node: {
|
||||
labelShape: {
|
||||
position: 'center',
|
||||
text: {
|
||||
fields: ['label'],
|
||||
formatter: (model) => model.data.label,
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultEdge: {
|
||||
style: {
|
||||
edge: {
|
||||
keyShape: {
|
||||
stroke: '#b5b5b5',
|
||||
},
|
||||
},
|
||||
});
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
function handleNodeClick(event) {
|
||||
const item = event.item;
|
||||
// animately move the graph to focus on the item.
|
||||
graph.focusItem(item);
|
||||
}
|
||||
|
||||
// listen to the node click event
|
||||
graph.on('node:click', handleNodeClick);
|
||||
// 节点上的点击事件
|
||||
graph.on('node:click', (event) => {
|
||||
const { itemId } = event;
|
||||
graph.focusItem(itemId);
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,39 +1,45 @@
|
||||
import G6 from '@antv/g6';
|
||||
/**
|
||||
* Focus a node with Animation
|
||||
* by Changzhe
|
||||
*/
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
x: 150,
|
||||
y: 50,
|
||||
label: 'node1',
|
||||
data: {
|
||||
x: 150,
|
||||
y: 50,
|
||||
label: 'node1',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
x: 200,
|
||||
y: 150,
|
||||
label: 'node2',
|
||||
data: {
|
||||
x: 200,
|
||||
y: 150,
|
||||
label: 'node2',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node3',
|
||||
x: 100,
|
||||
y: 150,
|
||||
label: 'node3',
|
||||
data: {
|
||||
x: 100,
|
||||
y: 150,
|
||||
label: 'node3',
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
},
|
||||
{
|
||||
id: 'edge2',
|
||||
source: 'node2',
|
||||
target: 'node3',
|
||||
},
|
||||
{
|
||||
id: 'edge3',
|
||||
source: 'node3',
|
||||
target: 'node1',
|
||||
},
|
||||
@ -43,48 +49,40 @@ const data = {
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
fitView: false,
|
||||
defaultNode: {
|
||||
style: {
|
||||
fill: '#DEE9FF',
|
||||
stroke: '#5B8FF9',
|
||||
data,
|
||||
node: {
|
||||
labelShape: {
|
||||
position: 'center',
|
||||
text: {
|
||||
fields: ['label'],
|
||||
formatter: (model) => model.data.label,
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultEdge: {
|
||||
style: {
|
||||
edge: {
|
||||
keyShape: {
|
||||
stroke: '#b5b5b5',
|
||||
},
|
||||
},
|
||||
// The global configuration for graph animation also takes effect on the focusItem
|
||||
// animate: true,
|
||||
// animateCfg: {
|
||||
// easing: 'easeCubic',
|
||||
// duration: 500
|
||||
// }
|
||||
});
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
function handleNodeClick(event) {
|
||||
const item = event.item;
|
||||
// animately move the graph to focus on the item.
|
||||
// the second parameter controlls whether move with animation, the third parameter is the animate configuration
|
||||
graph.focusItem(item, true, {
|
||||
easing: 'easeCubic',
|
||||
duration: 500,
|
||||
// 节点上的点击事件
|
||||
graph.on('node:click', (event) => {
|
||||
const { itemId } = event;
|
||||
graph.focusItem(itemId, {
|
||||
easing: 'ease-in',
|
||||
duration: 1000,
|
||||
});
|
||||
}
|
||||
|
||||
// listen to the node click event
|
||||
graph.on('node:click', handleNodeClick);
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,91 +1,83 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Graph, extend, Extensions } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{ id: 'node1', x: 150, y: 250 },
|
||||
{ id: 'node2', x: 350, y: 250 },
|
||||
{ id: 'node1', data: { x: 150, y: 250 } },
|
||||
{ id: 'node2', data: { x: 350, y: 250 } },
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
let shift = true;
|
||||
|
||||
const container = document.getElementById('container');
|
||||
|
||||
const switchDiv = document.createElement('div');
|
||||
switchDiv.innerHTML = `Press 'shift' and drag begin on empty space to brush select. Click 「HERE」 to switch trigger to \'drag\', and disable drag-canvas
|
||||
<br /> 按住 'shift' 并从画布空白处开始拖拽即可开始框选。点击「这里」将 trigger 切换为 'drag',同时关闭画布拖拽`;
|
||||
container.appendChild(switchDiv);
|
||||
|
||||
const width = container.scrollWidth;
|
||||
const height = (container.scrollHeight || 500) - 30;
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
fitCenter: true,
|
||||
modes: {
|
||||
default: ['brush-select', 'drag-node', 'drag-canvas'],
|
||||
altSelect: [
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
behaviors: {
|
||||
'hover-activate': Extensions.HoverActivate,
|
||||
'brush-select': Extensions.BrushSelect,
|
||||
},
|
||||
});
|
||||
|
||||
const modes = {};
|
||||
const ALLOWED_TRIGGERS = ['shift', 'alt', 'ctrl', 'drag', 'meta'];
|
||||
ALLOWED_TRIGGERS.forEach((trigger) => {
|
||||
const defaultBehavior = ['drag-node', 'hover-activate'];
|
||||
if (trigger === 'shift') {
|
||||
modes.default = [...defaultBehavior, 'brush-select'];
|
||||
} else {
|
||||
modes[`${trigger}Select`] = [
|
||||
...defaultBehavior,
|
||||
{
|
||||
type: 'brush-select',
|
||||
trigger: 'drag',
|
||||
trigger: trigger,
|
||||
},
|
||||
'drag-node',
|
||||
],
|
||||
},
|
||||
/* styles for different states, there are built-in styles for states: active, inactive, selected, highlight, disable. you can extend or custom it by the following lines */
|
||||
/* 不同状态下节点和边的样式,G6 提供以下状态名的默认样式:active, inactive, selected, highlight, disable。可以通过如下方式修改或者扩展全局状态样式*/
|
||||
// nodeStateStyles: {
|
||||
// selected: {
|
||||
// stroke: '#f00',
|
||||
// lineWidth: 3
|
||||
// }
|
||||
// },
|
||||
// edgeStateStyles: {
|
||||
// selected: {
|
||||
// lineWidth: 3,
|
||||
// stroke: '#f00'
|
||||
// }
|
||||
// }
|
||||
});
|
||||
|
||||
graph.on('node:mouseenter', (e) => {
|
||||
graph.setItemState(e.item, 'active', true);
|
||||
});
|
||||
|
||||
graph.on('node:mouseleave', (e) => {
|
||||
graph.setItemState(e.item, 'active', false);
|
||||
});
|
||||
|
||||
graph.on('nodeselectchange', (e) => {
|
||||
console.log(e.selectedItems, e.select);
|
||||
});
|
||||
|
||||
switchDiv.addEventListener('click', (e) => {
|
||||
shift = !shift;
|
||||
if (shift) {
|
||||
graph.setMode('default');
|
||||
switchDiv.innerHTML = `Press \'shift\' and drag begin on empty space to brush select. Click Here to switch trigger to \'drag\', and disable drag-canvas
|
||||
<br /> 按住 'shift' 并从画布空白处开始拖拽即可开始框选。点击「这里」将 trigger 切换为 'drag',同时关闭画布拖拽`;
|
||||
} else {
|
||||
graph.setMode('altSelect');
|
||||
switchDiv.innerHTML = `Press \'alt\' and drag begin on empty space to brush select. Click Here to switch trigger to key \'shift\', and enable drag-canvas
|
||||
<br /> 按住 'alt' 并从画布空白处开始拖拽即可开始框选。点击「这里」将 trigger 切换为 'shift',同时开启画布拖拽`;
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
const graph = new ExtGraph({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
autoFit: 'center',
|
||||
modes,
|
||||
data,
|
||||
});
|
||||
|
||||
const switchDiv = document.createElement('div');
|
||||
switchDiv.innerHTML = `👉 Trigger:`;
|
||||
switchDiv.style.position = 'absolute';
|
||||
switchDiv.style.zIndex = 10;
|
||||
container.appendChild(switchDiv);
|
||||
const selector = document.createElement('select');
|
||||
selector.id = 'selector';
|
||||
ALLOWED_TRIGGERS.forEach((trigger, index) => {
|
||||
const option = document.createElement('option');
|
||||
option.value = trigger;
|
||||
option.innerHTML = `${trigger}`;
|
||||
selector.appendChild(option);
|
||||
});
|
||||
switchDiv.appendChild(selector);
|
||||
|
||||
// Listen to the selector, change the graph mode when the selector is changed
|
||||
selector.addEventListener('change', (e) => {
|
||||
const value = e.target.value;
|
||||
const mode = value === 'shift' ? 'default' : `${value}Select`;
|
||||
// change the graph mode
|
||||
graph.setMode(mode);
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight - 30);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,91 +1,82 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Graph, extend, Extensions } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{ id: 'node1', x: 150, y: 250 },
|
||||
{ id: 'node2', x: 350, y: 250 },
|
||||
{ id: 'node1', data: { x: 150, y: 250 } },
|
||||
{ id: 'node2', data: { x: 350, y: 250 } },
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
let shift = true;
|
||||
|
||||
const graphDiv = document.getElementById('container');
|
||||
|
||||
const switchDiv = document.createElement('div');
|
||||
switchDiv.innerHTML = `Press \'shift\' to select multiple nodes. Click Here to switch trigger to key \'alt\'
|
||||
<br /> 按住 'shift' 可多选节点。点击「这里」将 trigger 切换为 'alt'`;
|
||||
graphDiv.appendChild(switchDiv);
|
||||
const container = document.getElementById('container');
|
||||
|
||||
const width = container.scrollWidth;
|
||||
const height = (container.scrollHeight || 500) - 30;
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
fitCenter: true,
|
||||
modes: {
|
||||
default: ['click-select', 'drag-node'],
|
||||
altSelect: [
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
behaviors: {
|
||||
'hover-activate': Extensions.HoverActivate,
|
||||
},
|
||||
});
|
||||
|
||||
const modes = {};
|
||||
const ALLOWED_TRIGGERS = ['shift', 'ctrl', 'alt', 'meta'];
|
||||
ALLOWED_TRIGGERS.forEach((trigger) => {
|
||||
const defaultBehavior = ['drag-node', 'hover-activate'];
|
||||
if (trigger === 'shift') {
|
||||
modes.default = [...defaultBehavior, 'click-select'];
|
||||
} else {
|
||||
modes[`${trigger}Select`] = [
|
||||
...defaultBehavior,
|
||||
{
|
||||
type: 'click-select',
|
||||
trigger: 'alt',
|
||||
trigger: trigger,
|
||||
},
|
||||
'drag-node',
|
||||
],
|
||||
},
|
||||
/* styles for different states, there are built-in styles for states: active, inactive, selected, highlight, disable. you can extend or custom it by the following lines */
|
||||
/* 不同状态下节点和边的样式,G6 提供以下状态名的默认样式:active, inactive, selected, highlight, disable。可以通过如下方式修改或者扩展全局状态样式*/
|
||||
// nodeStateStyles: {
|
||||
// selected: {
|
||||
// stroke: '#f00',
|
||||
// lineWidth: 3
|
||||
// }
|
||||
// },
|
||||
// edgeStateStyles: {
|
||||
// selected: {
|
||||
// lineWidth: 3,
|
||||
// stroke: '#f00'
|
||||
// }
|
||||
// }
|
||||
});
|
||||
|
||||
graph.on('node:mouseenter', (e) => {
|
||||
graph.setItemState(e.item, 'active', true);
|
||||
});
|
||||
|
||||
graph.on('node:mouseleave', (e) => {
|
||||
graph.setItemState(e.item, 'active', false);
|
||||
});
|
||||
|
||||
graph.on('nodeselectchange', (e) => {
|
||||
console.log(e.selectedItems, e.select);
|
||||
});
|
||||
|
||||
switchDiv.addEventListener('click', (e) => {
|
||||
shift = !shift;
|
||||
if (shift) {
|
||||
graph.setMode('default');
|
||||
switchDiv.innerHTML = `Press \'shift\' to select multiple nodes. Click Here to switch trigger to key \'alt\'
|
||||
<br /> 按住 'shift' 可多选节点。点击「这里」将 trigger 切换为 'alt'`;
|
||||
} else {
|
||||
graph.setMode('altSelect');
|
||||
switchDiv.innerHTML = `Press \'alt\' to select multiple nodes. Click Here to switch trigger to key \'shift\'
|
||||
<br /> 按住 'alt' 可多选节点。点击「这里」将 trigger 切换为 'shift'`;
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
const graph = new ExtGraph({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
autoFit: 'center',
|
||||
modes,
|
||||
data,
|
||||
});
|
||||
|
||||
const switchDiv = document.createElement('div');
|
||||
switchDiv.innerHTML = `👉 Trigger:`;
|
||||
switchDiv.style.position = 'absolute';
|
||||
switchDiv.style.zIndex = 10;
|
||||
container.appendChild(switchDiv);
|
||||
const selector = document.createElement('select');
|
||||
selector.id = 'selector';
|
||||
ALLOWED_TRIGGERS.forEach((trigger, index) => {
|
||||
const option = document.createElement('option');
|
||||
option.value = trigger;
|
||||
option.innerHTML = `${trigger}`;
|
||||
selector.appendChild(option);
|
||||
});
|
||||
switchDiv.appendChild(selector);
|
||||
|
||||
// Listen to the selector, change the graph mode when the selector is changed
|
||||
selector.addEventListener('change', (e) => {
|
||||
const value = e.target.value;
|
||||
const mode = value === 'shift' ? 'default' : `${value}Select`;
|
||||
// change the graph mode
|
||||
graph.setMode(mode);
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight - 30);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,89 +1,83 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Graph, extend, Extensions } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{ id: 'node1', x: 150, y: 250 },
|
||||
{ id: 'node2', x: 350, y: 250 },
|
||||
{ id: 'node1', data: { x: 150, y: 250 } },
|
||||
{ id: 'node2', data: { x: 350, y: 250 } },
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
let shift = true;
|
||||
|
||||
const switchDiv = document.createElement('div');
|
||||
switchDiv.innerHTML = `Press down the 'shift' on keyboard and drag to begin select. Click「HERE」to switch trigger to \'drag\', and custom lasso style, and disable drag-canvas
|
||||
<br /> 按住 'shift' 可开始拉索选择。点击「这里」切换 trigger 为 'drag',同时修改拉索样式和关闭画布拖拽`;
|
||||
const container = document.getElementById('container');
|
||||
container.appendChild(switchDiv);
|
||||
|
||||
const width = container.scrollWidth;
|
||||
const height = (container.scrollHeight || 500) - 30;
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
fitCenter: true,
|
||||
modes: {
|
||||
default: ['lasso-select', 'drag-node', 'drag-canvas'],
|
||||
dragLasso: [
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
behaviors: {
|
||||
'hover-activate': Extensions.HoverActivate,
|
||||
'lasso-select': Extensions.LassoSelect,
|
||||
},
|
||||
});
|
||||
|
||||
const modes = {};
|
||||
const ALLOWED_TRIGGERS = ['shift', 'alt', 'ctrl', 'drag', 'meta'];
|
||||
ALLOWED_TRIGGERS.forEach((trigger) => {
|
||||
const defaultBehavior = ['drag-node', 'hover-activate'];
|
||||
if (trigger === 'shift') {
|
||||
modes.default = [...defaultBehavior, 'lasso-select'];
|
||||
} else {
|
||||
modes[`${trigger}Select`] = [
|
||||
...defaultBehavior,
|
||||
{
|
||||
type: 'lasso-select',
|
||||
delegateStyle: {
|
||||
fill: '#f00',
|
||||
fillOpacity: 0.05,
|
||||
stroke: '#f00',
|
||||
lineWidth: 1,
|
||||
},
|
||||
onSelect: (nodes, edges) => {
|
||||
console.log('onSelect', nodes, edges);
|
||||
},
|
||||
trigger: 'drag',
|
||||
trigger: trigger,
|
||||
},
|
||||
'drag-node',
|
||||
],
|
||||
},
|
||||
nodeStateStyles: {
|
||||
selected: {
|
||||
stroke: '#f00',
|
||||
lineWidth: 3,
|
||||
},
|
||||
},
|
||||
edgeStateStyles: {
|
||||
selected: {
|
||||
lineWidth: 3,
|
||||
stroke: '#f00',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('nodeselectchange', (e) => {
|
||||
console.log(e.selectedItems, e.select);
|
||||
});
|
||||
|
||||
switchDiv.addEventListener('click', (e) => {
|
||||
shift = !shift;
|
||||
if (shift) {
|
||||
graph.setMode('default');
|
||||
switchDiv.innerHTML = `Press down the 'shift' on keyboard and drag to begin select. Click「HERE」to switch trigger to \'drag\', and custom lasso style, and disable drag-canvas
|
||||
<br /> 按住 'shift' 可开始拉索选择。点击「这里」切换 trigger 为 'drag',同时修改拉索样式和关闭画布拖拽`;
|
||||
} else {
|
||||
graph.setMode('dragLasso');
|
||||
switchDiv.innerHTML = `Drag on the canvas to begin lasso select. Click「HERE」to switch trigger to \'shift\', and enable drag-canvas
|
||||
<br /> 拖拽画布即可进行拉索选择。点击「这里」切换 trigger 为 'drag',同时开启画布拖拽`;
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
autoFit: 'center',
|
||||
modes,
|
||||
data,
|
||||
});
|
||||
|
||||
const switchDiv = document.createElement('div');
|
||||
switchDiv.innerHTML = `👉 Trigger:`;
|
||||
switchDiv.style.position = 'absolute';
|
||||
switchDiv.style.zIndex = 10;
|
||||
container.appendChild(switchDiv);
|
||||
const selector = document.createElement('select');
|
||||
selector.id = 'selector';
|
||||
ALLOWED_TRIGGERS.forEach((trigger, index) => {
|
||||
const option = document.createElement('option');
|
||||
option.value = trigger;
|
||||
option.innerHTML = `${trigger}`;
|
||||
selector.appendChild(option);
|
||||
});
|
||||
switchDiv.appendChild(selector);
|
||||
|
||||
// Listen to the selector, change the graph mode when the selector is changed
|
||||
selector.addEventListener('change', (e) => {
|
||||
const value = e.target.value;
|
||||
const mode = value === 'shift' ? 'default' : `${value}Select`;
|
||||
// change the graph mode
|
||||
graph.setMode(mode);
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight - 30);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,75 +1,19 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Graph, Extensions, extend } from '@antv/g6';
|
||||
|
||||
/**
|
||||
* 该案例演示,当点击叶子节点时,如何动态向树图中添加多条数据。
|
||||
* 主要演示changeData的用法。
|
||||
*/
|
||||
const ExtGraph = extend(Graph, {
|
||||
edges: {
|
||||
'cubic-horizontal-edge': Extensions.CubicHorizontalEdge,
|
||||
'cubic-vertical-edge': Extensions.CubicVerticalEdge,
|
||||
},
|
||||
});
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
const graph = new G6.TreeGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
modes: {
|
||||
default: ['collapse-expand', 'drag-canvas'],
|
||||
},
|
||||
fitView: true,
|
||||
layout: {
|
||||
type: 'compactBox',
|
||||
direction: 'LR',
|
||||
defalutPosition: [],
|
||||
getId: function getId(d) {
|
||||
return d.id;
|
||||
},
|
||||
getHeight: function getHeight() {
|
||||
return 16;
|
||||
},
|
||||
getWidth: function getWidth() {
|
||||
return 16;
|
||||
},
|
||||
getVGap: function getVGap() {
|
||||
return 50;
|
||||
},
|
||||
getHGap: function getHGap() {
|
||||
return 100;
|
||||
},
|
||||
},
|
||||
});
|
||||
graph.node(function (node) {
|
||||
return {
|
||||
size: 16,
|
||||
anchorPoints: [
|
||||
[0, 0.5],
|
||||
[1, 0.5],
|
||||
],
|
||||
style: {
|
||||
fill: '#DEE9FF',
|
||||
stroke: '#5B8FF9',
|
||||
},
|
||||
label: node.id,
|
||||
labelCfg: {
|
||||
position: node.children && node.children.length > 0 ? 'left' : 'right',
|
||||
},
|
||||
};
|
||||
});
|
||||
let i = 0;
|
||||
graph.edge(function () {
|
||||
i++;
|
||||
return {
|
||||
type: 'cubic-horizontal',
|
||||
color: '#A3B1BF',
|
||||
label: i,
|
||||
};
|
||||
});
|
||||
|
||||
const data = {
|
||||
isRoot: true,
|
||||
id: 'Root',
|
||||
style: {
|
||||
fill: 'red',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
id: 'SubTreeNode1',
|
||||
@ -127,59 +71,103 @@ const data = {
|
||||
},
|
||||
],
|
||||
};
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
data,
|
||||
modes: {
|
||||
default: ['drag-canvas', 'zoom-canvas', 'drag-node'],
|
||||
},
|
||||
node: (model) => {
|
||||
return {
|
||||
id: model.id,
|
||||
data: {
|
||||
...model.data,
|
||||
labelBackgroundShape: {},
|
||||
|
||||
labelShape: {
|
||||
text: model.id,
|
||||
position: model.data.childrenIds?.length ? 'left' : 'right',
|
||||
offsetX: model.data.childrenIds?.length ? -10 : 10,
|
||||
maxWidth: '300%',
|
||||
},
|
||||
anchorPoints: [
|
||||
[0, 0.5],
|
||||
[1, 0.5],
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
edge: {
|
||||
type: 'cubic-horizontal-edge',
|
||||
},
|
||||
layout: {
|
||||
type: 'compactBox',
|
||||
direction: 'LR',
|
||||
getHeight: function getHeight() {
|
||||
return 32;
|
||||
},
|
||||
getWidth: function getWidth() {
|
||||
return 32;
|
||||
},
|
||||
getVGap: function getVGap() {
|
||||
return 10;
|
||||
},
|
||||
getHGap: function getHGap() {
|
||||
return 100;
|
||||
},
|
||||
},
|
||||
autoFit: 'view',
|
||||
data: {
|
||||
type: 'treeData',
|
||||
value: data,
|
||||
},
|
||||
});
|
||||
|
||||
/** Click */
|
||||
let count = 0;
|
||||
|
||||
graph.on('node:click', function (evt) {
|
||||
const item = evt.item;
|
||||
graph.on('node:click', (event) => {
|
||||
const { itemId } = event;
|
||||
|
||||
const nodeId = item.get('id');
|
||||
const model = item.getModel();
|
||||
const children = model.children;
|
||||
if (!children || children.length === 0) {
|
||||
const childData = [
|
||||
{
|
||||
id: 'child-data-' + count,
|
||||
type: 'rect',
|
||||
children: [
|
||||
{
|
||||
id: 'x-' + count,
|
||||
},
|
||||
{
|
||||
id: 'y-' + count,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'child-data1-' + count,
|
||||
children: [
|
||||
{
|
||||
id: 'x1-' + count,
|
||||
},
|
||||
{
|
||||
id: 'y1-' + count,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const newNodes = [
|
||||
{ id: `child-data1-${count}`, data: { type: 'rect-node' } },
|
||||
{ id: `x1-${count}` },
|
||||
{ id: `y1-${count}` },
|
||||
{ id: `child-data2-${count}`, data: { type: 'rect-node' } },
|
||||
{ id: `x2-${count}` },
|
||||
{ id: `y2-${count}` },
|
||||
];
|
||||
|
||||
const parentData = graph.findDataById(nodeId);
|
||||
if (!parentData.children) {
|
||||
parentData.children = [];
|
||||
}
|
||||
// 如果childData是一个数组,则直接赋值给parentData.children
|
||||
// 如果是一个对象,则使用parentData.children.push(obj)
|
||||
parentData.children = childData;
|
||||
graph.changeData();
|
||||
count++;
|
||||
}
|
||||
const newEdges = [
|
||||
{ id: `edge1-${count}`, source: itemId, target: `child-data1-${count}` },
|
||||
{ id: `edge2-${count}`, source: itemId, target: `child-data2-${count}` },
|
||||
{ id: `edge3-${count}`, source: `child-data1-${count}`, target: `x1-${count}` },
|
||||
{ id: `edge4-${count}`, source: `child-data1-${count}`, target: `y1-${count}` },
|
||||
{ id: `edge5-${count}`, source: `child-data2-${count}`, target: `x2-${count}` },
|
||||
{ id: `edge6-${count}`, source: `child-data2-${count}`, target: `y2-${count}` },
|
||||
];
|
||||
|
||||
graph.addData('node', newNodes);
|
||||
graph.addData('edge', newEdges);
|
||||
|
||||
graph.layout();
|
||||
count++;
|
||||
});
|
||||
|
||||
graph.on('canvas:click', (e) => graph.layout());
|
||||
|
||||
const tip = document.createElement('span');
|
||||
tip.innerHTML = 'Click on the leaf node to dynamically add multiple pieces of data to the tree diagram.';
|
||||
tip.style.position = 'absolute';
|
||||
container.appendChild(tip);
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
// });
|
||||
|
@ -4,22 +4,6 @@
|
||||
"en": "Category"
|
||||
},
|
||||
"demos": [
|
||||
{
|
||||
"filename": "dragSubtree.js",
|
||||
"title": {
|
||||
"zh": "拖拽子树改变结构",
|
||||
"en": "Move Subtree to New Parent"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*EU_yRa8hMJUAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "collapseSlibing.js",
|
||||
"title": {
|
||||
"zh": "合并同类兄弟节点",
|
||||
"en": "Collapse the Sibling Nodes"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*ugs9Qad-bQMAAAAAAAAAAAAAARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "changeData.js",
|
||||
"title": {
|
||||
@ -27,14 +11,6 @@
|
||||
"en": "Using changeData"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*wc1-QaqOPsgAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "loadTreeData.js",
|
||||
"title": {
|
||||
"zh": "使用 addChild",
|
||||
"en": "Using addChild"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*wc1-QaqOPsgAAAAAAAAAAABkARQnAQ"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,220 +1,181 @@
|
||||
import G6 from '@antv/g6';
|
||||
|
||||
/**
|
||||
* The usage of arrows
|
||||
* by Shiwu
|
||||
*/
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: '0',
|
||||
x: 150,
|
||||
y: 50,
|
||||
},
|
||||
{
|
||||
id: '1',
|
||||
x: 350,
|
||||
y: 50,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
x: 150,
|
||||
y: 100,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
x: 350,
|
||||
y: 100,
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
x: 150,
|
||||
y: 150,
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
x: 350,
|
||||
y: 150,
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
x: 150,
|
||||
y: 200,
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
x: 350,
|
||||
y: 200,
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
x: 150,
|
||||
y: 250,
|
||||
},
|
||||
{
|
||||
id: '9',
|
||||
x: 350,
|
||||
y: 250,
|
||||
},
|
||||
{
|
||||
id: '10',
|
||||
x: 150,
|
||||
y: 300,
|
||||
},
|
||||
{
|
||||
id: '11',
|
||||
x: 350,
|
||||
y: 300,
|
||||
},
|
||||
{
|
||||
id: '12',
|
||||
x: 150,
|
||||
y: 350,
|
||||
},
|
||||
{
|
||||
id: '13',
|
||||
x: 350,
|
||||
y: 350,
|
||||
},
|
||||
{
|
||||
id: '14',
|
||||
x: 150,
|
||||
y: 400,
|
||||
},
|
||||
{
|
||||
id: '15',
|
||||
x: 350,
|
||||
y: 400,
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge0',
|
||||
source: '0',
|
||||
target: '1',
|
||||
label: 'default arrow',
|
||||
style: {
|
||||
endArrow: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge1',
|
||||
source: '2',
|
||||
target: '3',
|
||||
label: 'triangle arrow',
|
||||
style: {
|
||||
endArrow: {
|
||||
path: G6.Arrow.triangle(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge2',
|
||||
source: '4',
|
||||
target: '5',
|
||||
label: 'vee arrow',
|
||||
style: {
|
||||
endArrow: {
|
||||
path: G6.Arrow.vee(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge3',
|
||||
source: '6',
|
||||
target: '7',
|
||||
label: 'circle arrow',
|
||||
style: {
|
||||
endArrow: {
|
||||
path: G6.Arrow.circle(5, 3),
|
||||
d: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge4',
|
||||
source: '8',
|
||||
target: '9',
|
||||
label: 'diamond arrow',
|
||||
style: {
|
||||
endArrow: {
|
||||
path: G6.Arrow.diamond(10, 10, 3),
|
||||
d: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge5',
|
||||
source: '10',
|
||||
target: '11',
|
||||
label: 'rect arrow',
|
||||
style: {
|
||||
endArrow: {
|
||||
path: G6.Arrow.rect(10, 10, 3),
|
||||
d: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge6',
|
||||
source: '12',
|
||||
target: '13',
|
||||
label: 'rect arrow 2',
|
||||
style: {
|
||||
endArrow: {
|
||||
path: G6.Arrow.rect(10, 2, 5),
|
||||
d: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge7',
|
||||
source: '14',
|
||||
target: '15',
|
||||
label: 'triangleRect arrow',
|
||||
style: {
|
||||
endArrow: {
|
||||
path: G6.Arrow.triangleRect(10, 10, 10, 2, 4),
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
import { Graph } from '@antv/g6';
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
data.edges.forEach((edge) => {
|
||||
edge.style.stroke = '#F6BD16';
|
||||
console.log(edge.style.endArrow);
|
||||
if (edge.id !== 'edge0') edge.style.endArrow.fill = '#F6BD16';
|
||||
});
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
|
||||
const graph = new Graph({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
defaultNode: {
|
||||
size: 15,
|
||||
style: {
|
||||
fill: '#DEE9FF',
|
||||
stroke: '#5B8FF9',
|
||||
},
|
||||
},
|
||||
modes: {
|
||||
// behaviors
|
||||
default: ['drag-node'],
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'],
|
||||
},
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 80,
|
||||
y: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
data: { x: 250, y: 100 },
|
||||
},
|
||||
{
|
||||
id: 'node3',
|
||||
data: {
|
||||
x: 80,
|
||||
y: 150,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node4',
|
||||
data: { x: 250, y: 150 },
|
||||
},
|
||||
{
|
||||
id: 'node5',
|
||||
data: {
|
||||
x: 80,
|
||||
y: 200,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node6',
|
||||
data: { x: 250, y: 200 },
|
||||
},
|
||||
{
|
||||
id: 'node7',
|
||||
data: {
|
||||
x: 80,
|
||||
y: 250,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node8',
|
||||
data: { x: 250, y: 250 },
|
||||
},
|
||||
{
|
||||
id: 'node9',
|
||||
data: {
|
||||
x: 80,
|
||||
y: 300,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node10',
|
||||
data: { x: 250, y: 300 },
|
||||
},
|
||||
{
|
||||
id: 'node11',
|
||||
data: {
|
||||
x: 80,
|
||||
y: 350,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node12',
|
||||
data: { x: 250, y: 350 },
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
data: {
|
||||
keyShape: {
|
||||
endArrow: true,
|
||||
},
|
||||
labelShape: {
|
||||
text: 'simple-arrow',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge2',
|
||||
source: 'node3',
|
||||
target: 'node4',
|
||||
data: {
|
||||
keyShape: {
|
||||
endArrow: {
|
||||
type: 'vee',
|
||||
},
|
||||
},
|
||||
labelShape: {
|
||||
text: 'vee-arrow',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge3',
|
||||
source: 'node5',
|
||||
target: 'node6',
|
||||
data: {
|
||||
keyShape: {
|
||||
endArrow: {
|
||||
type: 'circle',
|
||||
},
|
||||
},
|
||||
labelShape: {
|
||||
text: 'circle-arrow',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge4',
|
||||
source: 'node7',
|
||||
target: 'node8',
|
||||
data: {
|
||||
keyShape: {
|
||||
endArrow: {
|
||||
type: 'rect',
|
||||
},
|
||||
},
|
||||
labelShape: {
|
||||
text: 'rect-arrow',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge5',
|
||||
source: 'node9',
|
||||
target: 'node10',
|
||||
data: {
|
||||
keyShape: {
|
||||
endArrow: {
|
||||
type: 'diamond',
|
||||
},
|
||||
},
|
||||
labelShape: {
|
||||
text: 'diamond-arrow',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge6',
|
||||
source: 'node11',
|
||||
target: 'node12',
|
||||
data: {
|
||||
keyShape: {
|
||||
endArrow: {
|
||||
type: 'triangle-rect',
|
||||
},
|
||||
},
|
||||
labelShape: {
|
||||
text: 'triangle-arrow',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,92 +1,87 @@
|
||||
import G6 from '@antv/g6';
|
||||
|
||||
/**
|
||||
* The usage of arrows
|
||||
* by Shiwu
|
||||
*/
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: '0',
|
||||
x: 150,
|
||||
y: 50,
|
||||
},
|
||||
{
|
||||
id: '1',
|
||||
x: 350,
|
||||
y: 50,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
x: 150,
|
||||
y: 100,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
x: 350,
|
||||
y: 100,
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge0',
|
||||
source: '0',
|
||||
target: '1',
|
||||
label: 'custom arrow 1',
|
||||
style: {
|
||||
endArrow: {
|
||||
path: 'M 3,-5 L 3,5 L 15,10 L 15,-10 Z',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge1',
|
||||
source: '2',
|
||||
target: '3',
|
||||
label: 'custom arrow 2',
|
||||
style: {
|
||||
endArrow: {
|
||||
path: 'M0,0 L10,4 L14,14 L18,4 L28,0 L18,-4 L14,-14 L10,-4 Z',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
import { Graph } from '@antv/g6';
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
data.edges.forEach((edge) => {
|
||||
edge.style.stroke = '#F6BD16';
|
||||
console.log(edge.style.endArrow);
|
||||
edge.style.endArrow.fill = '#F6BD16';
|
||||
});
|
||||
const graph = new G6.Graph({
|
||||
|
||||
const graph = new Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
defaultNode: {
|
||||
size: 15,
|
||||
style: {
|
||||
fill: '#DEE9FF',
|
||||
stroke: '#5B8FF9',
|
||||
},
|
||||
},
|
||||
modes: {
|
||||
// behaviors
|
||||
default: ['drag-node'],
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'],
|
||||
},
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 80,
|
||||
y: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
data: { x: 300, y: 100 },
|
||||
},
|
||||
{
|
||||
id: 'node3',
|
||||
data: {
|
||||
x: 80,
|
||||
y: 150,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node4',
|
||||
data: { x: 300, y: 150 },
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
data: {
|
||||
keyShape: {
|
||||
fill: '#F6BD16',
|
||||
stroke: '#F6BD16',
|
||||
endArrow: {
|
||||
path: 'M0,0 L10,4 L14,14 L18,4 L28,0 L18,-4 L14,-14 L10,-4 Z',
|
||||
fill: '#F6BD16',
|
||||
stroke: '#F6BD16',
|
||||
},
|
||||
},
|
||||
labelShape: {
|
||||
text: 'custom arrow 1',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge2',
|
||||
source: 'node3',
|
||||
target: 'node4',
|
||||
data: {
|
||||
keyShape: {
|
||||
fill: '#F6BD16',
|
||||
stroke: '#F6BD16',
|
||||
endArrow: {
|
||||
path: 'M 3,-5 L 3,5 L 15,10 L 15,-10 Z',
|
||||
fill: '#F6BD16',
|
||||
stroke: '#F6BD16',
|
||||
},
|
||||
},
|
||||
labelShape: {
|
||||
text: 'custom arrow 2',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,146 +1,150 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Extensions, Graph, stdLib, extend } from '@antv/g6';
|
||||
|
||||
/**
|
||||
* The demo shows customing a combo type by extending the built-in circle combo
|
||||
* by Shiwu
|
||||
*
|
||||
*/
|
||||
|
||||
// The symbols for the marker inside the combo
|
||||
const collapseIcon = (x, y, r) => {
|
||||
return [
|
||||
['M', x - r, y],
|
||||
['a', r, r, 0, 1, 0, r * 2, 0],
|
||||
['a', r, r, 0, 1, 0, -r * 2, 0],
|
||||
['M', x - r + 4, y],
|
||||
['L', x - r + 2 * r - 4, y],
|
||||
];
|
||||
};
|
||||
const expandIcon = (x, y, r) => {
|
||||
return [
|
||||
['M', x - r, y],
|
||||
['a', r, r, 0, 1, 0, r * 2, 0],
|
||||
['a', r, r, 0, 1, 0, -r * 2, 0],
|
||||
['M', x - r + 4, y],
|
||||
['L', x - r + 2 * r - 4, y],
|
||||
['M', x - r + r, y - r + 4],
|
||||
['L', x, y + r - 4],
|
||||
];
|
||||
};
|
||||
|
||||
G6.registerCombo(
|
||||
'cCircle',
|
||||
{
|
||||
drawShape: function draw(cfg, group) {
|
||||
const self = this;
|
||||
// Get the shape style, where the style.r corresponds to the R in the Illustration of Built-in Rect Combo
|
||||
const style = self.getShapeStyle(cfg);
|
||||
// Add a circle shape as keyShape which is the same as the extended 'circle' type Combo
|
||||
const circle = group.addShape('circle', {
|
||||
attrs: {
|
||||
...style,
|
||||
x: 0,
|
||||
y: 0,
|
||||
r: style.r,
|
||||
},
|
||||
draggable: true,
|
||||
// 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: 'combo-keyShape',
|
||||
});
|
||||
// Add the marker on the bottom
|
||||
const marker = group.addShape('marker', {
|
||||
attrs: {
|
||||
...style,
|
||||
fill: '#fff',
|
||||
opacity: 1,
|
||||
x: 0,
|
||||
y: style.r,
|
||||
r: 10,
|
||||
symbol: collapseIcon,
|
||||
},
|
||||
draggable: true,
|
||||
// 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: 'combo-marker-shape',
|
||||
});
|
||||
|
||||
return circle;
|
||||
},
|
||||
// Define the updating logic for the marker
|
||||
afterUpdate: function afterUpdate(cfg, combo) {
|
||||
const self = this;
|
||||
// Get the shape style, where the style.r corresponds to the R in the Illustration of Built-in Rect Combo
|
||||
const style = self.getShapeStyle(cfg);
|
||||
const group = combo.get('group');
|
||||
// Find the marker shape in the graphics group of the Combo
|
||||
const marker = group.find((ele) => ele.get('name') === 'combo-marker-shape');
|
||||
// Update the marker shape
|
||||
marker.attr({
|
||||
x: 0,
|
||||
y: style.r,
|
||||
// The property 'collapsed' in the combo data represents the collapsing state of the Combo
|
||||
// Update the symbol according to 'collapsed'
|
||||
symbol: cfg.collapsed ? expandIcon : collapseIcon,
|
||||
});
|
||||
},
|
||||
},
|
||||
'circle',
|
||||
);
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{ id: 'node1', x: 250, y: 200, comboId: 'combo1' },
|
||||
{ id: 'node2', x: 300, y: 200, comboId: 'combo1' },
|
||||
{ id: 'node3', x: 100, y: 200, comboId: 'combo3' },
|
||||
],
|
||||
combos: [
|
||||
{ id: 'combo1', label: 'Combo 1', parentId: 'combo2' },
|
||||
{ id: 'combo2', label: 'Combo 2' },
|
||||
{ id: 'combo3', label: 'Combo 3', collapsed: true },
|
||||
],
|
||||
};
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const descriptionDiv = document.createElement('div');
|
||||
descriptionDiv.innerHTML = 'Click the bottom marker to collapse/expand the combo.';
|
||||
const container = document.getElementById('container');
|
||||
container.appendChild(descriptionDiv);
|
||||
|
||||
const width = container.scrollWidth;
|
||||
const height = (container.scrollHeight || 500) - 20;
|
||||
const graph = new G6.Graph({
|
||||
class CCircleCombo extends Extensions.CircleCombo {
|
||||
drawOtherShapes(model, shapeMap, diffData) {
|
||||
const { data } = model;
|
||||
const keyShapeBBox = shapeMap.keyShape.getLocalBounds();
|
||||
const otherShapes = {
|
||||
markerShape: this.upsertShape(
|
||||
'path',
|
||||
'markerShape',
|
||||
{
|
||||
cursor: 'pointer',
|
||||
stroke: '#666',
|
||||
lineWidth: 1,
|
||||
fill: '#fff',
|
||||
path: data.collapsed
|
||||
? stdLib.markers.expand(keyShapeBBox.center[0], keyShapeBBox.max[1], 8)
|
||||
: stdLib.markers.collapse(keyShapeBBox.center[0], keyShapeBBox.max[1], 8),
|
||||
},
|
||||
shapeMap,
|
||||
model,
|
||||
),
|
||||
};
|
||||
return otherShapes;
|
||||
}
|
||||
}
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
combos: {
|
||||
'c-circle-combo': CCircleCombo,
|
||||
},
|
||||
behaviors: {
|
||||
'hover-activate': Extensions.HoverActivate,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// Set groupByTypes to false to get rendering result with reasonable visual zIndex for combos
|
||||
groupByTypes: false,
|
||||
// Configure the combos globally
|
||||
defaultCombo: {
|
||||
// The type of the combos. You can also assign type in the data of combos
|
||||
type: 'cCircle',
|
||||
labelCfg: {
|
||||
refY: 2,
|
||||
stackCfg: {
|
||||
ignoreStateChange: true,
|
||||
},
|
||||
node: {
|
||||
labelShape: {
|
||||
position: 'center',
|
||||
text: {
|
||||
fields: ['id'],
|
||||
formatter: (model) => model.id,
|
||||
},
|
||||
},
|
||||
// ... Other global configurations for combos
|
||||
// animates: {
|
||||
// update: [
|
||||
// {
|
||||
// fields: ['opacity'],
|
||||
// shapeId: 'haloShape',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
},
|
||||
combo: (model) => {
|
||||
return {
|
||||
id: model.id,
|
||||
data: {
|
||||
type: 'c-circle-combo',
|
||||
...model.data,
|
||||
keyShape: {
|
||||
padding: [10, 20, 30, 40],
|
||||
r: 50,
|
||||
},
|
||||
labelShape: {
|
||||
text: model.data.label,
|
||||
position: 'top',
|
||||
},
|
||||
otherShapes: {},
|
||||
},
|
||||
};
|
||||
},
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 350,
|
||||
y: 200,
|
||||
parentId: 'combo1',
|
||||
},
|
||||
},
|
||||
{ id: 'node2', data: { x: 350, y: 250, parentId: 'combo1' } },
|
||||
{ id: 'node3', data: { x: 100, y: 200, parentId: 'combo3' } },
|
||||
],
|
||||
edges: [
|
||||
{ id: 'edge1', source: 'node1', target: 'node2' },
|
||||
{ id: 'edge2', source: 'node1', target: 'node3' },
|
||||
{ id: 'edge3', source: 'combo1', target: 'node3' },
|
||||
],
|
||||
combos: [
|
||||
{ id: 'combo1', data: { label: 'Combo 1', parentId: 'combo2' } },
|
||||
{ id: 'combo2', data: { label: 'Combo 2' } },
|
||||
{ id: 'combo3', data: { label: 'Combo 3', collapsed: true } },
|
||||
],
|
||||
},
|
||||
modes: {
|
||||
default: ['drag-combo', 'drag-node', 'drag-canvas'],
|
||||
default: [
|
||||
{
|
||||
type: 'drag-node',
|
||||
enableTransient: false,
|
||||
updateComboStructure: false,
|
||||
},
|
||||
'drag-canvas',
|
||||
{
|
||||
type: 'click-select',
|
||||
itemTypes: ['node', 'edge', 'combo'],
|
||||
},
|
||||
{
|
||||
type: 'drag-combo',
|
||||
enableTransient: true,
|
||||
updateComboStructure: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
// collapse/expand when click the marker
|
||||
graph.on('combo:click', (e) => {
|
||||
if (e.target.get('name') === 'combo-marker-shape') {
|
||||
// graph.collapseExpandCombo(e.item.getModel().id);
|
||||
graph.collapseExpandCombo(e.item);
|
||||
if (graph.get('layout')) graph.layout();
|
||||
else graph.refreshPositions();
|
||||
/** Click the bottom marker to collapse/expand the combo. */
|
||||
graph.on('combo:click', (event) => {
|
||||
const { itemId, target } = event;
|
||||
if (target.id === 'markerShape') {
|
||||
const model = graph.getComboData(itemId);
|
||||
if (model.data.collapsed) {
|
||||
graph.expandCombo(itemId);
|
||||
} else {
|
||||
graph.collapseCombo(itemId);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight - 20);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,144 +1,174 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Extensions, Graph, stdLib, extend } from '@antv/g6';
|
||||
|
||||
/**
|
||||
* The demo shows customing a combo type by extending the built-in circle combo
|
||||
* by Shiwu
|
||||
*
|
||||
*/
|
||||
|
||||
// The symbols for the marker inside the combo
|
||||
const collapseIcon = (x, y, r) => {
|
||||
return [
|
||||
['M', x - r, y],
|
||||
['a', r, r, 0, 1, 0, r * 2, 0],
|
||||
['a', r, r, 0, 1, 0, -r * 2, 0],
|
||||
['M', x - r + 4, y],
|
||||
['L', x - r + 2 * r - 4, y],
|
||||
];
|
||||
};
|
||||
const expandIcon = (x, y, r) => {
|
||||
return [
|
||||
['M', x - r, y],
|
||||
['a', r, r, 0, 1, 0, r * 2, 0],
|
||||
['a', r, r, 0, 1, 0, -r * 2, 0],
|
||||
['M', x - r + 4, y],
|
||||
['L', x - r + 2 * r - 4, y],
|
||||
['M', x - r + r, y - r + 4],
|
||||
['L', x, y + r - 4],
|
||||
];
|
||||
};
|
||||
|
||||
G6.registerCombo(
|
||||
'cRect',
|
||||
{
|
||||
drawShape: function drawShape(cfg, group) {
|
||||
const self = this;
|
||||
// Get the padding from the configuration
|
||||
cfg.padding = cfg.padding || [50, 20, 20, 20];
|
||||
// Get the shape's style, where the style.width and style.height correspond to the width and height in the figure of Illustration of Built-in Rect Combo
|
||||
const style = self.getShapeStyle(cfg);
|
||||
// Add a rect shape as the keyShape which is the same as the extended rect Combo
|
||||
const rect = group.addShape('rect', {
|
||||
attrs: {
|
||||
...style,
|
||||
x: -style.width / 2 - (cfg.padding[3] - cfg.padding[1]) / 2,
|
||||
y: -style.height / 2 - (cfg.padding[0] - cfg.padding[2]) / 2,
|
||||
width: style.width,
|
||||
height: style.height,
|
||||
},
|
||||
draggable: true,
|
||||
// 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: 'combo-keyShape',
|
||||
});
|
||||
// Add the circle on the right
|
||||
group.addShape('marker', {
|
||||
attrs: {
|
||||
...style,
|
||||
fill: '#fff',
|
||||
opacity: 1,
|
||||
// cfg.style.width and cfg.style.heigth correspond to the innerWidth and innerHeight in the figure of Illustration of Built-in Rect Combo
|
||||
x: cfg.style.width / 2 + cfg.padding[1],
|
||||
y: (cfg.padding[2] - cfg.padding[0]) / 2,
|
||||
r: 10,
|
||||
symbol: collapseIcon,
|
||||
},
|
||||
draggable: true,
|
||||
// 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: 'combo-marker-shape',
|
||||
});
|
||||
return rect;
|
||||
},
|
||||
// Define the updating logic of the right circle
|
||||
afterUpdate: function afterUpdate(cfg, combo) {
|
||||
const group = combo.get('group');
|
||||
// Find the circle shape in the graphics group of the Combo by name
|
||||
const marker = group.find((ele) => ele.get('name') === 'combo-marker-shape');
|
||||
// Update the position of the right circle
|
||||
marker.attr({
|
||||
// cfg.style.width and cfg.style.heigth correspond to the innerWidth and innerHeight in the figure of Illustration of Built-in Rect Combo
|
||||
x: cfg.style.width / 2 + cfg.padding[1],
|
||||
y: (cfg.padding[2] - cfg.padding[0]) / 2,
|
||||
// The property 'collapsed' in the combo data represents the collapsing state of the Combo
|
||||
// Update the symbol according to 'collapsed'
|
||||
symbol: cfg.collapsed ? expandIcon : collapseIcon,
|
||||
});
|
||||
},
|
||||
},
|
||||
'rect',
|
||||
);
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{ id: 'node1', x: 250, y: 200, comboId: 'combo1' },
|
||||
{ id: 'node2', x: 300, y: 200, comboId: 'combo1' },
|
||||
{ id: 'node3', x: 100, y: 200, comboId: 'combo3' },
|
||||
],
|
||||
combos: [
|
||||
{ id: 'combo1', label: 'Combo 1', parentId: 'combo2' },
|
||||
{ id: 'combo2', label: 'Combo 2' },
|
||||
{ id: 'combo3', label: 'Combo 3', collapsed: true },
|
||||
],
|
||||
};
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const descriptionDiv = document.createElement('div');
|
||||
descriptionDiv.innerHTML = 'Click the right marker to collapse/expand the combo.';
|
||||
const container = document.getElementById('container');
|
||||
descriptionDiv.innerHTML = 'Click the bottom marker to collapse/expand the combo.';
|
||||
container.appendChild(descriptionDiv);
|
||||
|
||||
const width = container.scrollWidth;
|
||||
const height = (container.scrollHeight || 500) - 20;
|
||||
const graph = new G6.Graph({
|
||||
class CRectCombo extends Extensions.RectCombo {
|
||||
drawOtherShapes(model, shapeMap, diffData) {
|
||||
const { data } = model;
|
||||
const keyShapeBBox = shapeMap.keyShape.getLocalBounds();
|
||||
const otherShapes = {
|
||||
markerShape: this.upsertShape(
|
||||
'path',
|
||||
'markerShape',
|
||||
{
|
||||
cursor: 'pointer',
|
||||
stroke: '#666',
|
||||
lineWidth: 1,
|
||||
fill: '#fff',
|
||||
path: data.collapsed
|
||||
? stdLib.markers.expand(keyShapeBBox.center[0], keyShapeBBox.max[1], 8)
|
||||
: stdLib.markers.collapse(keyShapeBBox.center[0], keyShapeBBox.max[1], 8),
|
||||
},
|
||||
shapeMap,
|
||||
model,
|
||||
),
|
||||
};
|
||||
return otherShapes;
|
||||
}
|
||||
}
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
combos: {
|
||||
'c-rect-combo': CRectCombo,
|
||||
},
|
||||
// behaviors: {
|
||||
// 'hover-activate': Extensions.HoverActivate,
|
||||
// },
|
||||
});
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// Set groupByTypes to false to get rendering result with reasonable visual zIndex for combos
|
||||
groupByTypes: false,
|
||||
// Configure the combos globally
|
||||
defaultCombo: {
|
||||
// The type of the combos. You can also assign type in the data of combos
|
||||
type: 'cRect',
|
||||
// ... Other global configurations for combos
|
||||
stackCfg: {
|
||||
ignoreStateChange: true,
|
||||
},
|
||||
node: {
|
||||
labelShape: {
|
||||
position: 'center',
|
||||
text: {
|
||||
fields: ['id'],
|
||||
formatter: (model) => model.id,
|
||||
},
|
||||
},
|
||||
animates: {
|
||||
update: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
combo: (model) => {
|
||||
return {
|
||||
id: model.id,
|
||||
data: {
|
||||
type: 'c-rect-combo',
|
||||
...model.data,
|
||||
keyShape: {
|
||||
padding: [10, 20, 30, 40],
|
||||
r: 50,
|
||||
},
|
||||
labelShape: {
|
||||
text: model.data.label,
|
||||
position: 'top',
|
||||
},
|
||||
otherShapes: {},
|
||||
animates: {
|
||||
buildIn: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 500,
|
||||
delay: 500 + Math.random() * 500,
|
||||
},
|
||||
],
|
||||
buildOut: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 200,
|
||||
},
|
||||
],
|
||||
update: [
|
||||
{
|
||||
fields: ['lineWidth', 'r'],
|
||||
shapeId: 'keyShape',
|
||||
},
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 350,
|
||||
y: 200,
|
||||
parentId: 'combo1',
|
||||
},
|
||||
},
|
||||
{ id: 'node2', data: { x: 350, y: 250, parentId: 'combo1' } },
|
||||
{ id: 'node3', data: { x: 100, y: 200, parentId: 'combo3' } },
|
||||
],
|
||||
edges: [
|
||||
{ id: 'edge1', source: 'node1', target: 'node2' },
|
||||
{ id: 'edge2', source: 'node1', target: 'node3' },
|
||||
],
|
||||
combos: [
|
||||
{ id: 'combo1', data: { label: 'Combo 1', parentId: 'combo2' } },
|
||||
{ id: 'combo2', data: { label: 'Combo 2' } },
|
||||
{ id: 'combo3', data: { label: 'Combo 3', collapsed: true } },
|
||||
],
|
||||
},
|
||||
modes: {
|
||||
default: ['drag-combo', 'drag-node', 'drag-canvas'],
|
||||
default: [
|
||||
{
|
||||
type: 'drag-node',
|
||||
enableTransient: false,
|
||||
updateComboStructure: false,
|
||||
},
|
||||
'drag-canvas',
|
||||
{
|
||||
type: 'click-select',
|
||||
itemTypes: ['node', 'edge', 'combo'],
|
||||
},
|
||||
{
|
||||
type: 'drag-combo',
|
||||
enableTransient: true,
|
||||
updateComboStructure: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
// collapse/expand when click the marker
|
||||
graph.on('combo:click', (e) => {
|
||||
if (e.target.get('name') === 'combo-marker-shape') {
|
||||
// graph.collapseExpandCombo(e.item.getModel().id);
|
||||
graph.collapseExpandCombo(e.item);
|
||||
if (graph.get('layout')) graph.layout();
|
||||
else graph.refreshPositions();
|
||||
/** Click the bottom marker to collapse/expand the combo. */
|
||||
graph.on('combo:click', (event) => {
|
||||
const { itemId, target } = event;
|
||||
if (target.id === 'markerShape') {
|
||||
const model = graph.getComboData(itemId);
|
||||
if (model.data.collapsed) {
|
||||
graph.expandCombo(itemId);
|
||||
} else {
|
||||
graph.collapseCombo(itemId);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight - 20);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,136 +1,108 @@
|
||||
import G6 from '@antv/g6';
|
||||
|
||||
/**
|
||||
* Extend the built-in line edge and override getPath and getShapeStyle to custom a polyline edge
|
||||
* by siogo's issue(https://github.com/antvis/g6/issues/814)
|
||||
*
|
||||
* If you want to fit the dragging, you need to adjust the controlpoints while dragging
|
||||
*
|
||||
*/
|
||||
|
||||
G6.registerEdge(
|
||||
'line-arrow',
|
||||
{
|
||||
getPath(points) {
|
||||
const startPoint = points[0];
|
||||
const endPoint = points[1];
|
||||
return [
|
||||
['M', startPoint.x, startPoint.y],
|
||||
['L', endPoint.x / 3 + (2 / 3) * startPoint.x, startPoint.y],
|
||||
['L', endPoint.x / 3 + (2 / 3) * startPoint.x, endPoint.y],
|
||||
['L', endPoint.x, endPoint.y],
|
||||
];
|
||||
},
|
||||
getShapeStyle(cfg) {
|
||||
const startPoint = cfg.startPoint;
|
||||
const endPoint = cfg.endPoint;
|
||||
const controlPoints = this.getControlPoints(cfg);
|
||||
let points = [startPoint]; // the start point
|
||||
// the control points
|
||||
if (controlPoints) {
|
||||
points = points.concat(controlPoints);
|
||||
}
|
||||
// the end point
|
||||
points.push(endPoint);
|
||||
const path = this.getPath(points);
|
||||
const style = Object.assign(
|
||||
{},
|
||||
G6.Global.defaultEdge.style,
|
||||
{
|
||||
stroke: '#BBB',
|
||||
lineWidth: 1,
|
||||
path,
|
||||
},
|
||||
cfg.style,
|
||||
);
|
||||
return style;
|
||||
},
|
||||
},
|
||||
'line',
|
||||
);
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: '7',
|
||||
x: 150,
|
||||
y: 100,
|
||||
size: 40,
|
||||
anchorPoints: [
|
||||
[1, 0.5],
|
||||
[1, 0],
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
x: 300,
|
||||
y: 200,
|
||||
size: 40,
|
||||
anchorPoints: [
|
||||
[0, 0.5],
|
||||
[0, 1],
|
||||
],
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
source: '7',
|
||||
target: '8',
|
||||
sourceAnchor: 0,
|
||||
targetAnchor: 0,
|
||||
},
|
||||
],
|
||||
};
|
||||
import { Graph, Extensions, extend } from '@antv/g6';
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
modes: {
|
||||
// behavior
|
||||
default: ['drag-node', 'drag-canvas'],
|
||||
},
|
||||
defaultNode: {
|
||||
type: 'circle',
|
||||
style: {
|
||||
fill: '#DEE9FF',
|
||||
stroke: '#5B8FF9',
|
||||
},
|
||||
linkPoints: {
|
||||
left: true,
|
||||
right: true,
|
||||
fill: '#fff',
|
||||
stroke: '#1890FF',
|
||||
size: 3,
|
||||
},
|
||||
},
|
||||
defaultEdge: {
|
||||
type: 'line-arrow',
|
||||
style: {
|
||||
stroke: '#F6BD16',
|
||||
startArrow: {
|
||||
path: 'M 0,0 L 12,6 L 9,0 L 12,-6 Z',
|
||||
fill: '#F6BD16',
|
||||
},
|
||||
endArrow: {
|
||||
path: 'M 0,0 L 12,6 L 9,0 L 12,-6 Z',
|
||||
fill: '#F6BD16',
|
||||
},
|
||||
},
|
||||
|
||||
class CustomPolyline extends Extensions.LineEdge {
|
||||
getPath(sourcePoint, targetPoint) {
|
||||
return [
|
||||
['M', sourcePoint.x, sourcePoint.y],
|
||||
['L', targetPoint.x / 3 + (2 / 3) * sourcePoint.x, sourcePoint.y],
|
||||
['L', targetPoint.x / 3 + (2 / 3) * sourcePoint.x, targetPoint.y],
|
||||
['L', targetPoint.x, targetPoint.y],
|
||||
];
|
||||
}
|
||||
|
||||
drawKeyShape(model, sourcePoint, targetPoint, shapeMap, diffData) {
|
||||
const { keyShape: keyShapeStyle } = this.mergedStyles;
|
||||
const { startArrow, endArrow, ...others } = keyShapeStyle;
|
||||
const lineStyle = {
|
||||
...others,
|
||||
path: this.getPath(sourcePoint, targetPoint),
|
||||
isBillboard: true,
|
||||
};
|
||||
this.upsertArrow('start', startArrow, others, model, lineStyle);
|
||||
this.upsertArrow('end', endArrow, others, model, lineStyle);
|
||||
return this.upsertShape('path', 'keyShape', lineStyle, shapeMap, model);
|
||||
}
|
||||
}
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
edges: {
|
||||
'custom-polyline': CustomPolyline,
|
||||
},
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
modes: {
|
||||
default: ['click-select', 'drag-node'],
|
||||
},
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 150,
|
||||
y: 100,
|
||||
r: 40,
|
||||
anchorPoints: [
|
||||
[1, 0.5],
|
||||
[1, 0],
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
data: {
|
||||
x: 300,
|
||||
y: 200,
|
||||
r: 40,
|
||||
anchorPoints: [
|
||||
[0, 0.5],
|
||||
[0, 1],
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
},
|
||||
],
|
||||
},
|
||||
edge: (edgeInnerModel) => {
|
||||
const { id, data } = edgeInnerModel;
|
||||
return {
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
type: 'custom-polyline',
|
||||
keyShape: {
|
||||
lineWidth: 1,
|
||||
stroke: '#F6BD16',
|
||||
startArrow: {
|
||||
path: 'M 0,0 L 12,6 L 9,0 L 12,-6 Z',
|
||||
fill: '#F6BD16',
|
||||
},
|
||||
endArrow: {
|
||||
path: 'M 0,0 L 12,6 L 9,0 L 12,-6 Z',
|
||||
fill: '#F6BD16',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,128 +0,0 @@
|
||||
import G6 from '@antv/g6';
|
||||
|
||||
/**
|
||||
* Custom a new polyline
|
||||
* by siogo's issue(https://github.com/antvis/g6/issues/814)
|
||||
*
|
||||
* If you want to fit the dragging, you need to adjust the controlpoints while dragging
|
||||
*/
|
||||
G6.registerEdge('line-arrow', {
|
||||
options: {
|
||||
style: {
|
||||
stroke: '#ccc',
|
||||
},
|
||||
},
|
||||
draw: function draw(cfg, group) {
|
||||
const startPoint = cfg.startPoint;
|
||||
const endPoint = cfg.endPoint;
|
||||
|
||||
const stroke = (cfg.style && cfg.style.stroke) || this.options.style.stroke;
|
||||
const startArrow = (cfg.style && cfg.style.startArrow) || undefined;
|
||||
const endArrow = (cfg.style && cfg.style.endArrow) || undefined;
|
||||
|
||||
const keyShape = group.addShape('path', {
|
||||
attrs: {
|
||||
path: [
|
||||
['M', startPoint.x, startPoint.y],
|
||||
['L', endPoint.x / 3 + (2 / 3) * startPoint.x, startPoint.y],
|
||||
['L', endPoint.x / 3 + (2 / 3) * startPoint.x, endPoint.y],
|
||||
['L', endPoint.x, endPoint.y],
|
||||
],
|
||||
stroke,
|
||||
lineWidth: 1,
|
||||
startArrow,
|
||||
endArrow,
|
||||
},
|
||||
className: 'edge-shape',
|
||||
// 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: 'edge-shape',
|
||||
});
|
||||
return keyShape;
|
||||
},
|
||||
});
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: '7',
|
||||
x: 150,
|
||||
y: 100,
|
||||
size: 40,
|
||||
anchorPoints: [
|
||||
[1, 0.5],
|
||||
[1, 0],
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
x: 300,
|
||||
y: 200,
|
||||
size: 40,
|
||||
anchorPoints: [
|
||||
[0, 0.5],
|
||||
[0, 1],
|
||||
],
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
source: '7',
|
||||
target: '8',
|
||||
sourceAnchor: 0,
|
||||
targetAnchor: 0,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
modes: {
|
||||
// behavior
|
||||
default: ['drag-node', 'drag-canvas'],
|
||||
},
|
||||
defaultNode: {
|
||||
type: 'circle',
|
||||
style: {
|
||||
fill: '#DEE9FF',
|
||||
stroke: '#5B8FF9',
|
||||
},
|
||||
linkPoints: {
|
||||
left: true,
|
||||
right: true,
|
||||
fill: '#fff',
|
||||
stroke: '#1890FF',
|
||||
size: 3,
|
||||
},
|
||||
},
|
||||
defaultEdge: {
|
||||
type: 'line-arrow',
|
||||
style: {
|
||||
stroke: '#F6BD16',
|
||||
startArrow: {
|
||||
path: 'M 0,0 L 12,6 L 9,0 L 12,-6 Z',
|
||||
fill: '#F6BD16',
|
||||
},
|
||||
endArrow: {
|
||||
path: 'M 0,0 L 12,6 L 9,0 L 12,-6 Z',
|
||||
fill: '#F6BD16',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight);
|
||||
};
|
@ -1,137 +1,95 @@
|
||||
import G6 from '@antv/g6';
|
||||
|
||||
/**
|
||||
* Custom the edge with multiple labels
|
||||
* by Changzhe
|
||||
*/
|
||||
|
||||
// custom the edge with multiple labels
|
||||
G6.registerEdge('multipleLabelsEdge', {
|
||||
options: {
|
||||
style: {
|
||||
stroke: '#000',
|
||||
},
|
||||
},
|
||||
labelAutoRotate: true,
|
||||
draw(cfg, group) {
|
||||
const startPoint = cfg.startPoint;
|
||||
const endPoint = cfg.endPoint;
|
||||
const stroke = (cfg.style && cfg.style.stroke) || this.options.style.stroke;
|
||||
|
||||
const shape = group.addShape('path', {
|
||||
attrs: {
|
||||
stroke,
|
||||
path: [
|
||||
['M', startPoint.x, startPoint.y],
|
||||
['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',
|
||||
});
|
||||
if (cfg.label && cfg.label.length) {
|
||||
// the left label
|
||||
group.addShape('text', {
|
||||
attrs: {
|
||||
text: cfg.label[0],
|
||||
fill: '#595959',
|
||||
textAlign: 'start',
|
||||
textBaseline: 'middle',
|
||||
x: startPoint.x,
|
||||
y: startPoint.y - 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: 'left-text-shape',
|
||||
});
|
||||
if (cfg.label.length > 1) {
|
||||
// the right label
|
||||
group.addShape('text', {
|
||||
attrs: {
|
||||
text: cfg.label[1],
|
||||
fill: '#595959',
|
||||
textAlign: 'end',
|
||||
textBaseline: 'middle',
|
||||
x: endPoint.x,
|
||||
y: endPoint.y - 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: 'right-text-shape',
|
||||
});
|
||||
}
|
||||
}
|
||||
// return the keyShape
|
||||
return shape;
|
||||
},
|
||||
});
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
x: 100,
|
||||
y: 100,
|
||||
label: 'node1',
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
x: 300,
|
||||
y: 100,
|
||||
label: 'node2',
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
// The left and right labels
|
||||
label: ['hello', 'world'],
|
||||
},
|
||||
],
|
||||
};
|
||||
import { Graph, Extensions, extend } from '@antv/g6';
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
|
||||
class MultipleLabelsEdge extends Extensions.LineEdge {
|
||||
drawOtherShapes(model, shapeMap, diffData) {
|
||||
const labels = model.data.labels;
|
||||
// the left label
|
||||
const startPoint = shapeMap.keyShape.getPoint(0);
|
||||
const leftTextStyle = {
|
||||
text: labels[0],
|
||||
fill: '#595959',
|
||||
fontSize: 12,
|
||||
textAlign: 'start',
|
||||
textBaseline: 'bottom',
|
||||
x: startPoint.x,
|
||||
y: startPoint.y,
|
||||
};
|
||||
shapeMap.leftTextShape = this.upsertShape('text', 'leftTextShape', leftTextStyle, shapeMap, model);
|
||||
|
||||
// the right label
|
||||
const endPoint = shapeMap.keyShape.getPoint(1);
|
||||
const rightTextStyle = {
|
||||
text: labels[1],
|
||||
fill: '#595959',
|
||||
fontSize: 12,
|
||||
textAlign: 'end',
|
||||
textBaseline: 'bottom',
|
||||
x: endPoint.x,
|
||||
y: endPoint.y,
|
||||
};
|
||||
shapeMap.rightTextShape = this.upsertShape('text', 'rightTextShape', rightTextStyle, shapeMap, model);
|
||||
return shapeMap;
|
||||
}
|
||||
}
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
edges: {
|
||||
'multiple-labels-edge': MultipleLabelsEdge,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
modes: {
|
||||
default: [
|
||||
default: ['click-select', 'drag-node'],
|
||||
},
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
type: 'drag-node',
|
||||
delegate: false,
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 100,
|
||||
y: 100,
|
||||
},
|
||||
},
|
||||
'drag-canvas',
|
||||
{
|
||||
type: 'zoom-canvas',
|
||||
sensitivity: 0.5,
|
||||
id: 'node2',
|
||||
data: {
|
||||
x: 300,
|
||||
y: 100,
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
data: {
|
||||
// The left and right labels
|
||||
labels: ['hello', 'world'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
defaultNode: {
|
||||
type: 'circle',
|
||||
size: [50],
|
||||
linkPoints: {
|
||||
left: true,
|
||||
right: true,
|
||||
},
|
||||
},
|
||||
defaultEdge: {
|
||||
type: 'multipleLabelsEdge',
|
||||
style: {
|
||||
edge: {
|
||||
type: 'multiple-labels-edge',
|
||||
keyShape: {
|
||||
stroke: '#F6BD16',
|
||||
},
|
||||
otherShapes: {},
|
||||
},
|
||||
});
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,115 +1,118 @@
|
||||
import G6 from '@antv/g6';
|
||||
|
||||
/**
|
||||
* Custom the edge with extra shapes
|
||||
* by Shiwu
|
||||
*/
|
||||
|
||||
// custom the edge with an extra rect
|
||||
G6.registerEdge(
|
||||
'extra-shape-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);
|
||||
const rectColor = cfg.midPointColor || '#333';
|
||||
// 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: rectColor || '#333',
|
||||
// x and y should be minus width / 2 and height / 2 respectively to translate the center of the rect to the midPoint
|
||||
// x 和 y 分别减去 width / 2 与 height / 2,使矩形中心在 midPoint 上
|
||||
x: midPoint.x - 5,
|
||||
y: midPoint.y - 5,
|
||||
},
|
||||
});
|
||||
|
||||
// get the coordinate of the quatile on the path
|
||||
// 获取路径上的四分位点坐标
|
||||
const quatile = shape.getPoint(0.25);
|
||||
const quatileColor = cfg.quatileColor || '#333';
|
||||
// add a circle on the quatile of the path
|
||||
// 在四分位点上放置一个圆形
|
||||
group.addShape('circle', {
|
||||
attrs: {
|
||||
r: 5,
|
||||
fill: quatileColor || '#333',
|
||||
x: quatile.x,
|
||||
y: quatile.y,
|
||||
},
|
||||
});
|
||||
},
|
||||
update: undefined,
|
||||
},
|
||||
'cubic',
|
||||
);
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
x: 100,
|
||||
y: 100,
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
x: 300,
|
||||
y: 100,
|
||||
},
|
||||
{
|
||||
id: 'node3',
|
||||
x: 300,
|
||||
y: 200,
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
midPointColor: '#f00',
|
||||
quatileColor: '#f00',
|
||||
},
|
||||
{
|
||||
source: 'node1',
|
||||
target: 'node3',
|
||||
midPointColor: '#0f0',
|
||||
quatileColor: '#0f0',
|
||||
},
|
||||
],
|
||||
};
|
||||
import { Graph, Extensions, extend } from '@antv/g6';
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
|
||||
class CustomEdge extends Extensions.CubicEdge {
|
||||
afterDraw(model, shapeMap, shapesChanged) {
|
||||
const data = model.data;
|
||||
const { keyShape } = shapeMap;
|
||||
const midPoint = keyShape.getPoint(0.5);
|
||||
shapeMap.midShape = this.upsertShape(
|
||||
'rect',
|
||||
'midShape',
|
||||
{
|
||||
width: 10,
|
||||
height: 10,
|
||||
x: midPoint.x - 5,
|
||||
y: midPoint.y - 5,
|
||||
fill: data.midShapeColor,
|
||||
...model.data?.otherShapes?.midShape, // merged style from mappers and states
|
||||
},
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
// get the coordinate of the quatile on the path
|
||||
// 获取路径上的四分位点坐标
|
||||
const quatile = keyShape.getPoint(0.25);
|
||||
// add a circle on the quatile of the path
|
||||
// 在四分位点上放置一个圆形
|
||||
shapeMap.quatileShape = this.upsertShape(
|
||||
'circle',
|
||||
'quatileShape',
|
||||
{
|
||||
r: 5,
|
||||
cx: quatile.x,
|
||||
cy: quatile.y,
|
||||
zIndex: 1,
|
||||
fill: data.quatileShapeColor,
|
||||
...model.data?.otherShapes?.quatileShape, // merged style from mappers and states
|
||||
},
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
return shapeMap;
|
||||
}
|
||||
}
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
edges: {
|
||||
'custom-edge': CustomEdge,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
modes: {
|
||||
default: ['drag-node', 'drag-canvas'],
|
||||
default: ['click-select'],
|
||||
},
|
||||
defaultEdge: {
|
||||
type: 'extra-shape-edge',
|
||||
style: {
|
||||
stroke: '#F6BD16',
|
||||
},
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 100,
|
||||
y: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
data: {
|
||||
x: 300,
|
||||
y: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node3',
|
||||
data: {
|
||||
x: 300,
|
||||
y: 200,
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
data: {
|
||||
midShapeColor: '#f00',
|
||||
quatileShapeColor: '#f00',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'edge2',
|
||||
source: 'node1',
|
||||
target: 'node3',
|
||||
data: {
|
||||
midShapeColor: '#0f0',
|
||||
quatileShapeColor: '#0f0',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
edge: {
|
||||
type: 'custom-edge',
|
||||
otherShapes: {},
|
||||
},
|
||||
});
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -20,14 +20,6 @@
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*FuS3Qq_lDiIAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "customPolyline2.js",
|
||||
"title": {
|
||||
"zh": "自定义折线 2",
|
||||
"en": "Custom Polyline 2"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*ACNnQLPy20EAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "edgeMulLabel.js",
|
||||
"title": {
|
||||
|
@ -1,213 +1,120 @@
|
||||
import G6 from '@antv/g6';
|
||||
|
||||
/**
|
||||
* Custom a area chart node
|
||||
* by Jingxi
|
||||
*
|
||||
* Custom a pie chart node
|
||||
*/
|
||||
import { Graph, Extensions, extend, stdLib } from '@antv/g6';
|
||||
|
||||
/**
|
||||
* Register a area chart node
|
||||
*/
|
||||
G6.registerNode('area', {
|
||||
draw(cfg, group) {
|
||||
const baseR = 30;
|
||||
|
||||
// Ref line
|
||||
let refR = baseR;
|
||||
const refInc = 10;
|
||||
for (let i = 0; i < 6; i++) {
|
||||
group.addShape('circle', {
|
||||
attrs: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
r: (refR += refInc),
|
||||
stroke: '#bae7ff',
|
||||
lineDash: [4, 4],
|
||||
},
|
||||
// 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: 'circle-shape',
|
||||
});
|
||||
}
|
||||
const everyIncAngle = (2 * Math.PI * (360 / 5)) / 360;
|
||||
const tempIncValues = [baseR, baseR, baseR, baseR, baseR];
|
||||
const allRs = [];
|
||||
cfg.details.forEach((cat) => {
|
||||
const oneRs = [];
|
||||
cat.values.forEach((v, i) => {
|
||||
const R = tempIncValues[i] + v * 0.4;
|
||||
oneRs.push(R);
|
||||
tempIncValues[i] = R;
|
||||
});
|
||||
allRs.push(oneRs);
|
||||
});
|
||||
const strokeColors = [
|
||||
'rgba(91, 143, 249,1)',
|
||||
'rgba(90, 216, 166,1)',
|
||||
'rgba(246, 189, 22,1)',
|
||||
'rgba(232, 104, 74,1)',
|
||||
'rgba(255, 157, 77,1)',
|
||||
];
|
||||
const fillColors = [
|
||||
'rgba(91, 143, 249,0.5)',
|
||||
'rgba(90, 216, 166,0.5)',
|
||||
'rgba(246, 189, 22,0.5)',
|
||||
'rgba(232, 104, 74,0.5)',
|
||||
'rgba(255, 157, 77,0.5)',
|
||||
];
|
||||
|
||||
allRs.reverse().forEach((Rs, index) => {
|
||||
let curAngle = 0;
|
||||
const poss = [];
|
||||
Rs.forEach((r) => {
|
||||
const xPos = r * Math.cos(curAngle);
|
||||
const yPos = r * Math.sin(curAngle);
|
||||
curAngle += everyIncAngle;
|
||||
poss.push([xPos, yPos]);
|
||||
});
|
||||
const Ls = poss.map((p, i) => {
|
||||
if (i === 0) {
|
||||
return ['M', ...p];
|
||||
}
|
||||
return ['L', ...p];
|
||||
});
|
||||
|
||||
group.addShape('path', {
|
||||
attrs: {
|
||||
path: [
|
||||
...Ls,
|
||||
['Z'], // close the path
|
||||
],
|
||||
stroke: strokeColors[index],
|
||||
fill: fillColors[index],
|
||||
},
|
||||
// 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-shape1',
|
||||
});
|
||||
});
|
||||
let nowAngle2 = 0;
|
||||
const everyIncAngleCat = (2 * Math.PI * (360 / 5)) / 360;
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const r = 30 + 60;
|
||||
const xPos = r * Math.cos(nowAngle2);
|
||||
const yPos = r * Math.sin(nowAngle2);
|
||||
|
||||
group.addShape('path', {
|
||||
attrs: {
|
||||
path: [
|
||||
['M', 0, 0],
|
||||
['L', xPos, yPos],
|
||||
],
|
||||
lineDash: [4, 4],
|
||||
stroke: 'darkgray',
|
||||
},
|
||||
// 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-shape2',
|
||||
});
|
||||
nowAngle2 += everyIncAngleCat;
|
||||
}
|
||||
|
||||
// add a circle with the same filling color with background
|
||||
group.addShape('circle', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0, // 居中
|
||||
y: 0,
|
||||
r: baseR,
|
||||
fill: cfg.centerColor,
|
||||
stroke: 'darkgray',
|
||||
},
|
||||
// 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: 'circle-shape',
|
||||
});
|
||||
|
||||
if (cfg.label) {
|
||||
group.addShape('text', {
|
||||
// attrs: style
|
||||
attrs: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
textAlign: 'center',
|
||||
textBaseline: 'middle',
|
||||
text: cfg.label,
|
||||
fill: 'white',
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
// 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: 'text-shape',
|
||||
});
|
||||
}
|
||||
return group;
|
||||
},
|
||||
});
|
||||
|
||||
/** data */
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'nodeD',
|
||||
x: 150,
|
||||
y: 150,
|
||||
label: 'Area1',
|
||||
type: 'area',
|
||||
anchorPoints: [
|
||||
[0, 0.5],
|
||||
[1, 0.5],
|
||||
],
|
||||
details: [
|
||||
{ cat: 'pv', values: [20, 30, 40, 30, 30], color: '#5ad8a6' },
|
||||
{ cat: 'dal', values: [40, 30, 20, 30, 50], color: '#ff99c3' },
|
||||
{ cat: 'uv', values: [40, 30, 30, 40, 40], color: '#6dc8ec' },
|
||||
{ cat: 'sal', values: [20, 30, 50, 20, 20], color: '#269a99' },
|
||||
{ cat: 'cal', values: [10, 10, 20, 20, 20], color: '#9270CA' },
|
||||
],
|
||||
centerColor: '#5b8ff9',
|
||||
},
|
||||
{
|
||||
id: 'nodeD2',
|
||||
x: 500,
|
||||
y: 150,
|
||||
label: 'Area2',
|
||||
type: 'area',
|
||||
anchorPoints: [
|
||||
[0, 0.5],
|
||||
[1, 0.5],
|
||||
],
|
||||
details: [
|
||||
{ cat: 'pv', values: [10, 10, 80, 20, 10], color: '#5ad8a6' },
|
||||
{ cat: 'dal', values: [20, 30, 10, 50, 40], color: '#ff99c3' },
|
||||
{ cat: 'uv', values: [10, 50, 30, 20, 30], color: '#6dc8ec' },
|
||||
{ cat: 'sal', values: [70, 30, 20, 20, 20], color: '#269a99' },
|
||||
{ cat: 'cal', values: [50, 10, 20, 70, 30], color: '#9270CA' },
|
||||
],
|
||||
centerColor: '#5b8ff9',
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
source: 'nodeD',
|
||||
target: 'nodeD2',
|
||||
},
|
||||
],
|
||||
};
|
||||
const lightBlue = '#5b8ff9';
|
||||
const lightOrange = '#5ad8a6';
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
|
||||
class PieNode extends Extensions.CircleNode {
|
||||
drawOtherShapes(model, shapeMap) {
|
||||
const { data: cfg } = model;
|
||||
// node radius
|
||||
const radius = cfg.size / 2;
|
||||
// the ratio of indegree to outdegree
|
||||
const inPercentage = cfg.inDegree / cfg.degree;
|
||||
// the anble for the indegree fan
|
||||
const inAngle = inPercentage * Math.PI * 2;
|
||||
// the end position for the in-degree fan
|
||||
const inArcEnd = [radius * Math.cos(inAngle), -radius * Math.sin(inAngle)];
|
||||
let isInBigArc = 0,
|
||||
isOutBigArc = 1;
|
||||
if (inAngle > Math.PI) {
|
||||
isInBigArc = 1;
|
||||
isOutBigArc = 0;
|
||||
}
|
||||
|
||||
const otherShapes = {
|
||||
['in-fan-shape']: this.upsertShape(
|
||||
'path',
|
||||
'in-fan-shape',
|
||||
{
|
||||
path: [
|
||||
['M', radius, 0],
|
||||
['A', radius, radius, 0, isInBigArc, 0, inArcEnd[0], inArcEnd[1]],
|
||||
['L', 0, 0],
|
||||
['Z'],
|
||||
],
|
||||
lineWidth: 0,
|
||||
fill: lightOrange,
|
||||
},
|
||||
shapeMap,
|
||||
model,
|
||||
),
|
||||
['out-fan-shape']: this.upsertShape(
|
||||
'path',
|
||||
'out-fan-shape',
|
||||
{
|
||||
path: [
|
||||
['M', inArcEnd[0], inArcEnd[1]],
|
||||
['A', radius, radius, 0, isOutBigArc, 0, radius, 0],
|
||||
['L', 0, 0],
|
||||
['Z'],
|
||||
],
|
||||
lineWidth: 0,
|
||||
fill: lightBlue,
|
||||
},
|
||||
shapeMap,
|
||||
model,
|
||||
),
|
||||
};
|
||||
return otherShapes;
|
||||
}
|
||||
}
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
nodes: {
|
||||
'pie-node': PieNode,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
});
|
||||
autoFit: 'center',
|
||||
modes: {
|
||||
default: ['drag-node'],
|
||||
},
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
data: {
|
||||
size: 80,
|
||||
inDegree: 80,
|
||||
degree: 360,
|
||||
x: 150,
|
||||
y: 150,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
data: {
|
||||
size: 80,
|
||||
inDegree: 280,
|
||||
degree: 360,
|
||||
x: 350,
|
||||
y: 150,
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [{ id: 'edge1', source: 'node1', target: 'node2' }],
|
||||
},
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
node: {
|
||||
type: 'pie-node',
|
||||
otherShapes: {},
|
||||
},
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,8 +1,9 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Extensions, Graph, extend, stdLib } from '@antv/g6';
|
||||
|
||||
let graph;
|
||||
|
||||
const ERROR_COLOR = '#F5222D';
|
||||
|
||||
const getNodeConfig = (node) => {
|
||||
if (node.nodeError) {
|
||||
return {
|
||||
@ -34,207 +35,68 @@ const getNodeConfig = (node) => {
|
||||
return config;
|
||||
};
|
||||
|
||||
const COLLAPSE_ICON = function COLLAPSE_ICON(x, y, r) {
|
||||
return [
|
||||
['M', x - r, y],
|
||||
['a', r, r, 0, 1, 0, r * 2, 0],
|
||||
['a', r, r, 0, 1, 0, -r * 2, 0],
|
||||
['M', x - r + 4, y],
|
||||
['L', x - r + 2 * r - 4, y],
|
||||
];
|
||||
};
|
||||
const EXPAND_ICON = function EXPAND_ICON(x, y, r) {
|
||||
return [
|
||||
['M', x - r, y],
|
||||
['a', r, r, 0, 1, 0, r * 2, 0],
|
||||
['a', r, r, 0, 1, 0, -r * 2, 0],
|
||||
['M', x - r + 4, y],
|
||||
['L', x - r + 2 * r - 4, y],
|
||||
['M', x - r + r, y - r + 4],
|
||||
['L', x, y + r - 4],
|
||||
];
|
||||
};
|
||||
const nodeBasicMethod = {
|
||||
createNodeBox: (group, config, w, h, isRoot) => {
|
||||
/* 最外面的大矩形 */
|
||||
const container = group.addShape('rect', {
|
||||
attrs: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: w,
|
||||
height: h,
|
||||
},
|
||||
// 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: 'big-rect-shape',
|
||||
});
|
||||
class CardNode extends Extensions.RectNode {
|
||||
drawOtherShapes(model, shapeMap) {
|
||||
const { data: cfg } = model;
|
||||
const config = getNodeConfig(cfg);
|
||||
|
||||
const isRoot = cfg.dataType === 'root';
|
||||
const nodeError = cfg.nodeError;
|
||||
|
||||
const otherShapes = {};
|
||||
|
||||
if (!isRoot) {
|
||||
/* 左边的小圆点 */
|
||||
group.addShape('circle', {
|
||||
attrs: {
|
||||
x: 3,
|
||||
y: h / 2,
|
||||
r: 6,
|
||||
fill: config.basicColor,
|
||||
},
|
||||
// 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: 'left-dot-shape',
|
||||
});
|
||||
otherShapes['left-dot-shape'] = this.upsertShape(
|
||||
'circle',
|
||||
'left-dot-shape',
|
||||
{ cx: 3, cy: 32, r: 6, fill: config.basicColor },
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
}
|
||||
/* 矩形 */
|
||||
group.addShape('rect', {
|
||||
attrs: {
|
||||
|
||||
/** rect shape */
|
||||
otherShapes['rect-shape'] = this.upsertShape(
|
||||
'rect',
|
||||
'rect-shape',
|
||||
{
|
||||
x: 3,
|
||||
y: 0,
|
||||
width: w - 19,
|
||||
height: h,
|
||||
width: 243 - 19,
|
||||
height: 64,
|
||||
fill: config.bgColor,
|
||||
stroke: config.borderColor,
|
||||
radius: 2,
|
||||
cursor: 'pointer',
|
||||
zIndex: 0,
|
||||
},
|
||||
// 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: 'rect-shape',
|
||||
});
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
|
||||
/* 左边的粗线 */
|
||||
group.addShape('rect', {
|
||||
attrs: {
|
||||
/* Left bolder border */
|
||||
otherShapes['left-border-shape'] = this.upsertShape(
|
||||
'rect',
|
||||
'left-border-shape',
|
||||
{
|
||||
x: 3,
|
||||
y: 0,
|
||||
width: 3,
|
||||
height: h,
|
||||
height: 64,
|
||||
fill: config.basicColor,
|
||||
radius: 1.5,
|
||||
zIndex: 0,
|
||||
},
|
||||
// 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: 'left-border-shape',
|
||||
});
|
||||
return container;
|
||||
},
|
||||
/* 生成树上的 marker */
|
||||
createNodeMarker: (group, collapsed, x, y) => {
|
||||
group.addShape('circle', {
|
||||
attrs: {
|
||||
x,
|
||||
y,
|
||||
r: 13,
|
||||
fill: 'rgba(47, 84, 235, 0.05)',
|
||||
opacity: 0,
|
||||
zIndex: -2,
|
||||
},
|
||||
// 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: 'collapse-icon-bg',
|
||||
});
|
||||
group.addShape('marker', {
|
||||
attrs: {
|
||||
x,
|
||||
y,
|
||||
r: 7,
|
||||
symbol: collapsed ? EXPAND_ICON : COLLAPSE_ICON,
|
||||
stroke: 'rgba(0,0,0,0.25)',
|
||||
fill: 'rgba(0,0,0,0)',
|
||||
lineWidth: 1,
|
||||
cursor: 'pointer',
|
||||
},
|
||||
// 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: 'collapse-icon',
|
||||
});
|
||||
},
|
||||
afterDraw: (cfg, group) => {
|
||||
/* 操作 marker 的背景色显示隐藏 */
|
||||
const icon = group.find((element) => element.get('name') === 'collapse-icon');
|
||||
if (icon) {
|
||||
const bg = group.find((element) => element.get('name') === 'collapse-icon-bg');
|
||||
icon.on('mouseenter', () => {
|
||||
bg.attr('opacity', 1);
|
||||
graph.get('canvas').draw();
|
||||
});
|
||||
icon.on('mouseleave', () => {
|
||||
bg.attr('opacity', 0);
|
||||
graph.get('canvas').draw();
|
||||
});
|
||||
}
|
||||
/* ip 显示 */
|
||||
const ipBox = group.find((element) => element.get('name') === 'ip-box');
|
||||
if (ipBox) {
|
||||
/* ip 复制的几个元素 */
|
||||
const ipLine = group.find((element) => element.get('name') === 'ip-cp-line');
|
||||
const ipBG = group.find((element) => element.get('name') === 'ip-cp-bg');
|
||||
const ipIcon = group.find((element) => element.get('name') === 'ip-cp-icon');
|
||||
const ipCPBox = group.find((element) => element.get('name') === 'ip-cp-box');
|
||||
|
||||
const onMouseEnter = () => {
|
||||
ipLine.attr('opacity', 1);
|
||||
ipBG.attr('opacity', 1);
|
||||
ipIcon.attr('opacity', 1);
|
||||
graph.get('canvas').draw();
|
||||
};
|
||||
const onMouseLeave = () => {
|
||||
ipLine.attr('opacity', 0);
|
||||
ipBG.attr('opacity', 0);
|
||||
ipIcon.attr('opacity', 0);
|
||||
graph.get('canvas').draw();
|
||||
};
|
||||
ipBox.on('mouseenter', () => {
|
||||
onMouseEnter();
|
||||
});
|
||||
ipBox.on('mouseleave', () => {
|
||||
onMouseLeave();
|
||||
});
|
||||
ipCPBox.on('mouseenter', () => {
|
||||
onMouseEnter();
|
||||
});
|
||||
ipCPBox.on('mouseleave', () => {
|
||||
onMouseLeave();
|
||||
});
|
||||
ipCPBox.on('click', () => { });
|
||||
}
|
||||
},
|
||||
setState: (name, value, item) => {
|
||||
const hasOpacityClass = [
|
||||
'ip-cp-line',
|
||||
'ip-cp-bg',
|
||||
'ip-cp-icon',
|
||||
'ip-cp-box',
|
||||
'ip-box',
|
||||
'collapse-icon-bg',
|
||||
];
|
||||
const group = item.getContainer();
|
||||
const childrens = group.get('children');
|
||||
graph.setAutoPaint(false);
|
||||
if (name === 'emptiness') {
|
||||
if (value) {
|
||||
childrens.forEach((shape) => {
|
||||
if (hasOpacityClass.indexOf(shape.get('name')) > -1) {
|
||||
return;
|
||||
}
|
||||
shape.attr('opacity', 0.4);
|
||||
});
|
||||
} else {
|
||||
childrens.forEach((shape) => {
|
||||
if (hasOpacityClass.indexOf(shape.get('name')) > -1) {
|
||||
return;
|
||||
}
|
||||
shape.attr('opacity', 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
graph.setAutoPaint(true);
|
||||
},
|
||||
};
|
||||
|
||||
G6.registerNode('card-node', {
|
||||
draw: (cfg, group) => {
|
||||
const config = getNodeConfig(cfg);
|
||||
const isRoot = cfg.dataType === 'root';
|
||||
const nodeError = cfg.nodeError;
|
||||
/* the biggest rect */
|
||||
const container = nodeBasicMethod.createNodeBox(group, config, 243, 64, isRoot);
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
|
||||
if (cfg.dataType !== 'root') {
|
||||
/* the type text */
|
||||
group.addShape('text', {
|
||||
attrs: {
|
||||
otherShapes['type-text-shape'] = this.upsertShape(
|
||||
'text',
|
||||
'type-text-shape',
|
||||
{
|
||||
text: cfg.dataType,
|
||||
x: 3,
|
||||
y: -10,
|
||||
@ -243,28 +105,32 @@ G6.registerNode('card-node', {
|
||||
textBaseline: 'middle',
|
||||
fill: 'rgba(0,0,0,0.65)',
|
||||
},
|
||||
// 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: 'type-text-shape',
|
||||
});
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
}
|
||||
|
||||
if (cfg.ip) {
|
||||
/* ip start */
|
||||
/* ipBox */
|
||||
const ipRect = group.addShape('rect', {
|
||||
attrs: {
|
||||
fill: nodeError ? null : '#FFF',
|
||||
stroke: nodeError ? 'rgba(255,255,255,0.65)' : null,
|
||||
otherShapes['ip-container-shape'] = this.upsertShape(
|
||||
'rect',
|
||||
'ip-container-shape',
|
||||
{
|
||||
fill: nodeError ? 'rgba(0,0,0,0)' : '#fff',
|
||||
stroke: nodeError ? 'rgba(255,255,255,0.65)' : 'rgba(0,0,0,0)',
|
||||
radius: 2,
|
||||
cursor: 'pointer',
|
||||
},
|
||||
// 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: 'ip-container-shape',
|
||||
});
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
|
||||
/* ip */
|
||||
const ipText = group.addShape('text', {
|
||||
attrs: {
|
||||
otherShapes['ip-text-shape'] = this.upsertShape(
|
||||
'text',
|
||||
'ip-text-shape',
|
||||
{
|
||||
text: cfg.ip,
|
||||
x: 0,
|
||||
y: 19,
|
||||
@ -274,42 +140,48 @@ G6.registerNode('card-node', {
|
||||
fill: nodeError ? 'rgba(255,255,255,0.85)' : 'rgba(0,0,0,0.65)',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
// 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: 'ip-text-shape',
|
||||
});
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
|
||||
const ipBBox = ipText.getBBox();
|
||||
const ipBBox = otherShapes['ip-text-shape'].getBBox();
|
||||
console.log('ipBBox', ipBBox);
|
||||
/* the distance from the IP to the right is 12px */
|
||||
ipText.attr({
|
||||
otherShapes['ip-text-shape'].attr({
|
||||
x: 224 - 12 - ipBBox.width,
|
||||
});
|
||||
/* ipBox */
|
||||
ipRect.attr({
|
||||
otherShapes['ip-container-shape'].attr({
|
||||
x: 224 - 12 - ipBBox.width - 4,
|
||||
y: ipBBox.minY - 5,
|
||||
y: ipBBox.y - 5,
|
||||
width: ipBBox.width + 8,
|
||||
height: ipBBox.height + 10,
|
||||
cursor: 'pointer',
|
||||
});
|
||||
|
||||
/* a transparent shape on the IP for click listener */
|
||||
group.addShape('rect', {
|
||||
attrs: {
|
||||
otherShapes['ip-box'] = this.upsertShape(
|
||||
'rect',
|
||||
'ip-box',
|
||||
{
|
||||
stroke: '',
|
||||
cursor: 'pointer',
|
||||
x: 224 - 12 - ipBBox.width - 4,
|
||||
y: ipBBox.minY - 5,
|
||||
y: ipBBox.y - 5,
|
||||
width: ipBBox.width + 8,
|
||||
height: ipBBox.height + 10,
|
||||
fill: '#fff',
|
||||
opacity: 0,
|
||||
},
|
||||
// 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: 'ip-box',
|
||||
});
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
|
||||
/* copyIpLine */
|
||||
group.addShape('rect', {
|
||||
attrs: {
|
||||
otherShapes['ip-cp-line'] = this.upsertShape(
|
||||
'rect',
|
||||
'ip-cp-line',
|
||||
{
|
||||
x: 194,
|
||||
y: 7,
|
||||
width: 1,
|
||||
@ -317,59 +189,60 @@ G6.registerNode('card-node', {
|
||||
fill: '#E3E6E8',
|
||||
opacity: 0,
|
||||
},
|
||||
// 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: 'ip-cp-line',
|
||||
});
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
|
||||
/* copyIpBG */
|
||||
group.addShape('rect', {
|
||||
attrs: {
|
||||
otherShapes['ip-cp-bg'] = this.upsertShape(
|
||||
'rect',
|
||||
'ip-cp-bg',
|
||||
{
|
||||
x: 195,
|
||||
y: 8,
|
||||
width: 22,
|
||||
height: 22,
|
||||
fill: '#FFF',
|
||||
cursor: 'pointer',
|
||||
opacity: 0,
|
||||
opacity: cfg.showIcon ? 1 : 0,
|
||||
},
|
||||
// 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: 'ip-cp-bg',
|
||||
});
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
|
||||
/* copyIpIcon */
|
||||
group.addShape('image', {
|
||||
attrs: {
|
||||
otherShapes['ip-cp-icon'] = this.upsertShape(
|
||||
'image',
|
||||
'ip-cp-icon',
|
||||
{
|
||||
x: 200,
|
||||
y: 13,
|
||||
height: 12,
|
||||
width: 10,
|
||||
width: 12,
|
||||
img: 'https://os.alipayobjects.com/rmsportal/DFhnQEhHyPjSGYW.png',
|
||||
cursor: 'pointer',
|
||||
opacity: 0,
|
||||
opacity: cfg.showIcon ? 1 : 0,
|
||||
},
|
||||
// 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: 'ip-cp-icon',
|
||||
});
|
||||
/* a transparent rect on the icon area for click listener */
|
||||
group.addShape('rect', {
|
||||
attrs: {
|
||||
x: 195,
|
||||
y: 8,
|
||||
width: 22,
|
||||
height: 22,
|
||||
fill: '#FFF',
|
||||
cursor: 'pointer',
|
||||
opacity: 0,
|
||||
},
|
||||
// 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: 'ip-cp-box',
|
||||
tooltip: 'Copy the IP',
|
||||
});
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
|
||||
/* a transparent rect on the icon area for click listener */
|
||||
otherShapes['ip-cp-box'] = this.upsertShape(
|
||||
'rect',
|
||||
'ip-cp-box',
|
||||
{ x: 195, y: 8, width: 22, height: 22, fill: '#FFF', cursor: 'pointer', opacity: 0 },
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
/* ip end */
|
||||
}
|
||||
|
||||
/* name */
|
||||
group.addShape('text', {
|
||||
attrs: {
|
||||
otherShapes['name-text-shape'] = this.upsertShape(
|
||||
'text',
|
||||
'name-text-shape',
|
||||
{
|
||||
text: cfg.name,
|
||||
x: 19,
|
||||
y: 19,
|
||||
@ -380,13 +253,15 @@ G6.registerNode('card-node', {
|
||||
fill: config.fontColor,
|
||||
cursor: 'pointer',
|
||||
},
|
||||
// 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: 'name-text-shape',
|
||||
});
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
|
||||
/* the description text */
|
||||
group.addShape('text', {
|
||||
attrs: {
|
||||
otherShapes['bottom-text-shape'] = this.upsertShape(
|
||||
'text',
|
||||
'bottom-text-shape',
|
||||
{
|
||||
text: cfg.keyInfo,
|
||||
x: 19,
|
||||
y: 45,
|
||||
@ -396,95 +271,152 @@ G6.registerNode('card-node', {
|
||||
fill: config.fontColor,
|
||||
cursor: 'pointer',
|
||||
},
|
||||
// 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: 'bottom-text-shape',
|
||||
});
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
|
||||
if (nodeError) {
|
||||
group.addShape('text', {
|
||||
attrs: {
|
||||
otherShapes['error-text-shape'] = this.upsertShape(
|
||||
'text',
|
||||
'error-text-shape',
|
||||
{
|
||||
x: 191,
|
||||
y: 62,
|
||||
text: '⚠️',
|
||||
fill: '#000',
|
||||
fontSize: 18,
|
||||
},
|
||||
// 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: 'error-text-shape',
|
||||
});
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
}
|
||||
|
||||
const hasChildren = cfg.children && cfg.children.length > 0;
|
||||
const keyShapeBBox = shapeMap.keyShape.getLocalBounds();
|
||||
if (hasChildren) {
|
||||
nodeBasicMethod.createNodeMarker(group, cfg.collapsed, 236, 32);
|
||||
otherShapes['marker-shape'] = this.upsertShape(
|
||||
'path',
|
||||
'markerShape',
|
||||
{
|
||||
zIndex: 10,
|
||||
cursor: 'pointer',
|
||||
stroke: '#666',
|
||||
lineWidth: 1,
|
||||
fill: '#fff',
|
||||
path: stdLib.markers.expand(keyShapeBBox.max[0] - 15, keyShapeBBox.center[1], 8),
|
||||
},
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
}
|
||||
return container;
|
||||
return otherShapes;
|
||||
}
|
||||
}
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
nodes: {
|
||||
'card-node': CardNode,
|
||||
},
|
||||
afterDraw: nodeBasicMethod.afterDraw,
|
||||
setState: nodeBasicMethod.setState,
|
||||
});
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
modes: {
|
||||
default: ['drag-node'],
|
||||
},
|
||||
defaultNode: {
|
||||
type: 'card-node',
|
||||
},
|
||||
});
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
name: 'cardNodeApp',
|
||||
ip: '127.0.0.1',
|
||||
nodeError: true,
|
||||
dataType: 'root',
|
||||
keyInfo: 'this is a card node info',
|
||||
x: 100,
|
||||
y: 50,
|
||||
id: 'node1',
|
||||
data: {
|
||||
showIcon: false,
|
||||
name: 'cardNodeApp',
|
||||
ip: '127.0.0.1',
|
||||
nodeError: true,
|
||||
dataType: 'root',
|
||||
keyInfo: 'this is a card node info',
|
||||
x: 100,
|
||||
y: 50,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'cardNodeApp',
|
||||
ip: '127.0.0.1',
|
||||
nodeError: false,
|
||||
dataType: 'subRoot',
|
||||
keyInfo: 'this is sub root',
|
||||
x: 100,
|
||||
y: 150,
|
||||
id: 'node2',
|
||||
data: {
|
||||
showIcon: false,
|
||||
name: 'cardNodeApp',
|
||||
ip: '127.0.0.1',
|
||||
nodeError: false,
|
||||
dataType: 'subRoot',
|
||||
keyInfo: 'this is sub root',
|
||||
x: 100,
|
||||
y: 150,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'cardNodeApp',
|
||||
ip: '127.0.0.1',
|
||||
nodeError: false,
|
||||
dataType: 'subRoot',
|
||||
keyInfo: 'this is sub root',
|
||||
x: 100,
|
||||
y: 250,
|
||||
children: [
|
||||
{
|
||||
name: 'sub',
|
||||
},
|
||||
],
|
||||
id: 'node3',
|
||||
data: {
|
||||
showIcon: false,
|
||||
name: 'cardNodeApp',
|
||||
ip: '127.0.0.1',
|
||||
nodeError: false,
|
||||
dataType: 'subRoot',
|
||||
keyInfo: 'this is sub root',
|
||||
x: 100,
|
||||
y: 250,
|
||||
children: [
|
||||
{
|
||||
name: 'sub',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [],
|
||||
};
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
graph = new ExtGraph({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
autoFit: 'center',
|
||||
data,
|
||||
node: {
|
||||
type: 'card-node',
|
||||
keyShape: {
|
||||
x: 243 / 2,
|
||||
y: 32,
|
||||
width: 243,
|
||||
height: 64,
|
||||
fill: 'transport',
|
||||
},
|
||||
otherShapes: {},
|
||||
},
|
||||
});
|
||||
|
||||
graph.on('node:pointermove', (event) => {
|
||||
const { itemId, target } = event;
|
||||
if (target.id === 'ip-container-shape') {
|
||||
graph.updateData('node', {
|
||||
id: itemId,
|
||||
data: {
|
||||
showIcon: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
graph.on('node:pointerleave', (event) => {
|
||||
const { itemId } = event;
|
||||
graph.updateData('node', {
|
||||
id: itemId,
|
||||
data: {
|
||||
showIcon: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,196 +1,216 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Graph, Extensions, extend, stdLib } from '@antv/g6';
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const ICON_MAP = {
|
||||
a: 'https://gw.alipayobjects.com/mdn/rms_8fd2eb/afts/img/A*0HC-SawWYUoAAAAAAAAAAABkARQnAQ',
|
||||
b: 'https://gw.alipayobjects.com/mdn/rms_8fd2eb/afts/img/A*sxK0RJ1UhNkAAAAAAAAAAABkARQnAQ',
|
||||
};
|
||||
|
||||
G6.registerNode(
|
||||
'card-node',
|
||||
{
|
||||
drawShape: function drawShape(cfg, group) {
|
||||
const color = cfg.error ? '#F4664A' : '#30BF78';
|
||||
const r = 2;
|
||||
const shape = group.addShape('rect', {
|
||||
attrs: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 200,
|
||||
height: 60,
|
||||
stroke: color,
|
||||
radius: r,
|
||||
},
|
||||
// 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: 'main-box',
|
||||
draggable: true,
|
||||
});
|
||||
class CardNode extends Extensions.RectNode {
|
||||
drawOtherShapes(model, shapeMap, diffData, diffState) {
|
||||
const keyShapeBBox = shapeMap.keyShape.getLocalBounds();
|
||||
const x = -keyShapeBBox.halfExtents[0],
|
||||
y = -keyShapeBBox.halfExtents[1];
|
||||
const { data } = model;
|
||||
|
||||
group.addShape('rect', {
|
||||
attrs: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 200,
|
||||
height: 20,
|
||||
fill: color,
|
||||
radius: [r, r, 0, 0],
|
||||
},
|
||||
// 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: 'title-box',
|
||||
draggable: true,
|
||||
});
|
||||
const color = data.error ? '#F4664A' : '#30BF78';
|
||||
const r = 2;
|
||||
|
||||
const otherShapes = {
|
||||
// title box
|
||||
titleBox: this.upsertShape(
|
||||
'rect',
|
||||
'titleBox',
|
||||
{ x: 0, y: 0, width: 200, height: 20, fill: color, radius: [r, r, 0, 0] },
|
||||
shapeMap,
|
||||
model,
|
||||
),
|
||||
// left icon
|
||||
group.addShape('image', {
|
||||
attrs: {
|
||||
nodeIcon: this.upsertShape(
|
||||
'image',
|
||||
'nodeIcon',
|
||||
{
|
||||
x: 4,
|
||||
y: 2,
|
||||
height: 16,
|
||||
width: 16,
|
||||
cursor: 'pointer',
|
||||
img: ICON_MAP[cfg.nodeType || 'app'],
|
||||
img: ICON_MAP[data.nodeType],
|
||||
},
|
||||
// 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: 'node-icon',
|
||||
});
|
||||
|
||||
shapeMap,
|
||||
model,
|
||||
),
|
||||
// title text
|
||||
group.addShape('text', {
|
||||
attrs: {
|
||||
title: this.upsertShape(
|
||||
'text',
|
||||
'title',
|
||||
{
|
||||
textBaseline: 'top',
|
||||
y: 2,
|
||||
x: 24,
|
||||
lineHeight: 20,
|
||||
text: cfg.title,
|
||||
text: data.title,
|
||||
fill: '#fff',
|
||||
fontSize: 12,
|
||||
},
|
||||
// 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: 'title',
|
||||
});
|
||||
shapeMap,
|
||||
model,
|
||||
),
|
||||
};
|
||||
|
||||
if (cfg.nodeLevel > 0) {
|
||||
group.addShape('marker', {
|
||||
attrs: {
|
||||
x: 184,
|
||||
y: 30,
|
||||
r: 6,
|
||||
cursor: 'pointer',
|
||||
symbol: cfg.collapse ? G6.Marker.expand : G6.Marker.collapse,
|
||||
stroke: '#666',
|
||||
lineWidth: 1,
|
||||
},
|
||||
// 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: 'collapse-icon',
|
||||
});
|
||||
}
|
||||
if (data.nodeLevel > 0) {
|
||||
otherShapes.markerShape = this.upsertShape(
|
||||
'path',
|
||||
'markerShape',
|
||||
{
|
||||
cursor: 'pointer',
|
||||
stroke: '#666',
|
||||
lineWidth: 1,
|
||||
fill: '#fff',
|
||||
path: data.collapsed
|
||||
? stdLib.markers.expand(keyShapeBBox.max[0], keyShapeBBox.center[1], 6)
|
||||
: stdLib.markers.collapse(keyShapeBBox.max[0], keyShapeBBox.center[1], 6),
|
||||
},
|
||||
shapeMap,
|
||||
);
|
||||
}
|
||||
|
||||
// The content list
|
||||
cfg.panels.forEach((item, index) => {
|
||||
// name text
|
||||
group.addShape('text', {
|
||||
attrs: {
|
||||
textBaseline: 'top',
|
||||
y: 25,
|
||||
x: 24 + index * 60,
|
||||
lineHeight: 20,
|
||||
text: item.title,
|
||||
fill: 'rgba(0,0,0, 0.4)',
|
||||
},
|
||||
// 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: `index-title-${index}`,
|
||||
});
|
||||
// content list
|
||||
data.panels.forEach((panel, index) => {
|
||||
otherShapes[`panel-title-${index}`] = this.upsertShape(
|
||||
'text',
|
||||
`panel-title-${index}`,
|
||||
{
|
||||
textBaseline: 'top',
|
||||
y: 25,
|
||||
x: 24 + index * 60,
|
||||
lineHeight: 20,
|
||||
text: panel.title,
|
||||
fill: 'rgba(0,0,0,0.4)',
|
||||
fontSize: 12,
|
||||
},
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
otherShapes[`panel-value-${index}`] = this.upsertShape(
|
||||
'text',
|
||||
`panel-value-${index}`,
|
||||
{
|
||||
textBaseline: 'top',
|
||||
y: 42,
|
||||
x: 24 + index * 60,
|
||||
lineHeight: 20,
|
||||
text: panel.value,
|
||||
fill: '#595959',
|
||||
fontSize: 12,
|
||||
},
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
});
|
||||
|
||||
// value text
|
||||
group.addShape('text', {
|
||||
attrs: {
|
||||
textBaseline: 'top',
|
||||
y: 42,
|
||||
x: 24 + index * 60,
|
||||
lineHeight: 20,
|
||||
text: item.value,
|
||||
fill: '#595959',
|
||||
},
|
||||
// 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: `index-title-${index}`,
|
||||
});
|
||||
});
|
||||
return shape;
|
||||
},
|
||||
return otherShapes;
|
||||
}
|
||||
}
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
nodes: {
|
||||
'card-node': CardNode,
|
||||
},
|
||||
'single-node',
|
||||
);
|
||||
});
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
autoFit: 'center',
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node'],
|
||||
default: ['drag-node'],
|
||||
},
|
||||
defaultNode: {
|
||||
type: 'card-node',
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 100,
|
||||
y: 100,
|
||||
nodeType: 'a',
|
||||
title: 'node1',
|
||||
error: true,
|
||||
nodeLevel: 2,
|
||||
panels: [
|
||||
{ title: '成功率', value: '11%' },
|
||||
{ title: '耗时', value: '111' },
|
||||
{ title: '错误数', value: '111' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
data: {
|
||||
x: 100,
|
||||
y: 200,
|
||||
nodeType: 'b',
|
||||
title: 'node2',
|
||||
error: false,
|
||||
nodeLevel: 0,
|
||||
panels: [
|
||||
{ title: '成功率', value: '11%' },
|
||||
{ title: '耗时', value: '111' },
|
||||
{ title: '错误数', value: '111' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node3',
|
||||
data: {
|
||||
x: 100,
|
||||
y: 300,
|
||||
title: 'node3',
|
||||
error: false,
|
||||
nodeType: 'a',
|
||||
nodeLevel: 3,
|
||||
panels: [
|
||||
{ title: '成功率', value: '11%' },
|
||||
{ title: '耗时', value: '111' },
|
||||
{ title: '错误数', value: '111' },
|
||||
],
|
||||
collapse: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
node: (nodeInnerModel) => {
|
||||
const { id, data } = nodeInnerModel;
|
||||
const color = data.error ? '#F4664A' : '#30BF78';
|
||||
const r = 2;
|
||||
return {
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
type: 'card-node',
|
||||
keyShape: {
|
||||
x: 100,
|
||||
y: 30,
|
||||
width: 200,
|
||||
height: 60,
|
||||
fill: '#fff',
|
||||
lineWidth: 1,
|
||||
stroke: color,
|
||||
radius: r,
|
||||
},
|
||||
otherShapes: {},
|
||||
},
|
||||
};
|
||||
},
|
||||
fitView: true,
|
||||
});
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
title: 'node1',
|
||||
error: true,
|
||||
nodeType: 'a',
|
||||
id: 'node1',
|
||||
nodeLevel: 2,
|
||||
panels: [
|
||||
{ title: '成功率', value: '11%' },
|
||||
{ title: '耗时', value: '111' },
|
||||
{ title: '错误数', value: '111' },
|
||||
],
|
||||
x: 100,
|
||||
y: 100,
|
||||
},
|
||||
{
|
||||
title: 'node2',
|
||||
error: false,
|
||||
nodeType: 'b',
|
||||
id: 'node2',
|
||||
nodeLevel: 0,
|
||||
panels: [
|
||||
{ title: '成功率', value: '11%' },
|
||||
{ title: '耗时', value: '111' },
|
||||
{ title: '错误数', value: '111' },
|
||||
],
|
||||
x: 100,
|
||||
y: 200,
|
||||
},
|
||||
{
|
||||
title: 'node3',
|
||||
error: false,
|
||||
nodeType: 'a',
|
||||
id: 'node3',
|
||||
nodeLevel: 3,
|
||||
panels: [
|
||||
{ title: '成功率', value: '11%' },
|
||||
{ title: '耗时', value: '111' },
|
||||
{ title: '错误数', value: '111' },
|
||||
],
|
||||
collapse: true,
|
||||
x: 100,
|
||||
y: 300,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -20,102 +20,6 @@
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*b-g0RoOpI3sAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "jsxNode.js",
|
||||
"title": {
|
||||
"zh": "使用 JSX 自定义节点",
|
||||
"en": "Custom Node with JSX"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*NcHWTKo3sEoAAAAAAAAAAAAAARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "jsxNodeWithAnimate.js",
|
||||
"title": {
|
||||
"zh": "使用 JSX 自定义节点(带动画)",
|
||||
"en": "Custom Node with JSX and animation"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*XfMbSZSrlREAAAAAAAAAAAAAARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "lineChartNode.js",
|
||||
"title": {
|
||||
"zh": "节点中嵌入 G2 的折线图",
|
||||
"en": "G2 Line Node"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*ZmavSL0dRJIAAAAAAAAAAAAAARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "intervalChartNode.js",
|
||||
"title": {
|
||||
"zh": "节点中嵌入 G2 的柱状图,点击节点可切换数据",
|
||||
"en": "G2 Interval Node"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*D00uR5wb__kAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "pointChartNode.js",
|
||||
"title": {
|
||||
"zh": "节点中嵌入 G2 的散点图",
|
||||
"en": "G2 Point Node"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*K7r7S7x-YhcAAAAAAAAAAAAAARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "pieChartNode.js",
|
||||
"title": {
|
||||
"zh": "节点中嵌入 G2 的饼图",
|
||||
"en": "G2 Pie Node"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*wCxXSK4eWPoAAAAAAAAAAAAAARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "svgDom.js",
|
||||
"title": {
|
||||
"zh": "DOM 节点",
|
||||
"en": "Dom Node"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*VgQlQK1MdbIAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "areaChart.js",
|
||||
"title": {
|
||||
"zh": "使用 G 自定义的面积图节点",
|
||||
"en": "Area Chart Node"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*YWuhR5ZjZfkAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "stackChart.js",
|
||||
"title": {
|
||||
"zh": "使用 G 自定义的堆叠图节点",
|
||||
"en": "Stacked Chart Node"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*giZSRafSctgAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "lineChart.js",
|
||||
"title": {
|
||||
"zh": "使用 G 自定义的折线图节点",
|
||||
"en": "Line Chart Node"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*mKmaTIBtVAUAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "barChart.js",
|
||||
"title": {
|
||||
"zh": "使用 G 自定义的南丁格尔图节点",
|
||||
"en": "Nightingale Node"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*lX3gRZWvbp4AAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "pointChart.js",
|
||||
"title": {
|
||||
"zh": "使用 G 自定义的点线图节点",
|
||||
"en": "Point Chart Node"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*M0ycSarMV2sAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "pieChart.js",
|
||||
"title": {
|
||||
@ -123,22 +27,6 @@
|
||||
"en": "Pie Chart Node"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*idENQpnwA1sAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "list.js",
|
||||
"title": {
|
||||
"zh": "列表",
|
||||
"en": "List"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*pxKjQ5dZx80AAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "scrollNode.js",
|
||||
"title": {
|
||||
"zh": "节点内容可滚动",
|
||||
"en": "Scrollable Node"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*n-ipTrTaPD4AAAAAAAAAAAAAARQnAQ"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,108 +1,120 @@
|
||||
import G6 from '@antv/g6';
|
||||
|
||||
/**
|
||||
* Custom a pie chart node
|
||||
* by Shiwu
|
||||
*
|
||||
*/
|
||||
import { Graph, Extensions, extend, stdLib } from '@antv/g6';
|
||||
|
||||
const lightBlue = '#5b8ff9';
|
||||
const lightOrange = '#5ad8a6';
|
||||
|
||||
// register a pie chart node
|
||||
G6.registerNode('pie-node', {
|
||||
draw: (cfg, group) => {
|
||||
const radius = cfg.size / 2; // node radius
|
||||
const inPercentage = cfg.inDegree / cfg.degree; // the ratio of indegree to outdegree
|
||||
const inAngle = inPercentage * Math.PI * 2; // the anble for the indegree fan
|
||||
const inArcEnd = [radius * Math.cos(inAngle), -radius * Math.sin(inAngle)]; // the end position for the in-degree fan
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
class PieNode extends Extensions.CircleNode {
|
||||
drawOtherShapes(model, shapeMap) {
|
||||
const { data: cfg } = model;
|
||||
// node radius
|
||||
const radius = cfg.size / 2;
|
||||
// the ratio of indegree to outdegree
|
||||
const inPercentage = cfg.inDegree / cfg.degree;
|
||||
// the anble for the indegree fan
|
||||
const inAngle = inPercentage * Math.PI * 2;
|
||||
// the end position for the in-degree fan
|
||||
const inArcEnd = [radius * Math.cos(inAngle), -radius * Math.sin(inAngle)];
|
||||
let isInBigArc = 0,
|
||||
isOutBigArc = 1;
|
||||
if (inAngle > Math.PI) {
|
||||
isInBigArc = 1;
|
||||
isOutBigArc = 0;
|
||||
}
|
||||
// fan shape for the in degree
|
||||
const fanIn = group.addShape('path', {
|
||||
attrs: {
|
||||
path: [
|
||||
['M', radius, 0],
|
||||
['A', radius, radius, 0, isInBigArc, 0, inArcEnd[0], inArcEnd[1]],
|
||||
['L', 0, 0],
|
||||
['Z'],
|
||||
],
|
||||
lineWidth: 0,
|
||||
fill: lightOrange,
|
||||
},
|
||||
// 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: 'in-fan-shape',
|
||||
});
|
||||
// draw the fan shape
|
||||
group.addShape('path', {
|
||||
attrs: {
|
||||
path: [
|
||||
['M', inArcEnd[0], inArcEnd[1]],
|
||||
['A', radius, radius, 0, isOutBigArc, 0, radius, 0],
|
||||
['L', 0, 0],
|
||||
['Z'],
|
||||
],
|
||||
lineWidth: 0,
|
||||
fill: lightBlue,
|
||||
},
|
||||
// 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: 'out-fan-shape',
|
||||
});
|
||||
// 返回 keyshape
|
||||
return fanIn;
|
||||
|
||||
const otherShapes = {
|
||||
['in-fan-shape']: this.upsertShape(
|
||||
'path',
|
||||
'in-fan-shape',
|
||||
{
|
||||
path: [
|
||||
['M', radius, 0],
|
||||
['A', radius, radius, 0, isInBigArc, 0, inArcEnd[0], inArcEnd[1]],
|
||||
['L', 0, 0],
|
||||
['Z'],
|
||||
],
|
||||
lineWidth: 0,
|
||||
fill: lightOrange,
|
||||
},
|
||||
shapeMap,
|
||||
model,
|
||||
),
|
||||
['out-fan-shape']: this.upsertShape(
|
||||
'path',
|
||||
'out-fan-shape',
|
||||
{
|
||||
path: [
|
||||
['M', inArcEnd[0], inArcEnd[1]],
|
||||
['A', radius, radius, 0, isOutBigArc, 0, radius, 0],
|
||||
['L', 0, 0],
|
||||
['Z'],
|
||||
],
|
||||
lineWidth: 0,
|
||||
fill: lightBlue,
|
||||
},
|
||||
shapeMap,
|
||||
model,
|
||||
),
|
||||
};
|
||||
return otherShapes;
|
||||
}
|
||||
}
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
nodes: {
|
||||
'pie-node': PieNode,
|
||||
},
|
||||
});
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'pie1',
|
||||
size: 80,
|
||||
inDegree: 80,
|
||||
degree: 360,
|
||||
x: 150,
|
||||
y: 150,
|
||||
},
|
||||
{
|
||||
id: 'pie2',
|
||||
size: 80,
|
||||
inDegree: 280,
|
||||
degree: 360,
|
||||
x: 350,
|
||||
y: 150,
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
source: 'pie1',
|
||||
target: 'pie2',
|
||||
},
|
||||
],
|
||||
};
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
linkCenter: true,
|
||||
defaultNode: {
|
||||
autoFit: 'center',
|
||||
modes: {
|
||||
default: ['drag-node'],
|
||||
},
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
data: {
|
||||
size: 80,
|
||||
inDegree: 80,
|
||||
degree: 360,
|
||||
x: 150,
|
||||
y: 150,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
data: {
|
||||
size: 80,
|
||||
inDegree: 280,
|
||||
degree: 360,
|
||||
x: 350,
|
||||
y: 150,
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [{ id: 'edge1', source: 'node1', target: 'node2' }],
|
||||
},
|
||||
|
||||
node: {
|
||||
type: 'pie-node',
|
||||
otherShapes: {},
|
||||
},
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,30 +1,37 @@
|
||||
import G6 from '@antv/g6';
|
||||
/**
|
||||
* Custom a pie chart node
|
||||
*/
|
||||
import { Graph, Extensions, extend, stdLib } from '@antv/g6';
|
||||
|
||||
// Custom a mark node
|
||||
G6.registerNode('justPoints', {
|
||||
draw(cfg, group) {
|
||||
class PointChartNode extends Extensions.CircleNode {
|
||||
drawOtherShapes(model, shapeMap) {
|
||||
const { data: cfg } = model;
|
||||
const baseR = 30;
|
||||
let nowAngle = 0;
|
||||
|
||||
const otherShapes = {};
|
||||
|
||||
// Ref line
|
||||
let refR = baseR;
|
||||
const refInc = 10;
|
||||
for (let i = 0; i < 5; i++) {
|
||||
group.addShape('circle', {
|
||||
attrs: {
|
||||
otherShapes[`circle-shape-${i}`] = this.upsertShape(
|
||||
'circle',
|
||||
`circle-shape-${i}`,
|
||||
{
|
||||
x: 0,
|
||||
y: 0,
|
||||
r: (refR += refInc),
|
||||
stroke: '#5ad8a6',
|
||||
lineDash: [4, 4],
|
||||
},
|
||||
// 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: 'circle-shape',
|
||||
});
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
}
|
||||
const everyIncAngle = (2 * Math.PI * (360 / 5 / 5)) / 360;
|
||||
nowAngle = nowAngle + everyIncAngle / 2;
|
||||
cfg.details.forEach((cat) => {
|
||||
cfg.details.forEach((cat, dIndex) => {
|
||||
// Calculate the positions for vertexes
|
||||
const postions = [];
|
||||
cat.values.forEach((item, index) => {
|
||||
@ -41,19 +48,18 @@ G6.registerNode('justPoints', {
|
||||
}
|
||||
});
|
||||
|
||||
console.log('postions', postions);
|
||||
|
||||
// add marks
|
||||
postions.forEach((pos, index) => {
|
||||
if (index !== 5) {
|
||||
group.addShape('circle', {
|
||||
attrs: {
|
||||
x: pos[0],
|
||||
y: pos[1],
|
||||
r: 3,
|
||||
lineWidth: 2,
|
||||
stroke: cat.color,
|
||||
},
|
||||
name: 'circle-marker-shape',
|
||||
});
|
||||
postions.forEach((pos, i) => {
|
||||
if (i !== 5) {
|
||||
otherShapes[`circle-marker-shape-${dIndex}-${i}`] = this.upsertShape(
|
||||
'circle',
|
||||
`circle-marker-shape-${dIndex}-${i}`,
|
||||
{ x: 20 * i, y: 30 * i, r: 3, lineWidth: 2, stroke: cat.color, zIndex: 20, fill: 'red' },
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -65,8 +71,10 @@ G6.registerNode('justPoints', {
|
||||
const xPos = r * Math.cos(nowAngle2);
|
||||
const yPos = r * Math.sin(nowAngle2);
|
||||
|
||||
group.addShape('path', {
|
||||
attrs: {
|
||||
otherShapes[`path-shape-${i}`] = this.upsertShape(
|
||||
'path',
|
||||
`path-shape-${i}`,
|
||||
{
|
||||
path: [
|
||||
['M', 0, 0],
|
||||
['L', xPos, yPos],
|
||||
@ -74,109 +82,103 @@ G6.registerNode('justPoints', {
|
||||
lineDash: [4, 4],
|
||||
stroke: '#5ad8a6',
|
||||
},
|
||||
// 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',
|
||||
});
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
nowAngle2 += everyIncAngleCat;
|
||||
}
|
||||
// add a circle with the same color with the background color
|
||||
group.addShape('circle', {
|
||||
attrs: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
r: baseR,
|
||||
fill: cfg.centerColor,
|
||||
stroke: 'darkgray',
|
||||
},
|
||||
// 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: 'circle-shape',
|
||||
});
|
||||
otherShapes['circle-shape'] = this.upsertShape(
|
||||
'circle',
|
||||
'circle-shape',
|
||||
{ x: 0, y: 0, r: baseR, fill: cfg.centerColor, stroke: 'darkgray' },
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
|
||||
if (cfg.label) {
|
||||
group.addShape('text', {
|
||||
attrs: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
textAlign: 'center',
|
||||
textBaseline: 'middle',
|
||||
text: cfg.label,
|
||||
fill: '#fff',
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
// 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: 'text-shape',
|
||||
});
|
||||
otherShapes['text-shape'] = this.upsertShape(
|
||||
'circle',
|
||||
'text-shape',
|
||||
{ x: 0, y: 0, textAlign: 'center', textBaseline: 'middle', text: cfg.label, fill: '#fff', fontStyle: 'bold' },
|
||||
shapeMap,
|
||||
model,
|
||||
);
|
||||
}
|
||||
return group;
|
||||
console.log('otherShapes', otherShapes);
|
||||
return otherShapes;
|
||||
}
|
||||
}
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
nodes: {
|
||||
'point-chart-node': PointChartNode,
|
||||
},
|
||||
});
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
autoFit: 'center',
|
||||
modes: {
|
||||
default: ['drag-node'],
|
||||
},
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'point1',
|
||||
data: {
|
||||
x: 150,
|
||||
y: 150,
|
||||
details: [
|
||||
{ cat: 'pv', values: [20, 30, 40, 30, 30], color: '#5B8FF9' },
|
||||
{ cat: 'dal', values: [40, 30, 20, 30, 50], color: '#5AD8A6' },
|
||||
{ cat: 'uv', values: [40, 30, 30, 40, 40], color: '#5D7092' },
|
||||
{ cat: 'sal', values: [20, 30, 50, 20, 20], color: '#F6BD16' },
|
||||
{ cat: 'cal', values: [10, 10, 20, 20, 20], color: '#E8684A' },
|
||||
],
|
||||
centerColor: '#5b8ff9',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'point2',
|
||||
data: {
|
||||
x: 500,
|
||||
y: 150,
|
||||
details: [
|
||||
{ cat: 'pv', values: [10, 10, 50, 20, 10], color: '#5ad8a6' },
|
||||
{ cat: 'dal', values: [20, 30, 10, 50, 40], color: '#ff99c3' },
|
||||
{ cat: 'uv', values: [10, 50, 30, 20, 30], color: '#6dc8ec' },
|
||||
{ cat: 'sal', values: [50, 30, 20, 20, 20], color: '#269a99' },
|
||||
{ cat: 'cal', values: [50, 10, 20, 50, 30], color: '#9270CA' },
|
||||
],
|
||||
centerColor: '#5b8ff9',
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [{ id: 'edge1', source: 'point1', target: 'point2' }],
|
||||
},
|
||||
|
||||
node: {
|
||||
type: 'point-chart-node',
|
||||
iconShape: {
|
||||
text: {
|
||||
fields: ['id'],
|
||||
formatter: (model) => model.id,
|
||||
},
|
||||
},
|
||||
otherShapes: {},
|
||||
},
|
||||
});
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'nodeC',
|
||||
x: 150,
|
||||
y: 150,
|
||||
label: 'Point2',
|
||||
type: 'justPoints',
|
||||
anchorPoints: [
|
||||
[0, 0.5],
|
||||
[1, 0.5],
|
||||
],
|
||||
details: [
|
||||
{ cat: 'pv', values: [20, 30, 40, 30, 30], color: '#5B8FF9' },
|
||||
{ cat: 'dal', values: [40, 30, 20, 30, 50], color: '#5AD8A6' },
|
||||
{ cat: 'uv', values: [40, 30, 30, 40, 40], color: '#5D7092' },
|
||||
{ cat: 'sal', values: [20, 30, 50, 20, 20], color: '#F6BD16' },
|
||||
{ cat: 'cal', values: [10, 10, 20, 20, 20], color: '#E8684A' },
|
||||
],
|
||||
centerColor: '#5b8ff9',
|
||||
},
|
||||
{
|
||||
id: 'nodeC2',
|
||||
x: 500,
|
||||
y: 150,
|
||||
label: 'Point2',
|
||||
type: 'justPoints',
|
||||
anchorPoints: [
|
||||
[0, 0.5],
|
||||
[1, 0.5],
|
||||
],
|
||||
details: [
|
||||
{ cat: 'pv', values: [10, 10, 50, 20, 10], color: '#5ad8a6' },
|
||||
{ cat: 'dal', values: [20, 30, 10, 50, 40], color: '#ff99c3' },
|
||||
{ cat: 'uv', values: [10, 50, 30, 20, 30], color: '#6dc8ec' },
|
||||
{ cat: 'sal', values: [50, 30, 20, 20, 20], color: '#269a99' },
|
||||
{ cat: 'cal', values: [50, 10, 20, 50, 30], color: '#9270CA' },
|
||||
],
|
||||
centerColor: '#5b8ff9',
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
source: 'nodeC',
|
||||
target: 'nodeC2',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight);
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,109 +1,116 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Graph } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
x: 250,
|
||||
y: 150,
|
||||
comboId: 'combo',
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
x: 350,
|
||||
y: 150,
|
||||
comboId: 'combo',
|
||||
},
|
||||
{
|
||||
id: 'node3',
|
||||
x: 250,
|
||||
y: 300,
|
||||
comboId: 'combo2',
|
||||
},
|
||||
{
|
||||
id: 'node4',
|
||||
x: 450,
|
||||
y: 300,
|
||||
comboId: 'combo2',
|
||||
},
|
||||
],
|
||||
combos: [
|
||||
{
|
||||
id: 'combo',
|
||||
label: 'Combo',
|
||||
},
|
||||
{
|
||||
id: 'combo2',
|
||||
label: 'with substitute icon while collapsed',
|
||||
collapsed: true,
|
||||
collapsedSubstituteIcon: {
|
||||
show: true,
|
||||
img: 'https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*IEQFS5VtXX8AAAAAAAAAAABkARQnAQ',
|
||||
width: 72,
|
||||
height: 72
|
||||
}
|
||||
},
|
||||
],
|
||||
};
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
const graph = new Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
// Set groupByTypes to false to get rendering result with reasonable visual zIndex for combos
|
||||
groupByTypes: false,
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node', 'drag-combo', 'collapse-expand-combo'],
|
||||
stackCfg: {
|
||||
ignoreStateChange: true,
|
||||
},
|
||||
defaultCombo: {
|
||||
type: 'circle',
|
||||
/* style for the keyShape */
|
||||
// style: {
|
||||
// lineWidth: 1,
|
||||
// },
|
||||
labelCfg: {
|
||||
/* label's offset to the keyShape */
|
||||
// refY: 10,
|
||||
/* label's position, options: center, top, bottom, left, right */
|
||||
position: 'top',
|
||||
/* label's style */
|
||||
// style: {
|
||||
// fontSize: 18,
|
||||
// },
|
||||
node: {
|
||||
labelShape: {
|
||||
position: 'center',
|
||||
text: {
|
||||
fields: ['id'],
|
||||
formatter: (model) => model.id,
|
||||
},
|
||||
},
|
||||
animates: {
|
||||
update: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
/* styles for different states, there are built-in styles for states: active, inactive, selected, highlight, disable */
|
||||
/* you can extend it or override it as you want */
|
||||
// comboStateStyles: {
|
||||
// active: {
|
||||
// fill: '#f00',
|
||||
// opacity: 0.5
|
||||
// },
|
||||
// },
|
||||
combo: (model) => {
|
||||
return {
|
||||
id: model.id,
|
||||
data: {
|
||||
type: 'circle-combo',
|
||||
...model.data,
|
||||
keyShape: {
|
||||
padding: [10, 20, 30, 40],
|
||||
r: 50,
|
||||
},
|
||||
labelShape: {
|
||||
text: model.id,
|
||||
},
|
||||
|
||||
animates: {
|
||||
buildIn: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 500,
|
||||
delay: 500 + Math.random() * 500,
|
||||
},
|
||||
],
|
||||
buildOut: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 200,
|
||||
},
|
||||
],
|
||||
update: [
|
||||
{
|
||||
fields: ['lineWidth', 'r'],
|
||||
shapeId: 'keyShape',
|
||||
},
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
data: {
|
||||
nodes: [
|
||||
{ id: 'node1', data: { x: 250, y: 150, parentId: 'combo1' } },
|
||||
{ id: 'node2', data: { x: 350, y: 150, parentId: 'combo1' } },
|
||||
{ id: 'node3', data: { x: 250, y: 300, parentId: 'combo2' } },
|
||||
],
|
||||
edges: [],
|
||||
combos: [
|
||||
{ id: 'combo1', data: { parentId: 'combo2' } }, // collapsed: true
|
||||
{ id: 'combo2', data: {} },
|
||||
],
|
||||
},
|
||||
modes: {
|
||||
default: [
|
||||
'collapse-expand-combo',
|
||||
{
|
||||
type: 'drag-node',
|
||||
enableTransient: false,
|
||||
updateComboStructure: false,
|
||||
},
|
||||
'drag-canvas',
|
||||
{
|
||||
type: 'click-select',
|
||||
itemTypes: ['node', 'edge', 'combo'],
|
||||
},
|
||||
{
|
||||
type: 'hover-activate',
|
||||
itemTypes: ['node', 'edge', 'combo'],
|
||||
},
|
||||
{
|
||||
type: 'drag-combo',
|
||||
enableTransient: true,
|
||||
updateComboStructure: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('combo:mouseenter', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', true);
|
||||
});
|
||||
|
||||
graph.on('combo:mouseleave', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', false);
|
||||
});
|
||||
graph.on('combo:click', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'selected', true);
|
||||
});
|
||||
graph.on('canvas:click', (evt) => {
|
||||
graph.getCombos().forEach((combo) => {
|
||||
graph.clearItemStates(combo);
|
||||
});
|
||||
});
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,111 +1,116 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Graph } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
x: 250,
|
||||
y: 150,
|
||||
comboId: 'combo',
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
x: 350,
|
||||
y: 150,
|
||||
comboId: 'combo',
|
||||
},
|
||||
{
|
||||
id: 'node3',
|
||||
x: 250,
|
||||
y: 300,
|
||||
comboId: 'combo2',
|
||||
},
|
||||
{
|
||||
id: 'node4',
|
||||
x: 450,
|
||||
y: 300,
|
||||
comboId: 'combo2',
|
||||
},
|
||||
],
|
||||
combos: [
|
||||
{
|
||||
id: 'combo',
|
||||
label: 'Combo',
|
||||
},
|
||||
{
|
||||
id: 'combo2',
|
||||
label: 'with substitute icon while collapsed',
|
||||
collapsed: true,
|
||||
collapsedSubstituteIcon: {
|
||||
show: true,
|
||||
img: 'https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*IEQFS5VtXX8AAAAAAAAAAABkARQnAQ',
|
||||
width: 72,
|
||||
height: 72
|
||||
}
|
||||
},
|
||||
],
|
||||
};
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
const graph = new Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
// Set groupByTypes to false to get rendering result with reasonable visual zIndex for combos
|
||||
groupByTypes: false,
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node', 'drag-combo', 'collapse-expand-combo'],
|
||||
stackCfg: {
|
||||
ignoreStateChange: true,
|
||||
},
|
||||
defaultCombo: {
|
||||
type: 'rect',
|
||||
/* The minimum size of the combo. combo 最小大小 */
|
||||
size: [50, 50],
|
||||
/* style for the keyShape */
|
||||
// style: {
|
||||
// lineWidth: 1,
|
||||
// },
|
||||
labelCfg: {
|
||||
/* label's offset to the keyShape */
|
||||
// refY: 10,
|
||||
/* label's position, options: center, top, bottom, left, right */
|
||||
position: 'top',
|
||||
/* label's style */
|
||||
// style: {
|
||||
// fontSize: 18,
|
||||
// },
|
||||
node: {
|
||||
labelShape: {
|
||||
position: 'center',
|
||||
text: {
|
||||
fields: ['id'],
|
||||
formatter: (model) => model.id,
|
||||
},
|
||||
},
|
||||
animates: {
|
||||
update: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
/* styles for different states, there are built-in styles for states: active, inactive, selected, highlight, disable */
|
||||
/* you can extend it or override it as you want */
|
||||
// comboStateStyles: {
|
||||
// active: {
|
||||
// fill: '#f00',
|
||||
// opacity: 0.5
|
||||
// },
|
||||
// },
|
||||
combo: (model) => {
|
||||
return {
|
||||
id: model.id,
|
||||
data: {
|
||||
type: 'rect-combo',
|
||||
...model.data,
|
||||
keyShape: {
|
||||
padding: [10, 20, 30, 40],
|
||||
r: 50,
|
||||
},
|
||||
labelShape: {
|
||||
text: model.id,
|
||||
},
|
||||
|
||||
animates: {
|
||||
buildIn: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 500,
|
||||
delay: 500 + Math.random() * 500,
|
||||
},
|
||||
],
|
||||
buildOut: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 200,
|
||||
},
|
||||
],
|
||||
update: [
|
||||
{
|
||||
fields: ['lineWidth', 'r'],
|
||||
shapeId: 'keyShape',
|
||||
},
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
data: {
|
||||
nodes: [
|
||||
{ id: 'node1', data: { x: 250, y: 150, parentId: 'combo1' } },
|
||||
{ id: 'node2', data: { x: 350, y: 150, parentId: 'combo1' } },
|
||||
{ id: 'node3', data: { x: 250, y: 300, parentId: 'combo2' } },
|
||||
],
|
||||
edges: [],
|
||||
combos: [
|
||||
{ id: 'combo1', data: { parentId: 'combo2' } },
|
||||
{ id: 'combo2', data: {} },
|
||||
],
|
||||
},
|
||||
modes: {
|
||||
default: [
|
||||
'collapse-expand-combo',
|
||||
{
|
||||
type: 'drag-node',
|
||||
enableTransient: false,
|
||||
updateComboStructure: false,
|
||||
},
|
||||
'drag-canvas',
|
||||
{
|
||||
type: 'click-select',
|
||||
itemTypes: ['node', 'edge', 'combo'],
|
||||
},
|
||||
{
|
||||
type: 'hover-activate',
|
||||
itemTypes: ['node', 'edge', 'combo'],
|
||||
},
|
||||
{
|
||||
type: 'drag-combo',
|
||||
enableTransient: true,
|
||||
updateComboStructure: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('combo:mouseenter', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', true);
|
||||
});
|
||||
|
||||
graph.on('combo:mouseleave', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', false);
|
||||
});
|
||||
graph.on('combo:click', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'selected', true);
|
||||
});
|
||||
graph.on('canvas:click', (evt) => {
|
||||
graph.getCombos().forEach((combo) => {
|
||||
graph.clearItemStates(combo);
|
||||
});
|
||||
});
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,107 +0,0 @@
|
||||
import G6 from '@antv/g6';
|
||||
|
||||
/**
|
||||
* The usage of arc edge
|
||||
* by Shiwu
|
||||
*/
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: '0',
|
||||
x: 150,
|
||||
y: 50,
|
||||
},
|
||||
{
|
||||
id: '1',
|
||||
x: 350,
|
||||
y: 250,
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
// Built-in arc edges
|
||||
{
|
||||
id: 'edge0',
|
||||
source: '0',
|
||||
target: '1',
|
||||
label: 'curveOffset = 20',
|
||||
curveOffset: 20,
|
||||
},
|
||||
{
|
||||
id: 'edge1',
|
||||
source: '0',
|
||||
target: '1',
|
||||
label: 'curveOffset = 50', // the bending degree
|
||||
curveOffset: 50,
|
||||
},
|
||||
{
|
||||
id: 'edge2',
|
||||
source: '0',
|
||||
target: '1',
|
||||
label: 'curveOffset = -50', // the bending degree
|
||||
curveOffset: -50,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
linkCenter: true,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
modes: {
|
||||
// supported behavior
|
||||
default: ['drag-node'],
|
||||
},
|
||||
defaultEdge: {
|
||||
type: 'arc',
|
||||
/* you can configure the global edge style as following lines */
|
||||
// style: {
|
||||
// stroke: '#F6BD16',
|
||||
// },
|
||||
labelCfg: {
|
||||
autoRotate: true,
|
||||
refY: -10,
|
||||
},
|
||||
},
|
||||
/* styles for different states, there are built-in styles for states: active, inactive, selected, highlight, disable */
|
||||
// edgeStateStyles: {
|
||||
// // edge style of active state
|
||||
// active: {
|
||||
// opacity: 0.5,
|
||||
// stroke: '#f00'
|
||||
// },
|
||||
// // edge style of selected state
|
||||
// selected: {
|
||||
// stroke: '#ff0'
|
||||
// lineWidth: 3,
|
||||
// },
|
||||
// },
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('edge:mouseenter', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', true);
|
||||
});
|
||||
|
||||
graph.on('edge:mouseleave', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', false);
|
||||
});
|
||||
|
||||
graph.on('edge:click', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'selected', true);
|
||||
});
|
||||
graph.on('canvas:click', (evt) => {
|
||||
graph.getEdges().forEach((edge) => {
|
||||
graph.clearItemStates(edge);
|
||||
});
|
||||
});
|
74
packages/site/examples/item/defaultEdges/demo/cubic.js
Normal file
74
packages/site/examples/item/defaultEdges/demo/cubic.js
Normal file
@ -0,0 +1,74 @@
|
||||
import { Graph, Extensions, extend } from '@antv/g6';
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
edges: {
|
||||
'cubic-edge': Extensions.CubicEdge,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
modes: {
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'],
|
||||
},
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 150,
|
||||
y: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
data: { x: 250, y: 200 },
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
data: {
|
||||
type: 'cubic-edge',
|
||||
keyShape: {
|
||||
/**
|
||||
* 控制点数组,默认:曲线中心附近
|
||||
* precise x-axis, y-axis coordinates of control points. Default is center of the curve
|
||||
*/
|
||||
controlPoints: [],
|
||||
/**
|
||||
* 控制点在两端点连线上的相对位置,范围 0 - 1
|
||||
* Relative position of the control point on the line between the two endpoints, range 0 - 1
|
||||
*/
|
||||
curvePosition: 0.5,
|
||||
/**
|
||||
* 控制点距离两端点连线的距离,可理解为控制边的弯曲程度
|
||||
* Distance of the control point from the line connecting the two end points
|
||||
*/
|
||||
curveOffset: 30,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
edge: {
|
||||
keyShape: {
|
||||
endArrow: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
@ -1,131 +0,0 @@
|
||||
import G6 from '@antv/g6';
|
||||
|
||||
/**
|
||||
* The usage of cubic edge
|
||||
* **/
|
||||
|
||||
G6.registerNode(
|
||||
'my-rect',
|
||||
{
|
||||
getAnchorPoints: function getAnchorPoints() {
|
||||
return [
|
||||
[0.5, 0],
|
||||
[0.5, 1],
|
||||
];
|
||||
},
|
||||
},
|
||||
'rect',
|
||||
);
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node0',
|
||||
x: 200,
|
||||
y: 10,
|
||||
size: 20,
|
||||
},
|
||||
{
|
||||
id: 'node1',
|
||||
x: 200,
|
||||
y: 50,
|
||||
label: '1222',
|
||||
type: 'my-rect',
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
x: 150,
|
||||
y: 150,
|
||||
type: 'my-rect',
|
||||
},
|
||||
{
|
||||
id: 'node3',
|
||||
x: 250,
|
||||
y: 150,
|
||||
type: 'my-rect',
|
||||
},
|
||||
{
|
||||
id: 'node4',
|
||||
x: 200,
|
||||
y: 250,
|
||||
type: 'my-rect',
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
source: 'node0',
|
||||
target: 'node1',
|
||||
},
|
||||
{
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
},
|
||||
{
|
||||
source: 'node1',
|
||||
target: 'node3',
|
||||
},
|
||||
{
|
||||
source: 'node2',
|
||||
target: 'node4',
|
||||
},
|
||||
{
|
||||
source: 'node3',
|
||||
target: 'node4',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
modes: {
|
||||
default: ['drag-canvas'],
|
||||
},
|
||||
defaultEdge: {
|
||||
type: 'cubic-vertical',
|
||||
/* you can configure the global edge style as following lines */
|
||||
// style: {
|
||||
// stroke: '#F6BD16',
|
||||
// },
|
||||
},
|
||||
/* styles for different states, there are built-in styles for states: active, inactive, selected, highlight, disable */
|
||||
// edgeStateStyles: {
|
||||
// // edge style of active state
|
||||
// active: {
|
||||
// opacity: 0.5,
|
||||
// stroke: '#f00'
|
||||
// },
|
||||
// // edge style of selected state
|
||||
// selected: {
|
||||
// stroke: '#ff0'
|
||||
// lineWidth: 3,
|
||||
// },
|
||||
// },
|
||||
});
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('edge:mouseenter', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', true);
|
||||
});
|
||||
|
||||
graph.on('edge:mouseleave', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', false);
|
||||
});
|
||||
|
||||
graph.on('edge:click', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'selected', true);
|
||||
});
|
||||
graph.on('canvas:click', (evt) => {
|
||||
graph.getEdges().forEach((edge) => {
|
||||
graph.clearItemStates(edge);
|
||||
});
|
||||
});
|
@ -1,107 +0,0 @@
|
||||
import G6 from '@antv/g6';
|
||||
|
||||
/**
|
||||
* The usage of cubic edge
|
||||
*
|
||||
* **/
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node5',
|
||||
x: 150,
|
||||
y: 200,
|
||||
label: '5',
|
||||
anchorPoints: [
|
||||
[0, 0.5],
|
||||
[1, 0.5],
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'node6',
|
||||
x: 300,
|
||||
y: 150,
|
||||
label: '6',
|
||||
anchorPoints: [
|
||||
[0, 0.5],
|
||||
[1, 0.5],
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'node7',
|
||||
x: 300,
|
||||
y: 250,
|
||||
label: '7',
|
||||
anchorPoints: [
|
||||
[0, 0.5],
|
||||
[1, 0.5],
|
||||
],
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
source: 'node5',
|
||||
target: 'node6',
|
||||
type: 'cubic-horizontal',
|
||||
},
|
||||
{
|
||||
source: 'node5',
|
||||
target: 'node7',
|
||||
type: 'cubic-horizontal',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
modes: {
|
||||
default: ['drag-canvas'],
|
||||
},
|
||||
defaultEdge: {
|
||||
type: 'cubic-horizontal',
|
||||
/* you can configure the global edge style as following lines */
|
||||
// style: {
|
||||
// stroke: '#F6BD16',
|
||||
// },
|
||||
},
|
||||
/* styles for different states, there are built-in styles for states: active, inactive, selected, highlight, disable */
|
||||
// edgeStateStyles: {
|
||||
// // edge style of active state
|
||||
// active: {
|
||||
// opacity: 0.5,
|
||||
// stroke: '#f00'
|
||||
// },
|
||||
// // edge style of selected state
|
||||
// selected: {
|
||||
// stroke: '#ff0'
|
||||
// lineWidth: 3,
|
||||
// },
|
||||
// },
|
||||
});
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('edge:mouseenter', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', true);
|
||||
});
|
||||
|
||||
graph.on('edge:mouseleave', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', false);
|
||||
});
|
||||
|
||||
graph.on('edge:click', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'selected', true);
|
||||
});
|
||||
graph.on('canvas:click', (evt) => {
|
||||
graph.getEdges().forEach((edge) => {
|
||||
graph.clearItemStates(edge);
|
||||
});
|
||||
});
|
@ -0,0 +1,87 @@
|
||||
import { Graph, Extensions, extend } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 100,
|
||||
y: 150,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
data: { x: 200, y: 100 },
|
||||
},
|
||||
{
|
||||
id: 'node3',
|
||||
data: { x: 200, y: 200 },
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
},
|
||||
{
|
||||
id: 'edge2',
|
||||
source: 'node1',
|
||||
target: 'node3',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
edges: {
|
||||
'cubic-horizontal-edge': Extensions.CubicHorizontalEdge,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
modes: {
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'],
|
||||
},
|
||||
data,
|
||||
node: {
|
||||
anchorPoints: [
|
||||
[0, 0.5],
|
||||
[1, 0.5],
|
||||
],
|
||||
},
|
||||
edge: {
|
||||
type: 'cubic-horizontal-edge',
|
||||
keyShape: {
|
||||
/**
|
||||
* 控制点数组,默认:曲线 1/3 和 2/3 处
|
||||
* precise x-axis, y-axis coordinates of control points. Default: 1/3 and 2/3 of the curve
|
||||
*/
|
||||
// controlPoints: [],
|
||||
/**
|
||||
* 控制点在两端点连线上的相对位置,范围 0 - 1
|
||||
* Relative position of the control point on the line between the two endpoints, range 0 - 1
|
||||
*/
|
||||
// curvePosition: 0.5,
|
||||
/**
|
||||
* 控制点距离两端点连线的距离,可理解为控制边的弯曲程度
|
||||
* Distance of the control point from the line connecting the two end points
|
||||
*/
|
||||
// curveOffset: 30,
|
||||
endArrow: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
@ -1,96 +1,105 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Graph, Extensions, extend } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: '0',
|
||||
x: 150,
|
||||
y: 150,
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 100,
|
||||
y: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '1',
|
||||
x: 350,
|
||||
y: 150,
|
||||
id: 'node2',
|
||||
data: { x: 200, y: 100, type: 'rect-node' },
|
||||
},
|
||||
{
|
||||
id: 'node3',
|
||||
data: {
|
||||
x: 300,
|
||||
y: 100,
|
||||
type: 'ellipse-node',
|
||||
keyShape: {
|
||||
rx: 20,
|
||||
ry: 16,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
// 内置 loop
|
||||
{
|
||||
source: '0',
|
||||
target: '0',
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node1',
|
||||
},
|
||||
{
|
||||
source: '1',
|
||||
target: '1',
|
||||
id: 'edge2',
|
||||
source: 'node2',
|
||||
target: 'node2',
|
||||
},
|
||||
{
|
||||
id: 'edge3',
|
||||
source: 'node3',
|
||||
target: 'node3',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
nodes: {
|
||||
'ellipse-node': Extensions.EllipseNode,
|
||||
},
|
||||
edges: {
|
||||
'loop-edge': Extensions.LoopEdge,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center
|
||||
fitCenter: true,
|
||||
modes: {
|
||||
// supported behavior
|
||||
default: ['drag-node'],
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'],
|
||||
},
|
||||
defaultEdge: {
|
||||
type: 'loop',
|
||||
/* you can configure the global edge style as following lines */
|
||||
style: {
|
||||
// stroke: '#F6BD16',
|
||||
endArrow: {
|
||||
path: 'M 0,0 L 20,10 L 20,-10 Z',
|
||||
fill: '#eee',
|
||||
data,
|
||||
edge: {
|
||||
type: 'loop-edge',
|
||||
keyShape: {
|
||||
...data.keyShape,
|
||||
loopCfg: {
|
||||
/**
|
||||
* 指定自环与节点的相对位置。默认为:top。支持的值有:top, top-right, right,bottom-right, bottom, bottom-left, left, top-left
|
||||
* The relative position relationship between self-loop edge and keyShape.
|
||||
*/
|
||||
// position: 'top',
|
||||
/**
|
||||
* 指定是否顺时针画环,默认为 true
|
||||
* Specifies whether to draw the loop clockwise, default is true.
|
||||
*/
|
||||
// clockwise: true,
|
||||
/**
|
||||
* 从节点 keyShape 的边缘到自环最顶端的位置,用于指定自环的曲度,默认为节点的高度
|
||||
* The position from edge of keyShape to the topmost point of the self-loop, defaults to the height of the node.
|
||||
*/
|
||||
// dist: 100,
|
||||
/**
|
||||
* 对于非圆形节点设置的连接点与节点中心坐标(top-right,bottom-right,top-left,bottom-left较特殊,为四个角坐标)在 x 轴或 y 轴方向的偏移量,默认为 `节点宽高中最小值的1/4`
|
||||
* Except circle, the offset of the connection point from center (top-right, bottom-right, top-left, bottom-left, and more specifically, the four corners of the node) in the x/y-axis direction is set to be `¼ of the smallest value of the node's width or height'.
|
||||
*/
|
||||
// pointPadding: 20,
|
||||
},
|
||||
},
|
||||
// 更多关于 loop 的配置请参考http://antv.alipay.com/zh/docs/manual/middle/elements/edges/loop/#%E8%87%AA%E7%8E%AF%E7%89%B9%E6%AE%8A%E9%85%8D%E7%BD%AE-loopcfg
|
||||
loopCfg: {
|
||||
position: 'top',
|
||||
endArrow: true,
|
||||
},
|
||||
},
|
||||
/* styles for different states, there are built-in styles for states: active, inactive, selected, highlight, disable */
|
||||
// edgeStateStyles: {
|
||||
// // edge style of active state
|
||||
// active: {
|
||||
// opacity: 0.5,
|
||||
// stroke: '#f00'
|
||||
// },
|
||||
// // edge style of selected state
|
||||
// selected: {
|
||||
// stroke: '#ff0'
|
||||
// lineWidth: 3,
|
||||
// },
|
||||
// },
|
||||
});
|
||||
|
||||
// 需要等 G 4.0 局部渲染完善后,就不用临时关闭了
|
||||
const canvas = graph.get('canvas');
|
||||
canvas.set('localRefresh', false);
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('edge:mouseenter', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', true);
|
||||
});
|
||||
|
||||
graph.on('edge:mouseleave', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', false);
|
||||
});
|
||||
|
||||
graph.on('edge:click', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'selected', true);
|
||||
});
|
||||
graph.on('canvas:click', (evt) => {
|
||||
graph.getEdges().forEach((edge) => {
|
||||
graph.clearItemStates(edge);
|
||||
});
|
||||
});
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -7,15 +7,15 @@
|
||||
{
|
||||
"filename": "polyline1.js",
|
||||
"title": {
|
||||
"zh": "折线 1",
|
||||
"en": "Polyline 1"
|
||||
"zh": "折线",
|
||||
"en": "Polyline"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*tGX6S5r94wkAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "polyline2.js",
|
||||
"title": {
|
||||
"zh": "折线 2",
|
||||
"zh": "折线(设置控制点)",
|
||||
"en": "Polyline 2"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*CA7oQJIkIl8AAAAAAAAAAABkARQnAQ"
|
||||
@ -23,13 +23,29 @@
|
||||
{
|
||||
"filename": "polyline3.js",
|
||||
"title": {
|
||||
"zh": "折线 3",
|
||||
"zh": "折线(智能寻径)",
|
||||
"en": "Polyline 3"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*E-MJQqf9mSUAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "cubic1.js",
|
||||
"filename": "quadratic.js",
|
||||
"title": {
|
||||
"zh": "二次贝塞尔曲线",
|
||||
"en": "Quadratic"
|
||||
},
|
||||
"screenshot": ""
|
||||
},
|
||||
{
|
||||
"filename": "cubic.js",
|
||||
"title": {
|
||||
"zh": "三次贝塞尔曲线",
|
||||
"en": "Cubic"
|
||||
},
|
||||
"screenshot": ""
|
||||
},
|
||||
{
|
||||
"filename": "verticalCubic.js",
|
||||
"title": {
|
||||
"zh": "三次贝塞尔曲线-垂直",
|
||||
"en": "Vertical Cubic"
|
||||
@ -37,21 +53,13 @@
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*UO_sQp0XftMAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "cubic2.js",
|
||||
"filename": "horizontalCubic.js",
|
||||
"title": {
|
||||
"zh": "三次贝塞尔曲线-水平",
|
||||
"en": "Horizontal Cubic"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*LxFySJ-uvhMAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "arc.js",
|
||||
"title": {
|
||||
"zh": "弧线",
|
||||
"en": "Arc"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*xkK6QoNUszgAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "loop.js",
|
||||
"title": {
|
||||
|
@ -1,87 +1,71 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Graph, Extensions, extend } from '@antv/g6';
|
||||
|
||||
/**
|
||||
* The usage of built-in polyline
|
||||
* by Shiwu
|
||||
*/
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: '0',
|
||||
x: 150,
|
||||
y: 100,
|
||||
},
|
||||
{
|
||||
id: '1',
|
||||
x: 350,
|
||||
y: 300,
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
// Built-in polyline
|
||||
{
|
||||
source: '0',
|
||||
target: '1',
|
||||
},
|
||||
],
|
||||
};
|
||||
const ExtGraph = extend(Graph, {
|
||||
edges: {
|
||||
'polyline-edge': Extensions.PolylineEdge,
|
||||
},
|
||||
});
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
// make the edge link to the centers of the end nodes
|
||||
linkCenter: true,
|
||||
modes: {
|
||||
// behavior
|
||||
default: ['drag-node'],
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'],
|
||||
},
|
||||
defaultEdge: {
|
||||
type: 'polyline',
|
||||
/* you can configure the global edge style as following lines */
|
||||
// style: {
|
||||
// stroke: '#F6BD16',
|
||||
// },
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 150,
|
||||
y: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
data: { x: 300, y: 200 },
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
data: {
|
||||
type: 'polyline-edge',
|
||||
keyShape: {
|
||||
/**
|
||||
* 拐弯处的圆角弧度,默认为直角
|
||||
*/
|
||||
radius: 0,
|
||||
/**
|
||||
* 拐弯处距离节点最小距离, 默认为 5
|
||||
*/
|
||||
offset: 5,
|
||||
/**
|
||||
* 控制点数组,不指定时根据 A* 算法自动生成折线。若指定了,则按照 controlPoints 指定的位置进行弯折
|
||||
*/
|
||||
// controlPoints: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
edge: {
|
||||
keyShape: {
|
||||
endArrow: true,
|
||||
},
|
||||
},
|
||||
/* styles for different states, there are built-in styles for states: active, inactive, selected, highlight, disable */
|
||||
// edgeStateStyles: {
|
||||
// // edge style of active state
|
||||
// active: {
|
||||
// opacity: 0.5,
|
||||
// stroke: '#f00'
|
||||
// },
|
||||
// // edge style of selected state
|
||||
// selected: {
|
||||
// stroke: '#ff0'
|
||||
// lineWidth: 3,
|
||||
// },
|
||||
// },
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('edge:mouseenter', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', true);
|
||||
});
|
||||
|
||||
graph.on('edge:mouseleave', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', false);
|
||||
});
|
||||
|
||||
graph.on('edge:click', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'selected', true);
|
||||
});
|
||||
graph.on('canvas:click', (evt) => {
|
||||
graph.getEdges().forEach((edge) => {
|
||||
graph.clearItemStates(edge);
|
||||
});
|
||||
});
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,88 +1,81 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Graph, Extensions, extend } from '@antv/g6';
|
||||
|
||||
/**
|
||||
* Built-in polyline edge with configurations
|
||||
* by 十吾
|
||||
*/
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: '2',
|
||||
x: 150,
|
||||
y: 150,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
x: 350,
|
||||
y: 250,
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
source: '2',
|
||||
target: '3',
|
||||
},
|
||||
],
|
||||
};
|
||||
const ExtGraph = extend(Graph, {
|
||||
edges: {
|
||||
'polyline-edge': Extensions.PolylineEdge,
|
||||
},
|
||||
});
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
/* translate the graph to align the canvas's center, support by v3.5.1 */
|
||||
fitCenter: true,
|
||||
modes: {
|
||||
/* behavior */
|
||||
default: ['drag-node'],
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'],
|
||||
},
|
||||
defaultEdge: {
|
||||
type: 'polyline',
|
||||
/* configure the bending radius and min distance to the end nodes */
|
||||
style: {
|
||||
radius: 10,
|
||||
offset: 30,
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 140,
|
||||
y: 130,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
data: { x: 400, y: 200 },
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
data: {
|
||||
type: 'polyline-edge',
|
||||
keyShape: {
|
||||
/**
|
||||
* 拐弯处的圆角弧度,默认为直角
|
||||
*/
|
||||
radius: 20,
|
||||
/**
|
||||
* 拐弯处距离节点最小距离, 默认为 5
|
||||
*/
|
||||
// offset: 0,
|
||||
/**
|
||||
* 控制点数组,不指定时根据 A* 算法自动生成折线。若指定了,则按照 controlPoints 指定的位置进行弯折
|
||||
*/
|
||||
controlPoints: [
|
||||
{ x: 220, y: 220 },
|
||||
{ x: 300, y: 130 },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
node: (nodeInnerModel) => {
|
||||
const { id, data } = nodeInnerModel;
|
||||
return {
|
||||
id,
|
||||
data,
|
||||
};
|
||||
},
|
||||
edge: {
|
||||
keyShape: {
|
||||
endArrow: true,
|
||||
/* and other styles */
|
||||
// stroke: '#F6BD16',
|
||||
},
|
||||
},
|
||||
/* styles for different states, there are built-in styles for states: active, inactive, selected, highlight, disable */
|
||||
// edgeStateStyles: {
|
||||
// // edge style of active state
|
||||
// active: {
|
||||
// opacity: 0.5,
|
||||
// stroke: '#f00'
|
||||
// },
|
||||
// // edge style of selected state
|
||||
// selected: {
|
||||
// stroke: '#ff0'
|
||||
// lineWidth: 3,
|
||||
// },
|
||||
// },
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('edge:mouseenter', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', true);
|
||||
});
|
||||
|
||||
graph.on('edge:mouseleave', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', false);
|
||||
});
|
||||
|
||||
graph.on('edge:click', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'selected', true);
|
||||
});
|
||||
graph.on('canvas:click', (evt) => {
|
||||
graph.getEdges().forEach((edge) => {
|
||||
graph.clearItemStates(edge);
|
||||
});
|
||||
});
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,109 +1,119 @@
|
||||
import G6 from '@antv/g6';
|
||||
|
||||
/**
|
||||
* Usage of built-in polyline edge with controlPoints
|
||||
* by 十吾
|
||||
*/
|
||||
import { Graph, Extensions, extend } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: '4',
|
||||
x: 150,
|
||||
y: 100,
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 140,
|
||||
y: 130,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
x: 350,
|
||||
y: 250,
|
||||
id: 'node2',
|
||||
data: {
|
||||
x: 400,
|
||||
y: 200,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'obstacle1',
|
||||
data: {
|
||||
x: 300,
|
||||
y: 90,
|
||||
// preventPolylineEdgeOverlap: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'obstacle2',
|
||||
data: {
|
||||
x: 300,
|
||||
y: 200,
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
source: '4',
|
||||
target: '5',
|
||||
// assign the control points to control the bending positions
|
||||
controlPoints: [
|
||||
{
|
||||
x: 260,
|
||||
y: 80,
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
data: {
|
||||
type: 'polyline-edge',
|
||||
keyShape: {
|
||||
/**
|
||||
* 拐弯处的圆角弧度,默认为直角,值为 0
|
||||
* The radius of the corner rounding, defaults to a right angle
|
||||
*/
|
||||
// radius: 20,
|
||||
/**
|
||||
* 拐弯处距离节点最小距离, 默认为 5
|
||||
* Minimum distance from the node at the corner, default is 5.
|
||||
*/
|
||||
offset: 20,
|
||||
/**
|
||||
* 控制点数组,不指定时根据 A* 算法自动生成折线。若指定了,则按照 controlPoints 指定的位置进行弯折
|
||||
* An array of control points that, when not specified, automatically generates the bends according to the A* algorithm. If specified, bends are made at the position specified by controlPoints.
|
||||
*/
|
||||
// controlPoints: [],
|
||||
routeCfg: {
|
||||
/**
|
||||
* 目前支持正交路由 'orth' 和地铁路由 'er'
|
||||
*/
|
||||
// name: 'er',
|
||||
/**
|
||||
* 是否开启自动避障,默认为 false
|
||||
* Whether to enable automatic obstacle avoidance, default is false
|
||||
*/
|
||||
obstacleAvoidance: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
x: 320,
|
||||
y: 50,
|
||||
},
|
||||
{
|
||||
x: 390,
|
||||
y: 110,
|
||||
},
|
||||
{
|
||||
x: 420,
|
||||
y: 110,
|
||||
},
|
||||
{
|
||||
x: 420,
|
||||
y: 140,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
edges: {
|
||||
'polyline-edge': Extensions.PolylineEdge,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
// make the edge link to the centers of the end nodes
|
||||
linkCenter: true,
|
||||
modes: {
|
||||
// behavior
|
||||
default: ['drag-node'],
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'],
|
||||
},
|
||||
defaultEdge: {
|
||||
type: 'polyline',
|
||||
/* you can configure the global edge style as following lines */
|
||||
// style: {
|
||||
// stroke: '#F6BD16',
|
||||
// },
|
||||
data,
|
||||
node: {
|
||||
type: 'rect-node',
|
||||
keyShape: {
|
||||
width: 60,
|
||||
height: 30,
|
||||
},
|
||||
labelShape: {
|
||||
text: {
|
||||
fields: ['id'],
|
||||
formatter: (model) => model.id,
|
||||
},
|
||||
position: 'bottom',
|
||||
},
|
||||
},
|
||||
edge: {
|
||||
keyShape: {
|
||||
endArrow: true,
|
||||
},
|
||||
},
|
||||
/* styles for different states, there are built-in styles for states: active, inactive, selected, highlight, disable */
|
||||
// edgeStateStyles: {
|
||||
// // edge style of active state
|
||||
// active: {
|
||||
// opacity: 0.5,
|
||||
// stroke: '#f00'
|
||||
// },
|
||||
// // edge style of selected state
|
||||
// selected: {
|
||||
// stroke: '#ff0'
|
||||
// lineWidth: 3,
|
||||
// },
|
||||
// },
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('edge:mouseenter', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', true);
|
||||
});
|
||||
|
||||
graph.on('edge:mouseleave', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', false);
|
||||
});
|
||||
|
||||
graph.on('edge:click', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'selected', true);
|
||||
});
|
||||
graph.on('canvas:click', (evt) => {
|
||||
graph.getEdges().forEach((edge) => {
|
||||
graph.clearItemStates(edge);
|
||||
});
|
||||
});
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
83
packages/site/examples/item/defaultEdges/demo/quadratic.js
Normal file
83
packages/site/examples/item/defaultEdges/demo/quadratic.js
Normal file
@ -0,0 +1,83 @@
|
||||
import { Graph, Extensions, extend } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 150,
|
||||
y: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
data: { x: 250, y: 200 },
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
data: {
|
||||
type: 'quadratic-edge',
|
||||
keyShape: {
|
||||
/**
|
||||
* 控制点数组,默认:曲线中心附近
|
||||
* precise x-axis, y-axis coordinates of control points. Default is center of the curve
|
||||
*/
|
||||
controlPoints: [],
|
||||
/**
|
||||
* 控制点在两端点连线上的相对位置,范围 0 - 1
|
||||
* Relative position of the control point on the line between the two endpoints, range 0 - 1
|
||||
*/
|
||||
curvePosition: 0.5,
|
||||
/**
|
||||
* 控制点距离两端点连线的距离,可理解为控制边的弯曲程度
|
||||
* Distance of the control point from the line connecting the two end points
|
||||
*/
|
||||
curveOffset: 30,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
edges: {
|
||||
'quadratic-edge': Extensions.QuadraticEdge,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
modes: {
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'],
|
||||
},
|
||||
data,
|
||||
node: (nodeInnerModel) => {
|
||||
const { id, data } = nodeInnerModel;
|
||||
return {
|
||||
id,
|
||||
data,
|
||||
};
|
||||
},
|
||||
edge: {
|
||||
keyShape: {
|
||||
endArrow: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
106
packages/site/examples/item/defaultEdges/demo/verticalCubic.js
Normal file
106
packages/site/examples/item/defaultEdges/demo/verticalCubic.js
Normal file
@ -0,0 +1,106 @@
|
||||
import { Graph, Extensions, extend } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 150,
|
||||
y: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
data: { x: 200, y: 200 },
|
||||
},
|
||||
{
|
||||
id: 'node3',
|
||||
data: { x: 100, y: 200 },
|
||||
},
|
||||
{
|
||||
id: 'node4',
|
||||
data: { x: 150, y: 300 },
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
},
|
||||
{
|
||||
id: 'edge2',
|
||||
source: 'node1',
|
||||
target: 'node3',
|
||||
},
|
||||
{
|
||||
id: 'edge3',
|
||||
source: 'node2',
|
||||
target: 'node4',
|
||||
},
|
||||
{
|
||||
id: 'edge4',
|
||||
source: 'node3',
|
||||
target: 'node4',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
edges: {
|
||||
'cubic-horizontal-edge': Extensions.CubicHorizontalEdge,
|
||||
'cubic-vertical-edge': Extensions.CubicVerticalEdge,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
modes: {
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'],
|
||||
},
|
||||
data,
|
||||
node: {
|
||||
anchorPoints: [
|
||||
[0.5, 0],
|
||||
[1, 0],
|
||||
],
|
||||
keyShape: {
|
||||
width: 80,
|
||||
height: 30,
|
||||
},
|
||||
},
|
||||
edge: {
|
||||
type: 'cubic-vertical-edge',
|
||||
keyShape: {
|
||||
endArrow: true,
|
||||
/**
|
||||
* 控制点数组,默认:曲线 1/3 和 2/3 处
|
||||
* precise x-axis, y-axis coordinates of control points. Default: 1/3 and 2/3 of the curve
|
||||
*/
|
||||
// controlPoints: [],
|
||||
/**
|
||||
* 控制点在两端点连线上的相对位置,范围 0 - 1
|
||||
* Relative position of the control point on the line between the two endpoints, range 0 - 1
|
||||
*/
|
||||
// curvePosition: 0.5,
|
||||
/**
|
||||
* 控制点距离两端点连线的距离,可理解为控制边的弯曲程度
|
||||
* Distance of the control point from the line connecting the two end points
|
||||
*/
|
||||
// curveOffset: 30,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
@ -4,103 +4,64 @@ const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'circle',
|
||||
label: 'Circle',
|
||||
x: 250,
|
||||
y: 150,
|
||||
data: {
|
||||
x: 250,
|
||||
y: 150,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node'],
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'],
|
||||
},
|
||||
defaultNode: {
|
||||
/* node type */
|
||||
type: 'circle',
|
||||
/* node size */
|
||||
size: [60],
|
||||
/* style for the keyShape */
|
||||
// style: {
|
||||
// fill: '#9EC9FF',
|
||||
// stroke: '#5B8FF9',
|
||||
// lineWidth: 3,
|
||||
// },
|
||||
labelCfg: {
|
||||
/* label's position, options: center, top, bottom, left, right */
|
||||
data,
|
||||
node: {
|
||||
keyShape: {
|
||||
r: 30,
|
||||
},
|
||||
labelShape: {
|
||||
text: {
|
||||
fields: ['id'],
|
||||
formatter: (model) => model.id,
|
||||
},
|
||||
position: 'bottom',
|
||||
/* label's offset to the keyShape, 4 by default */
|
||||
// offset: 12,
|
||||
/* label's style */
|
||||
// style: {
|
||||
// fontSize: 20,
|
||||
// fill: '#ccc',
|
||||
// fontWeight: 500
|
||||
// }
|
||||
},
|
||||
/* configurations for four linkpoints */
|
||||
linkPoints: {
|
||||
top: true,
|
||||
right: true,
|
||||
bottom: true,
|
||||
left: true,
|
||||
/* linkPoints' size, 8 by default */
|
||||
// size: 5,
|
||||
/* linkPoints' style */
|
||||
// fill: '#ccc',
|
||||
// stroke: '#333',
|
||||
// lineWidth: 2,
|
||||
labelBackgroundShape: {
|
||||
fill: 'red',
|
||||
},
|
||||
/* icon configuration */
|
||||
icon: {
|
||||
/* whether show the icon, false by default */
|
||||
show: true,
|
||||
/* icon's img address, string type */
|
||||
// img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
/* icon's size, 20 * 20 by default: */
|
||||
// width: 40,
|
||||
// height: 40
|
||||
anchorShapes: [
|
||||
{
|
||||
position: [0, 0.5],
|
||||
r: 2,
|
||||
fill: 'red',
|
||||
},
|
||||
],
|
||||
iconShape: {
|
||||
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
width: 20,
|
||||
height: 20,
|
||||
},
|
||||
badgeShapes: [
|
||||
{
|
||||
text: '1',
|
||||
position: 'rightTop',
|
||||
color: 'blue',
|
||||
},
|
||||
],
|
||||
},
|
||||
/* styles for different states, there are built-in styles for states: active, inactive, selected, highlight, disable */
|
||||
// nodeStateStyles: {
|
||||
// // node style of active state
|
||||
// active: {
|
||||
// fillOpacity: 0.8,
|
||||
// },
|
||||
// // node style of selected state
|
||||
// selected: {
|
||||
// lineWidth: 5,
|
||||
// },
|
||||
// },
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('node:mouseenter', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', true);
|
||||
});
|
||||
|
||||
graph.on('node:mouseleave', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', false);
|
||||
});
|
||||
|
||||
graph.on('node:click', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'selected', true);
|
||||
});
|
||||
graph.on('canvas:click', (evt) => {
|
||||
graph.getNodes().forEach((node) => {
|
||||
graph.clearItemStates(node);
|
||||
});
|
||||
});
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,106 +1,75 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Graph, Extensions, extend } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'diamond',
|
||||
label: 'Diamond',
|
||||
x: 250,
|
||||
y: 150,
|
||||
data: {
|
||||
x: 250,
|
||||
y: 150,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
nodes: {
|
||||
'diamond-node': Extensions.DiamondNode,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node'],
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'],
|
||||
},
|
||||
defaultNode: {
|
||||
type: 'diamond',
|
||||
/* node size */
|
||||
size: [80, 60],
|
||||
/* style for the keyShape */
|
||||
// style: {
|
||||
// fill: '#9EC9FF',
|
||||
// stroke: '#5B8FF9',
|
||||
// lineWidth: 3,
|
||||
// },
|
||||
labelCfg: {
|
||||
/* label's position, options: center, top, bottom, left, right */
|
||||
data,
|
||||
node: {
|
||||
type: 'diamond-node',
|
||||
keyShape: {
|
||||
r: 30,
|
||||
},
|
||||
labelShape: {
|
||||
text: {
|
||||
fields: ['id'],
|
||||
formatter: (model) => model.id,
|
||||
},
|
||||
position: 'bottom',
|
||||
/* label's offset to the keyShape, 4 by default */
|
||||
// offset: 12,
|
||||
/* label's style */
|
||||
// style: {
|
||||
// fontSize: 20,
|
||||
// fill: '#ccc',
|
||||
// fontWeight: 500
|
||||
// }
|
||||
},
|
||||
/* configurations for four linkpoints */
|
||||
linkPoints: {
|
||||
top: true,
|
||||
right: true,
|
||||
bottom: true,
|
||||
left: true,
|
||||
/* linkPoints' size, 8 by default */
|
||||
// size: 5,
|
||||
/* linkPoints' style */
|
||||
// fill: '#ccc',
|
||||
// stroke: '#333',
|
||||
// lineWidth: 2,
|
||||
labelBackgroundShape: {
|
||||
fill: 'red',
|
||||
},
|
||||
/* icon configuration */
|
||||
icon: {
|
||||
/* whether show the icon, false by default */
|
||||
show: true,
|
||||
/* icon's img address, string type */
|
||||
// img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
/* icon's size, 20 * 20 by default: */
|
||||
// width: 40,
|
||||
// height: 40
|
||||
anchorShapes: [
|
||||
{
|
||||
position: [0, 0.5],
|
||||
r: 2,
|
||||
fill: 'red',
|
||||
},
|
||||
],
|
||||
iconShape: {
|
||||
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
width: 20,
|
||||
height: 20,
|
||||
},
|
||||
badgeShapes: [
|
||||
{
|
||||
text: '1',
|
||||
position: 'rightTop',
|
||||
color: 'blue',
|
||||
},
|
||||
],
|
||||
},
|
||||
/* styles for different states, there are built-in styles for states: active, inactive, selected, highlight, disable */
|
||||
// nodeStateStyles: {
|
||||
// // node style of active state
|
||||
// active: {
|
||||
// fillOpacity: 0.8,
|
||||
// },
|
||||
// // node style of selected state
|
||||
// selected: {
|
||||
// lineWidth: 5,
|
||||
// },
|
||||
// },
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('node:mouseenter', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', true);
|
||||
});
|
||||
|
||||
graph.on('node:mouseleave', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', false);
|
||||
});
|
||||
|
||||
graph.on('node:click', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'selected', true);
|
||||
});
|
||||
|
||||
graph.on('canvas:click', (evt) => {
|
||||
graph.getNodes().forEach((node) => {
|
||||
graph.clearItemStates(node);
|
||||
});
|
||||
});
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,118 +1,111 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Graph, Extensions, extend } from '@antv/g6';
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'donut',
|
||||
label: 'Donut',
|
||||
x: 250,
|
||||
y: 150,
|
||||
// the attributes for drawing donut
|
||||
donutAttrs: {
|
||||
prop1: 10,
|
||||
prop2: 20,
|
||||
prop3: 25
|
||||
},
|
||||
// the color map for drawing donut
|
||||
donutColorMap: {
|
||||
prop1: '#8eaade',
|
||||
prop2: '#5c7cb8',
|
||||
prop3: '#1e3f7d'
|
||||
data: {
|
||||
x: 250,
|
||||
y: 150,
|
||||
/**
|
||||
* 甜甜圈内径在 keyShape 半径的占比,取值为 0-1,默认 0.6
|
||||
* The ratio of the inner diameter of the donut to the radius of the keyShape, the value is 0-1
|
||||
*/
|
||||
innerSize: 0.6,
|
||||
/**
|
||||
* 甜甜圈字段,每个字段必须为 [key: string]: number
|
||||
* Donut fields, each field must be [key: string]: number
|
||||
*/
|
||||
donutAttr: {
|
||||
income: 80,
|
||||
outcome: 40,
|
||||
unknown: 45,
|
||||
},
|
||||
/**
|
||||
* 甜甜圈颜色映射,字段名与 attrs 中的字段名对应。不指定则使用默认色板
|
||||
* A donut colormap with field names corresponding to those in attrs. If not specified, the default swatch will be used
|
||||
*/
|
||||
donutColorMap: {
|
||||
income: '#78D3F8',
|
||||
outcome: '#F08BB4',
|
||||
unknown: '#65789B',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
const ExtGraph = extend(Graph, {
|
||||
nodes: {
|
||||
'donut-node': Extensions.DonutNode,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node'],
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'],
|
||||
},
|
||||
defaultNode: {
|
||||
/* node type, the priority is lower than the type in the node data */
|
||||
type: 'donut',
|
||||
/* node size */
|
||||
size: 60,
|
||||
/* style for the keyShape */
|
||||
// style: {
|
||||
// fill: '#9EC9FF',
|
||||
// stroke: '#5B8FF9',
|
||||
// lineWidth: 3,
|
||||
// },
|
||||
labelCfg: {
|
||||
/* label's position, options: center, top, bottom, left, right */
|
||||
data,
|
||||
node: {
|
||||
type: 'donut-node',
|
||||
keyShape: {
|
||||
r: 30,
|
||||
},
|
||||
labelShape: {
|
||||
text: {
|
||||
fields: ['id'],
|
||||
formatter: (model) => model.id,
|
||||
},
|
||||
position: 'bottom',
|
||||
/* label's offset to the keyShape, 4 by default */
|
||||
// offset: 12,
|
||||
/* label's style */
|
||||
// style: {
|
||||
// fontSize: 20,
|
||||
// fill: '#ccc',
|
||||
// fontWeight: 500
|
||||
// }
|
||||
},
|
||||
/* configurations for four linkpoints */
|
||||
// linkPoints: {
|
||||
// top: true,
|
||||
// right: true,
|
||||
// bottom: true,
|
||||
// left: true,
|
||||
// /* linkPoints' size, 8 by default */
|
||||
// // size: 5,
|
||||
// /* linkPoints' style */
|
||||
// // fill: '#ccc',
|
||||
// // stroke: '#333',
|
||||
// // lineWidth: 2,
|
||||
// },
|
||||
/* icon configuration */
|
||||
icon: {
|
||||
/* whether show the icon, false by default */
|
||||
show: true,
|
||||
/* icon's img address, string type */
|
||||
// img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
/* icon's size, 20 * 20 by default: */
|
||||
// width: 40,
|
||||
// height: 40
|
||||
labelBackgroundShape: {
|
||||
fill: 'red',
|
||||
},
|
||||
anchorShapes: [
|
||||
{
|
||||
position: [0, 0.5],
|
||||
r: 2,
|
||||
fill: 'red',
|
||||
},
|
||||
],
|
||||
iconShape: {
|
||||
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
width: 20,
|
||||
height: 20,
|
||||
},
|
||||
badgeShapes: [
|
||||
{
|
||||
text: '1',
|
||||
position: 'rightTop',
|
||||
color: 'blue',
|
||||
},
|
||||
],
|
||||
/** Shapes of donut */
|
||||
donutShapes: {
|
||||
innerSize: { fields: ['innerSize'], formatter: (model) => model.data.innerSize },
|
||||
attrs: {
|
||||
fields: ['donutAttr'],
|
||||
formatter: (model) => model.data.donutAttr,
|
||||
},
|
||||
|
||||
colorMap: {
|
||||
fields: ['donutColorMap'],
|
||||
formatter: (model) => model.data.donutColorMap,
|
||||
},
|
||||
},
|
||||
},
|
||||
/* styles for different states, there are built-in styles for states: active, inactive, selected, highlight, disable */
|
||||
// nodeStateStyles: {
|
||||
// // node style of active state
|
||||
// active: {
|
||||
// fillOpacity: 0.8,
|
||||
// },
|
||||
// // node style of selected state
|
||||
// selected: {
|
||||
// lineWidth: 5,
|
||||
// },
|
||||
// },
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('node:mouseenter', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', true);
|
||||
});
|
||||
|
||||
graph.on('node:mouseleave', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', false);
|
||||
});
|
||||
|
||||
graph.on('node:click', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'selected', true);
|
||||
});
|
||||
graph.on('canvas:click', (evt) => {
|
||||
graph.getNodes().forEach((node) => {
|
||||
graph.clearItemStates(node);
|
||||
});
|
||||
});
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,105 +1,84 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Graph, Extensions, extend } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'ellipse',
|
||||
label: 'Ellipse',
|
||||
x: 250,
|
||||
y: 150,
|
||||
data: {
|
||||
x: 250,
|
||||
y: 150,
|
||||
type: 'ellipse-node',
|
||||
keyShape: {
|
||||
/**
|
||||
* 水平半径,默认 30
|
||||
* the horizontal radius of the ellipse, default is 30
|
||||
*/
|
||||
// rx: 30,
|
||||
/**
|
||||
* 垂直半径,数字可选,默认 20
|
||||
* the vertical radius of the ellipse, default is 20
|
||||
*/
|
||||
// ry: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
nodes: {
|
||||
'ellipse-node': Extensions.EllipseNode,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node'],
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'],
|
||||
},
|
||||
defaultNode: {
|
||||
type: 'ellipse',
|
||||
size: [80, 50],
|
||||
/* style for the keyShape */
|
||||
// style: {
|
||||
// fill: '#9EC9FF',
|
||||
// stroke: '#5B8FF9',
|
||||
// lineWidth: 3,
|
||||
// },
|
||||
labelCfg: {
|
||||
/* label's position, options: center, top, bottom, left, right */
|
||||
data,
|
||||
node: {
|
||||
labelShape: {
|
||||
text: {
|
||||
fields: ['id'],
|
||||
formatter: (model) => model.id,
|
||||
},
|
||||
position: 'bottom',
|
||||
/* label's offset to the keyShape, 4 by default */
|
||||
// offset: 12,
|
||||
/* label's style */
|
||||
// style: {
|
||||
// fontSize: 20,
|
||||
// fill: '#ccc',
|
||||
// fontWeight: 500
|
||||
// }
|
||||
},
|
||||
/* configurations for four linkpoints */
|
||||
linkPoints: {
|
||||
top: true,
|
||||
right: true,
|
||||
bottom: true,
|
||||
left: true,
|
||||
/* linkPoints' size, 8 by default */
|
||||
// size: 5,
|
||||
/* linkPoints' style */
|
||||
// fill: '#ccc',
|
||||
// stroke: '#333',
|
||||
// lineWidth: 2,
|
||||
labelBackgroundShape: {
|
||||
fill: 'red',
|
||||
},
|
||||
/* icon configuration */
|
||||
icon: {
|
||||
/* whether show the icon, false by default */
|
||||
show: true,
|
||||
/* icon's img address, string type */
|
||||
// img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
/* icon's size, 20 * 20 by default: */
|
||||
// width: 40,
|
||||
// height: 40
|
||||
anchorShapes: [
|
||||
{
|
||||
position: [0, 0.5],
|
||||
r: 2,
|
||||
fill: 'red',
|
||||
},
|
||||
],
|
||||
iconShape: {
|
||||
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
width: 20,
|
||||
height: 20,
|
||||
},
|
||||
badgeShapes: [
|
||||
{
|
||||
text: '1',
|
||||
position: 'rightTop',
|
||||
color: 'blue',
|
||||
},
|
||||
],
|
||||
},
|
||||
/* styles for different states, there are built-in styles for states: active, inactive, selected, highlight, disable */
|
||||
// nodeStateStyles: {
|
||||
// // node style of active state
|
||||
// active: {
|
||||
// fillOpacity: 0.8,
|
||||
// },
|
||||
// // node style of selected state
|
||||
// selected: {
|
||||
// lineWidth: 5,
|
||||
// },
|
||||
// },
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('node:mouseenter', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', true);
|
||||
});
|
||||
|
||||
graph.on('node:mouseleave', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', false);
|
||||
});
|
||||
|
||||
graph.on('node:click', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'selected', true);
|
||||
});
|
||||
|
||||
graph.on('canvas:click', (evt) => {
|
||||
graph.getNodes().forEach((node) => {
|
||||
graph.clearItemStates(node);
|
||||
});
|
||||
});
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
85
packages/site/examples/item/defaultNodes/demo/hexagon.js
Normal file
85
packages/site/examples/item/defaultNodes/demo/hexagon.js
Normal file
@ -0,0 +1,85 @@
|
||||
import { Graph, Extensions, extend } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'hexagon',
|
||||
data: {
|
||||
x: 250,
|
||||
y: 150,
|
||||
type: 'hexagon-node',
|
||||
keyShape: {
|
||||
/**
|
||||
* 六边形的半径,默认为 20
|
||||
* the radius of the hexagon, default is 20
|
||||
*/
|
||||
r: 20,
|
||||
/**
|
||||
* 六边形方向,默认为 horizontal
|
||||
* the direction of the hexagon, default is 'horizontal'
|
||||
* supported value: 'horizontal' | 'vertical'
|
||||
*/
|
||||
direction: 'horizontal',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
nodes: {
|
||||
'hexagon-node': Extensions.HexagonNode,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
modes: {
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'],
|
||||
},
|
||||
data,
|
||||
node: {
|
||||
labelShape: {
|
||||
text: {
|
||||
fields: ['id'],
|
||||
formatter: (model) => model.id,
|
||||
},
|
||||
position: 'bottom',
|
||||
},
|
||||
labelBackgroundShape: {
|
||||
fill: 'red',
|
||||
},
|
||||
anchorShapes: [
|
||||
{
|
||||
position: [0, 0.5],
|
||||
r: 2,
|
||||
fill: 'red',
|
||||
},
|
||||
],
|
||||
iconShape: {
|
||||
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
width: 20,
|
||||
height: 20,
|
||||
},
|
||||
badgeShapes: [
|
||||
{
|
||||
text: '1',
|
||||
position: 'rightTop',
|
||||
color: 'blue',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
@ -1,43 +1 @@
|
||||
import G6 from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'image',
|
||||
img: 'https://gw.alipayobjects.com/os/s/prod/antv/assets/image/logo-with-text-73b8a.svg',
|
||||
x: 250,
|
||||
y: 150,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
defaultNode: {
|
||||
type: 'image',
|
||||
size: [260, 80],
|
||||
clipCfg: {
|
||||
show: false,
|
||||
// Clip type options: circle, ellipse, rect, path
|
||||
type: 'circle',
|
||||
// circle
|
||||
r: 30,
|
||||
// clip style
|
||||
style: {
|
||||
lineWidth: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node'],
|
||||
},
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
// TODO
|
||||
|
@ -69,12 +69,12 @@
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*i2tETYnJ0dIAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "image.js",
|
||||
"filename": "hexagon.js",
|
||||
"title": {
|
||||
"zh": "图片",
|
||||
"en": "Image"
|
||||
"zh": "六边形",
|
||||
"en": "Hexagon"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*o-MuTpQaj7EAAAAAAAAAAABkARQnAQ"
|
||||
"screenshot": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,112 +1,95 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Graph, Extensions, extend } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'rect1',
|
||||
label: 'rect1',
|
||||
description: 'description, hidden when undefined',
|
||||
x: 250,
|
||||
y: 150,
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 250,
|
||||
y: 150,
|
||||
type: 'modelRect-node',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
nodes: {
|
||||
'modelRect-node': Extensions.ModelRectNode,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
defaultNode: {
|
||||
type: 'modelRect',
|
||||
size: [270, 80],
|
||||
style: {
|
||||
radius: 5,
|
||||
stroke: '#69c0ff',
|
||||
fill: '#ffffff',
|
||||
lineWidth: 1,
|
||||
fillOpacity: 1,
|
||||
},
|
||||
// label configurations
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: '#595959',
|
||||
fontSize: 14,
|
||||
},
|
||||
offset: 30,
|
||||
},
|
||||
// left rect
|
||||
preRect: {
|
||||
show: true,
|
||||
width: 4,
|
||||
fill: '#40a9ff',
|
||||
radius: 2,
|
||||
},
|
||||
// configurations for the four linkpoints
|
||||
linkPoints: {
|
||||
top: false,
|
||||
right: false,
|
||||
bottom: false,
|
||||
left: false,
|
||||
// the size of the linkpoints' circle
|
||||
size: 10,
|
||||
lineWidth: 1,
|
||||
fill: '#72CC4A',
|
||||
stroke: '#72CC4A',
|
||||
},
|
||||
// configurations for the icon
|
||||
logoIcon: {
|
||||
// whether to show the icon
|
||||
show: true,
|
||||
x: 0,
|
||||
y: 0,
|
||||
// the image url for the icon, string type
|
||||
img:
|
||||
'https://gw.alipayobjects.com/zos/basement_prod/4f81893c-1806-4de4-aff3-9a6b266bc8a2.svg',
|
||||
width: 16,
|
||||
height: 16,
|
||||
// adjust the offset along x-axis for the icon
|
||||
offset: 0,
|
||||
},
|
||||
// configurations for state icon
|
||||
stateIcon: {
|
||||
// whether to show the icon
|
||||
show: true,
|
||||
x: 0,
|
||||
y: 0,
|
||||
// the image url for the icon, string type
|
||||
img:
|
||||
'https://gw.alipayobjects.com/zos/basement_prod/300a2523-67e0-4cbf-9d4a-67c077b40395.svg',
|
||||
width: 16,
|
||||
height: 16,
|
||||
// adjust hte offset along x-axis for the icon
|
||||
offset: -5,
|
||||
},
|
||||
},
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node'],
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'],
|
||||
},
|
||||
nodeStateStyles: {
|
||||
hover: {
|
||||
lineWidth: 2,
|
||||
stroke: '#1890ff',
|
||||
fill: '#e6f7ff',
|
||||
},
|
||||
data,
|
||||
node: (innerModel) => {
|
||||
return {
|
||||
...innerModel,
|
||||
data: {
|
||||
...innerModel.data,
|
||||
labelShape: {
|
||||
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed vestibulum dapibus lectus, sit amet volutpat ante commodo et. Nulla auctor metus id mauris iaculis tempus. Nunc tincidunt libero et justo laoreet, id tristique purus efficitur. Integer aliquet risus nec ullamcorper bibendum. Proin luctus enim a odio aliquam, id condimentum lorem fermentum. Vivamus fermentum lacus vitae metus placerat, eu rutrum est lacinia. Aenean ullamcorper rhoncus ligula, a facilisis metus eleifend nec. Sed scelerisque, ipsum eget consectetur fermentum, sapien orci bibendum ligula, eget aliquam dolor augue vitae mauris. Cras at justo at lectus tempor fringilla. In ac dolor et elit semper sollicitudin. Duis ac volutpat nunc, sed fringilla leo. Nunc tempus, justo eu consequat lobortis, sapien eros varius nulla, vitae pretium libero magna nec ligula. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam id nisi vel nibh eleifend tempor. Fusce eget eleifend augue.',
|
||||
// offsetX: 0,
|
||||
// offsetY: 0,
|
||||
position: 'bottom',
|
||||
maxWidth: '0',
|
||||
},
|
||||
anchorShapes: [
|
||||
{
|
||||
position: 'left',
|
||||
r: 2,
|
||||
fill: 'red',
|
||||
},
|
||||
],
|
||||
otherShapes: {
|
||||
// left rect
|
||||
preRect: {
|
||||
fill: '#5CAAF8',
|
||||
},
|
||||
// text description
|
||||
description: {
|
||||
show: true,
|
||||
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed vestibulum dapibus lectus, sit amet volutpat ante commodo et. Nulla auctor metus id mauris iaculis tempus. Nunc tincidunt libero et justo laoreet, id tristique purus efficitur. Integer aliquet risus nec ullamcorper bibendum. Proin luctus enim a odio aliquam, id condimentum lorem fermentum. Vivamus fermentum lacus vitae metus placerat, eu rutrum est lacinia. Aenean ullamcorper rhoncus ligula, a facilisis metus eleifend nec. Sed scelerisque, ipsum eget consectetur fermentum, sapien orci bibendum ligula, eget aliquam dolor augue vitae mauris. Cras at justo at lectus tempor fringilla. In ac dolor et elit semper sollicitudin. Duis ac volutpat nunc, sed fringilla leo. Nunc tempus, justo eu consequat lobortis, sapien eros varius nulla, vitae pretium libero magna nec ligula. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam id nisi vel nibh eleifend tempor. Fusce eget eleifend augue.',
|
||||
fontSize: 12,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
},
|
||||
// left icon describing the information
|
||||
logoIcon: {
|
||||
img: 'https://gw.alipayobjects.com/zos/basement_prod/4f81893c-1806-4de4-aff3-9a6b266bc8a2.svg',
|
||||
// text: 'logo',
|
||||
width: 16,
|
||||
height: 16,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
},
|
||||
// right icon describing the state
|
||||
stateIcon: {
|
||||
img: 'https://gw.alipayobjects.com/zos/basement_prod/300a2523-67e0-4cbf-9d4a-67c077b40395.svg',
|
||||
// text: 'state',
|
||||
width: 16,
|
||||
height: 16,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('node:mouseenter', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'hover', true);
|
||||
});
|
||||
|
||||
graph.on('node:mouseleave', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'hover', false);
|
||||
});
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -3,106 +3,72 @@ import G6 from '@antv/g6';
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'rect',
|
||||
label: 'rect',
|
||||
x: 250,
|
||||
y: 150,
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 250,
|
||||
y: 150,
|
||||
type: 'rect-node',
|
||||
keyShape: {
|
||||
/**
|
||||
* 矩形的宽度,默认为32
|
||||
* the width of the rectangle, default is 32
|
||||
*/
|
||||
// width: 32,
|
||||
/**
|
||||
* 矩形的高度,默认为32
|
||||
* the height of the rectangle, default is 32
|
||||
*/
|
||||
// height: 32,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node'],
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'],
|
||||
},
|
||||
defaultNode: {
|
||||
/* node type */
|
||||
type: 'rect',
|
||||
/* node size */
|
||||
size: [80, 40],
|
||||
/* style for the keyShape */
|
||||
// style: {
|
||||
// fill: '#9EC9FF',
|
||||
// stroke: '#5B8FF9',
|
||||
// lineWidth: 3,
|
||||
// radius: 10
|
||||
// },
|
||||
labelCfg: {
|
||||
/* label's position, options: center, top, bottom, left, right */
|
||||
position: 'center',
|
||||
/* label's offset to the keyShape, 4 by default */
|
||||
// offset: 12,
|
||||
/* label's style */
|
||||
// style: {
|
||||
// fontSize: 20,
|
||||
// fill: '#ccc',
|
||||
// fontWeight: 500
|
||||
// }
|
||||
data,
|
||||
node: {
|
||||
labelShape: {
|
||||
text: 'label',
|
||||
position: 'bottom',
|
||||
},
|
||||
/* configurations for four linkpoints */
|
||||
linkPoints: {
|
||||
top: true,
|
||||
right: true,
|
||||
bottom: true,
|
||||
left: true,
|
||||
/* linkPoints' size, 8 by default */
|
||||
// size: 5,
|
||||
/* linkPoints' style */
|
||||
// fill: '#ccc',
|
||||
// stroke: '#333',
|
||||
// lineWidth: 2,
|
||||
labelBackgroundShape: {
|
||||
fill: 'red',
|
||||
},
|
||||
/* icon configuration */
|
||||
icon: {
|
||||
/* whether show the icon, false by default */
|
||||
show: true,
|
||||
/* icon's img address, string type */
|
||||
// img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
/* icon's size, 20 * 20 by default: */
|
||||
// width: 40,
|
||||
// height: 40
|
||||
anchorShapes: [
|
||||
{
|
||||
position: [0, 0.5],
|
||||
r: 2,
|
||||
fill: 'red',
|
||||
},
|
||||
],
|
||||
iconShape: {
|
||||
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
width: 20,
|
||||
height: 20,
|
||||
},
|
||||
badgeShapes: [
|
||||
{
|
||||
text: '1',
|
||||
position: 'rightTop',
|
||||
color: 'blue',
|
||||
},
|
||||
],
|
||||
},
|
||||
/* styles for different states, there are built-in styles for states: active, inactive, selected, highlight, disable */
|
||||
// nodeStateStyles: {
|
||||
// // node style of active state
|
||||
// active: {
|
||||
// fillOpacity: 0.8,
|
||||
// },
|
||||
// // node style of selected state
|
||||
// selected: {
|
||||
// lineWidth: 5,
|
||||
// },
|
||||
// },
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('node:mouseenter', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', true);
|
||||
});
|
||||
|
||||
graph.on('node:mouseleave', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', false);
|
||||
});
|
||||
|
||||
graph.on('node:click', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'selected', true);
|
||||
});
|
||||
|
||||
graph.on('canvas:click', (evt) => {
|
||||
graph.getNodes().forEach((node) => {
|
||||
graph.clearItemStates(node);
|
||||
});
|
||||
});
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
1
packages/site/examples/item/defaultNodes/demo/sphere.js
Normal file
1
packages/site/examples/item/defaultNodes/demo/sphere.js
Normal file
@ -0,0 +1 @@
|
||||
// TODO
|
@ -1,106 +1,81 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Graph, Extensions, extend } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'star',
|
||||
label: 'Star',
|
||||
x: 250,
|
||||
y: 200,
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 250,
|
||||
y: 150,
|
||||
type: 'star-node',
|
||||
keyShape: {
|
||||
/**
|
||||
* 星形的外部半径,默认为 20
|
||||
* outer radius of the star,默认为 20
|
||||
*/
|
||||
// outerR: 20,
|
||||
/**
|
||||
* 星形的内部半径,默认为 7.5
|
||||
* inner radius of the star,默认为 7.5
|
||||
*/
|
||||
// innerR: 7.5
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
nodes: {
|
||||
'star-node': Extensions.StarNode,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node'],
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'],
|
||||
},
|
||||
defaultNode: {
|
||||
/* node type */
|
||||
type: 'star',
|
||||
/* node size */
|
||||
size: [60, 30],
|
||||
/* style for the keyShape */
|
||||
// style: {
|
||||
// fill: '#9EC9FF',
|
||||
// stroke: '#5B8FF9',
|
||||
// lineWidth: 3,
|
||||
// },
|
||||
labelCfg: {
|
||||
/* label's position, options: center, top, bottom, left, right */
|
||||
data,
|
||||
node: {
|
||||
labelShape: {
|
||||
text: 'label',
|
||||
position: 'bottom',
|
||||
/* label's offset to the keyShape, 4 by default */
|
||||
offset: 20,
|
||||
/* label's style */
|
||||
// style: {
|
||||
// fontSize: 20,
|
||||
// fill: '#ccc',
|
||||
// fontWeight: 500
|
||||
// }
|
||||
},
|
||||
/* configurations for four linkpoints */
|
||||
linkPoints: {
|
||||
top: true,
|
||||
right: true,
|
||||
bottom: true,
|
||||
left: true,
|
||||
/* linkPoints' size, 8 by default */
|
||||
// size: 5,
|
||||
/* linkPoints' style */
|
||||
// fill: '#ccc',
|
||||
// stroke: '#333',
|
||||
// lineWidth: 2,
|
||||
labelBackgroundShape: {
|
||||
fill: 'red',
|
||||
},
|
||||
/* icon configuration */
|
||||
icon: {
|
||||
/* whether show the icon, false by default */
|
||||
show: true,
|
||||
/* icon's img address, string type */
|
||||
// img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
/* icon's size, 20 * 20 by default: */
|
||||
// width: 40,
|
||||
// height: 40
|
||||
anchorShapes: [
|
||||
{
|
||||
position: [0, 0.5],
|
||||
r: 2,
|
||||
fill: 'red',
|
||||
},
|
||||
],
|
||||
iconShape: {
|
||||
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
width: 20,
|
||||
height: 20,
|
||||
},
|
||||
/* styles for different states, there are built-in styles for states: active, inactive, selected, highlight, disable */
|
||||
// nodeStateStyles: {
|
||||
// // node style of active state
|
||||
// active: {
|
||||
// fillOpacity: 0.8,
|
||||
// },
|
||||
// // node style of selected state
|
||||
// selected: {
|
||||
// lineWidth: 5,
|
||||
// },
|
||||
// },
|
||||
badgeShapes: [
|
||||
{
|
||||
text: '1',
|
||||
position: 'rightTop',
|
||||
color: 'blue',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('node:mouseenter', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', true);
|
||||
});
|
||||
|
||||
graph.on('node:mouseleave', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', false);
|
||||
});
|
||||
|
||||
graph.on('node:click', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'selected', true);
|
||||
});
|
||||
graph.on('canvas:click', (evt) => {
|
||||
graph.getNodes().forEach((node) => {
|
||||
graph.clearItemStates(node);
|
||||
});
|
||||
});
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,106 +1,81 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Graph, Extensions, extend } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'circle',
|
||||
label: 'Triangle',
|
||||
x: 250,
|
||||
y: 200,
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 250,
|
||||
y: 150,
|
||||
type: 'triangle-node',
|
||||
keyShape: {
|
||||
/**
|
||||
* 三角形的半径,默认为12
|
||||
* the radius of the triangle, default is 12
|
||||
*/
|
||||
r: 12,
|
||||
/**
|
||||
* 三角形的方向,可选值 'up'|'left'|'right'|'down'
|
||||
* the direction of the triangle, 'up' indicates upward, 'left' indicates leftward, 'right' indicates rightward, 'down' indicates downward
|
||||
*/
|
||||
direction: 'up',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const ExtGraph = extend(Graph, {
|
||||
nodes: {
|
||||
'triangle-node': Extensions.TriangleNode,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node'],
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'],
|
||||
},
|
||||
defaultNode: {
|
||||
type: 'triangle',
|
||||
size: [40],
|
||||
// options: down, up
|
||||
direction: 'up',
|
||||
/* style for the keyShape */
|
||||
// style: {
|
||||
// fill: '#9EC9FF',
|
||||
// stroke: '#5B8FF9',
|
||||
// lineWidth: 3,
|
||||
// },
|
||||
labelCfg: {
|
||||
/* label's position, options: center, top, bottom, left, right */
|
||||
data,
|
||||
node: {
|
||||
labelShape: {
|
||||
text: 'label',
|
||||
position: 'bottom',
|
||||
/* label's offset to the keyShape, 4 by default */
|
||||
offset: 20,
|
||||
/* label's style */
|
||||
// style: {
|
||||
// fontSize: 20,
|
||||
// fill: '#ccc',
|
||||
// fontWeight: 500
|
||||
// }
|
||||
},
|
||||
/* configurations for four linkpoints */
|
||||
linkPoints: {
|
||||
top: true,
|
||||
right: true,
|
||||
bottom: true,
|
||||
left: true,
|
||||
/* linkPoints' size, 8 by default */
|
||||
// size: 5,
|
||||
/* linkPoints' style */
|
||||
// fill: '#ccc',
|
||||
// stroke: '#333',
|
||||
// lineWidth: 2,
|
||||
labelBackgroundShape: {
|
||||
fill: 'red',
|
||||
},
|
||||
/* icon configuration */
|
||||
icon: {
|
||||
/* whether show the icon, false by default */
|
||||
show: true,
|
||||
/* icon's img address, string type */
|
||||
// img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
/* icon's size, 20 * 20 by default: */
|
||||
// width: 40,
|
||||
// height: 40
|
||||
anchorShapes: [
|
||||
{
|
||||
position: [0, 0.5],
|
||||
r: 2,
|
||||
fill: 'red',
|
||||
},
|
||||
],
|
||||
iconShape: {
|
||||
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
width: 20,
|
||||
height: 20,
|
||||
},
|
||||
/* styles for different states, there are built-in styles for states: active, inactive, selected, highlight, disable */
|
||||
// nodeStateStyles: {
|
||||
// // node style of active state
|
||||
// active: {
|
||||
// fillOpacity: 0.8,
|
||||
// },
|
||||
// // node style of selected state
|
||||
// selected: {
|
||||
// lineWidth: 5,
|
||||
// },
|
||||
// },
|
||||
badgeShapes: [
|
||||
{
|
||||
text: '1',
|
||||
position: 'rightTop',
|
||||
color: 'blue',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('node:mouseenter', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', true);
|
||||
});
|
||||
|
||||
graph.on('node:mouseleave', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'active', false);
|
||||
});
|
||||
|
||||
graph.on('node:click', (evt) => {
|
||||
const { item } = evt;
|
||||
graph.setItemState(item, 'selected', true);
|
||||
});
|
||||
graph.on('canvas:click', (evt) => {
|
||||
graph.getNodes().forEach((node) => {
|
||||
graph.clearItemStates(node);
|
||||
});
|
||||
});
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,290 +1,243 @@
|
||||
import G6 from "@antv/g6";
|
||||
import { Graph, Extensions, extend, stdLib } from '@antv/g6';
|
||||
|
||||
/**
|
||||
* Process the long label, hover to show the complete label, click the icon to copy the label
|
||||
* provided by GitHub user @WontonCat
|
||||
* Thanks for contributing!
|
||||
*/
|
||||
const container = document.getElementById('container');
|
||||
|
||||
const tipDiv = document.createElement('div');
|
||||
tipDiv.innerHTML = `Hover to show the complete label, click the icon to copy the content. Hover 显示完整 label,点击左侧 icon 复制 label 内容`;
|
||||
document.getElementById('container').appendChild(tipDiv);
|
||||
const tipDiv = document.createElement('div');
|
||||
tipDiv.innerHTML = `Click to show the complete label. <br/> Click the icon to copy the content.`;
|
||||
container.appendChild(tipDiv);
|
||||
|
||||
|
||||
/**
|
||||
* format the string
|
||||
* @param {string} str The origin string
|
||||
* @param {number} maxWidth max width
|
||||
* @param {number} fontSize font size
|
||||
* @return {string} the processed result
|
||||
*/
|
||||
const fittingString = (str, maxWidth, fontSize) => {
|
||||
const ellipsis = "...";
|
||||
const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0];
|
||||
let currentWidth = 0;
|
||||
let res = str;
|
||||
const pattern = new RegExp("[\u4E00-\u9FA5]+"); // distinguish the Chinese charactors and letters
|
||||
str.split("").forEach((letter, i) => {
|
||||
if (currentWidth > maxWidth - ellipsisLength) return;
|
||||
if (pattern.test(letter)) {
|
||||
// Chinese charactors
|
||||
currentWidth += fontSize;
|
||||
} else {
|
||||
// get the width of single letter according to the fontSize
|
||||
currentWidth += G6.Util.getLetterWidth(letter, fontSize);
|
||||
}
|
||||
if (currentWidth > maxWidth - ellipsisLength) {
|
||||
res = `${str.substr(0, i)}${ellipsis}`;
|
||||
}
|
||||
});
|
||||
return res;
|
||||
};
|
||||
|
||||
const copyStr = (str) => {
|
||||
const input = document.createElement("textarea");
|
||||
input.value = str;
|
||||
document.body.appendChild(input);
|
||||
input.select();
|
||||
document.execCommand("Copy");
|
||||
document.body.removeChild(input);
|
||||
alert("Copy Success!");
|
||||
};
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
x: 100,
|
||||
y: 0,
|
||||
id: "node1",
|
||||
topText: "This label is too long to be displayed",
|
||||
bottomText: "This label is too long to be displayed"
|
||||
},
|
||||
{
|
||||
x: 100,
|
||||
y: 100,
|
||||
id: "node2",
|
||||
topText: "Short Label",
|
||||
bottomText: "Click the Logo to Copy!"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const width = document.getElementById("container").scrollWidth;
|
||||
const height = document.getElementById("container").scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
container: "container",
|
||||
width,
|
||||
height: height - 50,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
defaultNode: {
|
||||
type: "copy-node"
|
||||
},
|
||||
defaultEdge: {
|
||||
color: "#F6BD16"
|
||||
}
|
||||
});
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const nodeHeight = 80;
|
||||
const nodeWidth = 200;
|
||||
const fillColor = "#f6e9d7";
|
||||
const fontColor = "#ff7900";
|
||||
const fillColor = '#f6e9d7';
|
||||
const fontColor = '#ff7900';
|
||||
const padding = 7;
|
||||
|
||||
G6.registerNode(
|
||||
"copy-node",
|
||||
{
|
||||
drawShape: function drawShape(cfg, group) {
|
||||
const rect = group.addShape("rect", {
|
||||
attrs: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
height: nodeHeight,
|
||||
width: nodeWidth,
|
||||
fill: fillColor,
|
||||
stroke: fontColor,
|
||||
lineWidth: 2,
|
||||
radius: 5
|
||||
}
|
||||
});
|
||||
class CopyNode extends Extensions.RectNode {
|
||||
drawOtherShapes(model, shapeMap, diffData, diffState) {
|
||||
const { data: cfg } = model;
|
||||
|
||||
// 上部文字区域
|
||||
const topGroup = group.addGroup({
|
||||
// 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: "top-group"
|
||||
});
|
||||
|
||||
topGroup.addShape("rect", {
|
||||
attrs: {
|
||||
fill: "#fff",
|
||||
stroke: "#c7d0d1",
|
||||
const topGroup = {
|
||||
topBox: this.upsertShape(
|
||||
'rect',
|
||||
'topBox',
|
||||
{
|
||||
fill: '#fff',
|
||||
stroke: '#c7d0d1',
|
||||
x: padding,
|
||||
y: padding,
|
||||
width: nodeWidth - padding * 2,
|
||||
height: 0.5 * nodeHeight - padding,
|
||||
lineWidth: 1.5,
|
||||
radius: 4
|
||||
}
|
||||
});
|
||||
|
||||
topGroup.addShape("text", {
|
||||
attrs: {
|
||||
text: fittingString(cfg.topText, nodeWidth - padding * 2 - 10, 14),
|
||||
x: 0.5 * nodeWidth,
|
||||
radius: 4,
|
||||
},
|
||||
shapeMap,
|
||||
model,
|
||||
),
|
||||
topText: this.upsertShape(
|
||||
'text',
|
||||
'topText',
|
||||
{
|
||||
// text: fittingString(cfg.topText, nodeWidth - padding * 2 - 10, 14),
|
||||
text: cfg.topText,
|
||||
x: cfg.isTopTextEllipsis ? padding : padding + 24,
|
||||
y: (0.5 * nodeHeight + padding) * 0.5,
|
||||
fontSize: 14,
|
||||
textAlign: "center",
|
||||
textBaseline: "middle",
|
||||
textAlign: 'start',
|
||||
textBaseline: 'middle',
|
||||
shadowColor: fontColor,
|
||||
fill: fontColor
|
||||
fill: fontColor,
|
||||
wordWrap: cfg.isTopTextEllipsis,
|
||||
wordWrapWidth: nodeWidth - padding * 2,
|
||||
textOverflow: 'ellipsis',
|
||||
maxLines: 1,
|
||||
},
|
||||
// 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: "top-text"
|
||||
});
|
||||
|
||||
topGroup.addShape("image", {
|
||||
attrs: {
|
||||
shapeMap,
|
||||
model,
|
||||
),
|
||||
topImage: this.upsertShape(
|
||||
'image',
|
||||
'topImage',
|
||||
{
|
||||
x: padding + 5,
|
||||
y: padding + (0.5 * nodeHeight - padding) * 0.5 - 10,
|
||||
height: 20,
|
||||
width: 20,
|
||||
img: "https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png",
|
||||
opacity: 0,
|
||||
cursor: "pointer"
|
||||
img: 'https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png',
|
||||
opacity: cfg.isTopTextEllipsis ? 0 : 1,
|
||||
cursor: 'pointer',
|
||||
},
|
||||
// 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: "top-copy-img"
|
||||
});
|
||||
shapeMap,
|
||||
model,
|
||||
),
|
||||
};
|
||||
|
||||
// 下部文字区域
|
||||
const bottomGroup = group.addGroup({
|
||||
// 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: "bottom-group"
|
||||
});
|
||||
|
||||
bottomGroup.addShape("text", {
|
||||
attrs: {
|
||||
text: fittingString(cfg.bottomText, nodeWidth - 10, 14),
|
||||
x: 0.5 * nodeWidth,
|
||||
const bottomGroup = {
|
||||
bottomText: this.upsertShape(
|
||||
'text',
|
||||
'bottomText',
|
||||
{
|
||||
// text: fittingString(cfg.bottomText, nodeWidth - 10, 14),
|
||||
text: cfg.bottomText,
|
||||
x: cfg.isBottomTextEllipsis ? padding : padding + 20,
|
||||
y: nodeHeight - (0.5 * nodeHeight + padding) * 0.5,
|
||||
fontSize: 14,
|
||||
textAlign: "center",
|
||||
textBaseline: "middle",
|
||||
textAlign: 'start',
|
||||
textBaseline: 'middle',
|
||||
shadowColor: fontColor,
|
||||
fill: fontColor
|
||||
fill: fontColor,
|
||||
wordWrap: cfg.isBottomTextEllipsis,
|
||||
wordWrapWidth: nodeWidth,
|
||||
textOverflow: 'ellipsis',
|
||||
maxLines: 1,
|
||||
},
|
||||
// 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: "bottom-text"
|
||||
});
|
||||
|
||||
bottomGroup.addShape("image", {
|
||||
attrs: {
|
||||
shapeMap,
|
||||
model,
|
||||
),
|
||||
bottomImage: this.upsertShape(
|
||||
'image',
|
||||
'bottomImage',
|
||||
{
|
||||
x: 5,
|
||||
y: 0.5 * nodeHeight + 8,
|
||||
height: 20,
|
||||
width: 20,
|
||||
img: "https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png",
|
||||
opacity: 0,
|
||||
cursor: "pointer"
|
||||
img: 'https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png',
|
||||
opacity: cfg.isBottomTextEllipsis ? 0 : 1,
|
||||
cursor: 'pointer',
|
||||
},
|
||||
// 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: "bottom-copy-img"
|
||||
});
|
||||
shapeMap,
|
||||
model,
|
||||
),
|
||||
};
|
||||
|
||||
return rect;
|
||||
},
|
||||
setState(name, value, item) {
|
||||
const group = item.get("group");
|
||||
const model = item.get("model");
|
||||
const { topText, bottomText } = model;
|
||||
return { ...topGroup, ...bottomGroup };
|
||||
}
|
||||
}
|
||||
|
||||
if (name === "top-group-active") {
|
||||
const img = group.find((e) => e.get("name") === "top-copy-img");
|
||||
img.attr({
|
||||
opacity: value ? 1 : 0
|
||||
});
|
||||
|
||||
const text = group.find((e) => e.get("name") === "top-text");
|
||||
// 区域是否够长 ? 居中, 展示内容不变 : 左对其, 展示完整
|
||||
const cutStr = fittingString(topText, nodeWidth - padding * 2 - 10, 14);
|
||||
if (cutStr === topText) {
|
||||
text.attr({
|
||||
fontWeight: value ? 800 : 700,
|
||||
shadowBlur: value ? 12.2 : 0,
|
||||
text: value
|
||||
? topText
|
||||
: fittingString(topText, nodeWidth - padding * 2 - 10, 14)
|
||||
});
|
||||
} else {
|
||||
text.attr({
|
||||
fontWeight: value ? 800 : 700,
|
||||
x: value ? padding + 30 : 0.5 * nodeWidth,
|
||||
shadowBlur: value ? 12.2 : 0,
|
||||
textAlign: value ? "left" : "center",
|
||||
text: value
|
||||
? topText
|
||||
: fittingString(topText, nodeWidth - padding * 2 - 10, 14)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (name === "bottom-group-active") {
|
||||
const img = group.find((e) => e.get("name") === "bottom-copy-img");
|
||||
img.attr({
|
||||
opacity: value ? 1 : 0
|
||||
});
|
||||
|
||||
const text = group.find((e) => e.get("name") === "bottom-text");
|
||||
const cutStr = fittingString(bottomText, nodeWidth - 10, 14);
|
||||
|
||||
if (cutStr === bottomText) {
|
||||
text.attr({
|
||||
fontWeight: value ? 800 : 700,
|
||||
shadowBlur: value ? 12.2 : 0,
|
||||
text: value
|
||||
? bottomText
|
||||
: fittingString(bottomText, nodeWidth - 10, 14)
|
||||
});
|
||||
} else {
|
||||
text.attr({
|
||||
fontWeight: value ? 800 : 700,
|
||||
x: value ? 30 : 0.5 * nodeWidth,
|
||||
shadowBlur: value ? 12.2 : 0,
|
||||
textAlign: value ? "left" : "center",
|
||||
text: value
|
||||
? bottomText
|
||||
: fittingString(bottomText, nodeWidth - 10, 14)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
const ExtGraph = extend(Graph, {
|
||||
nodes: {
|
||||
'copy-node': CopyNode,
|
||||
},
|
||||
"single-node"
|
||||
);
|
||||
|
||||
graph.on("top-group:mouseenter", (e) => {
|
||||
graph.setItemState(e.item, "top-group-active", true);
|
||||
});
|
||||
graph.on("top-group:mouseleave", (e) => {
|
||||
graph.setItemState(e.item, "top-group-active", false);
|
||||
});
|
||||
graph.on("bottom-group:mouseenter", (e) => {
|
||||
graph.setItemState(e.item, "bottom-group-active", true);
|
||||
});
|
||||
graph.on("bottom-group:mouseleave", (e) => {
|
||||
graph.setItemState(e.item, "bottom-group-active", false);
|
||||
});
|
||||
|
||||
graph.on("node:click", (e) => {
|
||||
const name = e.target.get("name");
|
||||
const model = e.item.get("model");
|
||||
if (name === "top-copy-img") {
|
||||
const text = model.topText;
|
||||
const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
modes: {
|
||||
default: ['drag-node'],
|
||||
},
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 100,
|
||||
y: 100,
|
||||
topText: 'This label is too long to be displayed',
|
||||
bottomText: 'This label is too long to be displayed',
|
||||
isTopTextEllipsis: true,
|
||||
isBottomTextEllipsis: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
data: {
|
||||
x: 100,
|
||||
y: 200,
|
||||
topText: 'Short Label',
|
||||
bottomText: 'Click the Logo to Copy!',
|
||||
isTopTextEllipsis: true,
|
||||
isBottomTextEllipsis: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
node: {
|
||||
type: 'copy-node',
|
||||
keyShape: {
|
||||
x: nodeWidth / 2,
|
||||
y: nodeHeight / 2,
|
||||
width: nodeWidth,
|
||||
height: nodeHeight,
|
||||
fill: fillColor,
|
||||
lineWidth: 2,
|
||||
stroke: fontColor,
|
||||
radius: 5,
|
||||
},
|
||||
otherShapes: {},
|
||||
},
|
||||
});
|
||||
|
||||
const copyStr = (str) => {
|
||||
const input = document.createElement('textarea');
|
||||
input.value = str;
|
||||
document.body.appendChild(input);
|
||||
input.select();
|
||||
document.execCommand('Copy');
|
||||
document.body.removeChild(input);
|
||||
alert('Copy Success!');
|
||||
};
|
||||
|
||||
const resetTextEllipsis = () => {
|
||||
const nodeIds = graph.getAllNodesData().map((node) => node.id);
|
||||
nodeIds.map((nodeId) => {
|
||||
graph.updateData('node', {
|
||||
id: nodeId,
|
||||
data: {
|
||||
isTopTextEllipsis: true,
|
||||
isBottomTextEllipsis: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
graph.on('node:pointermove', (event) => {
|
||||
const { itemId, target } = event;
|
||||
resetTextEllipsis();
|
||||
if (target.id === 'topText' || target.id === 'topImage') {
|
||||
graph.updateData('node', {
|
||||
id: itemId,
|
||||
data: {
|
||||
isTopTextEllipsis: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (target.id === 'bottomText' || target.id === 'bottomImage') {
|
||||
graph.updateData('node', {
|
||||
id: itemId,
|
||||
data: {
|
||||
isBottomTextEllipsis: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
graph.on('node:click', (event) => {
|
||||
const { itemId, target } = event;
|
||||
if (target.id === 'topImage') {
|
||||
const model = graph.getNodeData(itemId);
|
||||
const text = model.data.topText;
|
||||
copyStr(text);
|
||||
} else if (name === "bottom-copy-img") {
|
||||
const text = model.bottomText;
|
||||
}
|
||||
if (target.id === 'bottomImage') {
|
||||
const model = graph.getNodeData(itemId);
|
||||
const text = model.data.bottomText;
|
||||
copyStr(text);
|
||||
}
|
||||
});
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
graph.on('node:pointerleave', (event) => {
|
||||
resetTextEllipsis();
|
||||
});
|
||||
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,125 +1,70 @@
|
||||
/**
|
||||
* When the label is too long to be displayed, hide the overflow characters and show a ellipsis instead
|
||||
* There are two ways to process the long labels
|
||||
* 1. Process in the data;
|
||||
* 2. Process when custom the edge or node like this:
|
||||
* group.addShape('text', {
|
||||
* attrs: {
|
||||
* text: fittingString(cfg.label, 50, 12),
|
||||
* ...
|
||||
* },
|
||||
* // 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: 'text-shape'
|
||||
* })
|
||||
*
|
||||
*/
|
||||
import G6 from '@antv/g6';
|
||||
|
||||
/**
|
||||
* format the string
|
||||
* @param {string} str The origin string
|
||||
* @param {number} maxWidth max width
|
||||
* @param {number} fontSize font size
|
||||
* @return {string} the processed result
|
||||
*/
|
||||
const fittingString = (str, maxWidth, fontSize) => {
|
||||
const ellipsis = '...';
|
||||
const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0];
|
||||
let currentWidth = 0;
|
||||
let res = str;
|
||||
const pattern = new RegExp('[\u4E00-\u9FA5]+'); // distinguish the Chinese charactors and letters
|
||||
str.split('').forEach((letter, i) => {
|
||||
if (currentWidth > maxWidth - ellipsisLength) return;
|
||||
if (pattern.test(letter)) {
|
||||
// Chinese charactors
|
||||
currentWidth += fontSize;
|
||||
} else {
|
||||
// get the width of single letter according to the fontSize
|
||||
currentWidth += G6.Util.getLetterWidth(letter, fontSize);
|
||||
}
|
||||
if (currentWidth > maxWidth - ellipsisLength) {
|
||||
res = `${str.substr(0, i)}${ellipsis}`;
|
||||
}
|
||||
});
|
||||
return res;
|
||||
};
|
||||
|
||||
const globalFontSize = 12;
|
||||
import { Graph } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
x: 100,
|
||||
y: 100,
|
||||
size: 40,
|
||||
label: 'This label is too long to be displayed',
|
||||
id: 'node1',
|
||||
anchorPoints: [
|
||||
[0, 0.5],
|
||||
[1, 0.5],
|
||||
],
|
||||
data: {
|
||||
x: 100,
|
||||
y: 150,
|
||||
label: 'This label is too long to be displayed',
|
||||
size: 50,
|
||||
},
|
||||
},
|
||||
{
|
||||
x: 300,
|
||||
y: 100,
|
||||
size: 80,
|
||||
label: 'This label is also too long to be displayed',
|
||||
id: 'node2',
|
||||
anchorPoints: [
|
||||
[0, 0.5],
|
||||
[1, 0.5],
|
||||
],
|
||||
data: {
|
||||
x: 400,
|
||||
y: 150,
|
||||
label: 'This label is too long to be displayed',
|
||||
size: 100,
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
label: 'This label is too long to be displayed',
|
||||
labelCfg: {
|
||||
refY: 20,
|
||||
style: {
|
||||
fontSize: globalFontSize,
|
||||
},
|
||||
},
|
||||
style: {
|
||||
endArrow: true,
|
||||
data: {
|
||||
label: 'This label is too long to be displayed',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const graph = new Graph({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
defaultNode: {
|
||||
style: {
|
||||
fill: '#DEE9FF',
|
||||
stroke: '#5B8FF9',
|
||||
modes: {
|
||||
default: ['drag-node'],
|
||||
},
|
||||
node: {
|
||||
keyShape: {
|
||||
r: {
|
||||
fields: ['size'],
|
||||
formatter: (model) => model.data.size / 2,
|
||||
},
|
||||
},
|
||||
labelCfg: {
|
||||
style: {
|
||||
fontSize: globalFontSize,
|
||||
labelShape: {
|
||||
position: 'center',
|
||||
text: {
|
||||
fields: ['label'],
|
||||
formatter: (model) => model.data.label,
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultEdge: {
|
||||
color: '#F6BD16',
|
||||
},
|
||||
data,
|
||||
});
|
||||
|
||||
// Modify the label in the data
|
||||
data.nodes.forEach(function (node) {
|
||||
node.label = fittingString(node.label, node.size, globalFontSize);
|
||||
});
|
||||
data.edges.forEach(function (edge) {
|
||||
edge.label = fittingString(edge.label, 120, globalFontSize);
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,100 +1,82 @@
|
||||
import G6 from '@antv/g6';
|
||||
/**
|
||||
* Process the long label by breaking the text
|
||||
* by Jingxi
|
||||
*
|
||||
*/
|
||||
import { Graph } from '@antv/g6';
|
||||
|
||||
/**
|
||||
* format the string
|
||||
* @param {string} str The origin string
|
||||
* @param {number} maxWidth max width
|
||||
* @param {number} fontSize font size
|
||||
* @return {string} the processed result
|
||||
*/
|
||||
const fittingString = (str, maxWidth, fontSize) => {
|
||||
let currentWidth = 0;
|
||||
let res = str;
|
||||
const pattern = new RegExp('[\u4E00-\u9FA5]+'); // distinguish the Chinese charactors and letters
|
||||
str.split('').forEach((letter, i) => {
|
||||
if (currentWidth > maxWidth) return;
|
||||
if (pattern.test(letter)) {
|
||||
// Chinese charactors
|
||||
currentWidth += fontSize;
|
||||
} else {
|
||||
// get the width of single letter according to the fontSize
|
||||
currentWidth += G6.Util.getLetterWidth(letter, fontSize);
|
||||
}
|
||||
if (currentWidth > maxWidth) {
|
||||
res = `${str.substr(0, i)}\n${str.substr(i)}`;
|
||||
}
|
||||
});
|
||||
return res;
|
||||
};
|
||||
|
||||
const globalFontSize = 12;
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
x: 100,
|
||||
y: 100,
|
||||
label: fittingString('Break the line if it is too long', 80, globalFontSize),
|
||||
id: 'node1',
|
||||
labelCfg: {
|
||||
position: 'bottom',
|
||||
data: {
|
||||
x: 100,
|
||||
y: 150,
|
||||
label: `This label is too long to be displayed`,
|
||||
size: 50,
|
||||
},
|
||||
anchorPoints: [
|
||||
[0, 0.5],
|
||||
[1, 0.5],
|
||||
],
|
||||
},
|
||||
{
|
||||
x: 300,
|
||||
y: 100,
|
||||
label: fittingString('Break the line if it is too long', 80, globalFontSize),
|
||||
id: 'node2',
|
||||
labelCfg: {
|
||||
position: 'bottom',
|
||||
data: {
|
||||
x: 400,
|
||||
y: 150,
|
||||
label: 'This label is too long to be displayed',
|
||||
size: 100,
|
||||
},
|
||||
anchorPoints: [
|
||||
[0, 0.5],
|
||||
[1, 0.5],
|
||||
],
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
label: fittingString('Break the line if it is too long', 100, globalFontSize),
|
||||
labelCfg: {
|
||||
refY: 20,
|
||||
},
|
||||
style: {
|
||||
endArrow: true,
|
||||
data: {
|
||||
label: 'This label is too long to be displayed',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const graph = new Graph({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
defaultNode: {
|
||||
type: 'rect',
|
||||
style: {
|
||||
fill: '#DEE9FF',
|
||||
stroke: '#5B8FF9',
|
||||
modes: {
|
||||
default: ['drag-node'],
|
||||
},
|
||||
node: {
|
||||
keyShape: {
|
||||
r: {
|
||||
fields: ['size'],
|
||||
formatter: (model) => model.data.size / 2,
|
||||
},
|
||||
},
|
||||
labelShape: {
|
||||
position: 'center',
|
||||
maxLines: 4,
|
||||
text: {
|
||||
fields: ['label'],
|
||||
formatter: (model) => model.data.label,
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultEdge: {
|
||||
color: '#F6BD16',
|
||||
edge: {
|
||||
labelShape: {
|
||||
position: 'center',
|
||||
maxLines: 4,
|
||||
text: {
|
||||
fields: ['label'],
|
||||
formatter: (model) => model.data.label,
|
||||
},
|
||||
wordWrapWidth: 80,
|
||||
},
|
||||
},
|
||||
data,
|
||||
});
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -23,7 +23,7 @@
|
||||
{
|
||||
"filename": "labelLen1.js",
|
||||
"title": {
|
||||
"zh": "使用换行符处理文本超长",
|
||||
"zh": "多行显示超长文本",
|
||||
"en": "Break Line for Long Label"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*EC0-R7tAn0MAAAAAAAAAAABkARQnAQ"
|
||||
|
@ -1,88 +1,69 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Graph } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
x: 150,
|
||||
y: 50,
|
||||
label: 'node1',
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
x: 250,
|
||||
y: 200,
|
||||
label: 'node2',
|
||||
},
|
||||
{
|
||||
id: 'node3',
|
||||
x: 100,
|
||||
y: 350,
|
||||
label: 'node3',
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
label: 'edge 1',
|
||||
},
|
||||
{
|
||||
source: 'node2',
|
||||
target: 'node3',
|
||||
label: 'edge 2',
|
||||
},
|
||||
{
|
||||
source: 'node3',
|
||||
target: 'node1',
|
||||
label: 'edge 3',
|
||||
},
|
||||
],
|
||||
};
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
const graph = new Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
defaultNode: {
|
||||
type: 'circle',
|
||||
labelCfg: {
|
||||
position: 'bottom',
|
||||
},
|
||||
modes: {
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'],
|
||||
},
|
||||
defaultEdge: {
|
||||
labelCfg: {
|
||||
autoRotate: true,
|
||||
style: {
|
||||
fill: '#1890ff',
|
||||
fontSize: 14,
|
||||
background: {
|
||||
fill: '#ffffff',
|
||||
stroke: '#9EC9FF',
|
||||
padding: [2, 2, 2, 2],
|
||||
radius: 2,
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 150,
|
||||
y: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
data: { x: 250, y: 200 },
|
||||
},
|
||||
{
|
||||
id: 'node3',
|
||||
data: { x: 450, y: 200 },
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
},
|
||||
{
|
||||
id: 'edge2',
|
||||
source: 'node1',
|
||||
target: 'node3',
|
||||
},
|
||||
{
|
||||
id: 'edge3',
|
||||
source: 'node2',
|
||||
target: 'node3',
|
||||
},
|
||||
],
|
||||
},
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node'],
|
||||
},
|
||||
nodeStateStyles: {
|
||||
// style configurations for hover state
|
||||
hover: {
|
||||
fillOpacity: 0.8,
|
||||
edge: {
|
||||
labelShape: {
|
||||
text: {
|
||||
fields: ['id'],
|
||||
formatter: (model) => model.id,
|
||||
},
|
||||
position: 'bottom',
|
||||
},
|
||||
// style configurations for selected state
|
||||
selected: {
|
||||
lineWidth: 5,
|
||||
labelBackgroundShape: {
|
||||
fill: 'red',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,83 +1,69 @@
|
||||
import G6 from '@antv/g6';
|
||||
import { Graph } from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
x: 150,
|
||||
y: 50,
|
||||
label: 'node1',
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
x: 250,
|
||||
y: 200,
|
||||
label: 'node2',
|
||||
},
|
||||
{
|
||||
id: 'node3',
|
||||
x: 100,
|
||||
y: 350,
|
||||
label: 'node3',
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
label: 'edge 1',
|
||||
},
|
||||
{
|
||||
source: 'node2',
|
||||
target: 'node3',
|
||||
label: 'edge 2',
|
||||
},
|
||||
{
|
||||
source: 'node3',
|
||||
target: 'node1',
|
||||
label: 'edge 3',
|
||||
},
|
||||
],
|
||||
};
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
const graph = new Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
defaultNode: {
|
||||
type: 'circle',
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: '#1890ff',
|
||||
fontSize: 14,
|
||||
background: {
|
||||
fill: '#ffffff',
|
||||
stroke: '#9EC9FF',
|
||||
padding: [2, 2, 2, 2],
|
||||
radius: 2,
|
||||
modes: {
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select'],
|
||||
},
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
data: {
|
||||
x: 150,
|
||||
y: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
data: { x: 250, y: 200 },
|
||||
},
|
||||
{
|
||||
id: 'node3',
|
||||
data: { x: 450, y: 200 },
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
},
|
||||
{
|
||||
id: 'edge2',
|
||||
source: 'node1',
|
||||
target: 'node3',
|
||||
},
|
||||
{
|
||||
id: 'edge3',
|
||||
source: 'node2',
|
||||
target: 'node3',
|
||||
},
|
||||
],
|
||||
},
|
||||
node: {
|
||||
labelShape: {
|
||||
text: {
|
||||
fields: ['id'],
|
||||
formatter: (model) => model.id,
|
||||
},
|
||||
position: 'bottom',
|
||||
},
|
||||
},
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node'],
|
||||
},
|
||||
nodeStateStyles: {
|
||||
// style configurations for hover state
|
||||
hover: {
|
||||
fillOpacity: 0.8,
|
||||
},
|
||||
// style configurations for selected state
|
||||
selected: {
|
||||
lineWidth: 5,
|
||||
labelBackgroundShape: {
|
||||
fill: 'red',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
if (typeof window !== 'undefined')
|
||||
window.onresize = () => {
|
||||
if (!graph || graph.destroyed) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.setSize([container.scrollWidth, container.scrollHeight]);
|
||||
};
|
||||
|
@ -1,16 +0,0 @@
|
||||
{
|
||||
"title": {
|
||||
"zh": "中文分类",
|
||||
"en": "Category"
|
||||
},
|
||||
"demos": [
|
||||
{
|
||||
"filename": "multiEdges.js",
|
||||
"title": {
|
||||
"zh": "两节点间存在多条边",
|
||||
"en": "Multiple Edges Between 2 Nodes"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*g2p_Qa_wZcIAAAAAAAAAAABkARQnAQ"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
import G6 from '@antv/g6';
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
x: 50,
|
||||
y: 350,
|
||||
label: 'A',
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
x: 250,
|
||||
y: 150,
|
||||
label: 'B',
|
||||
},
|
||||
{
|
||||
id: 'node3',
|
||||
x: 450,
|
||||
y: 350,
|
||||
label: 'C',
|
||||
},
|
||||
],
|
||||
edges: [],
|
||||
};
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
data.edges.push({
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
label: `${i}th edge of A-B`,
|
||||
});
|
||||
}
|
||||
for (let i = 0; i < 5; i++) {
|
||||
data.edges.push({
|
||||
source: 'node2',
|
||||
target: 'node3',
|
||||
label: `${i}th edge of B-C`,
|
||||
});
|
||||
}
|
||||
|
||||
G6.Util.processParallelEdges(data.edges);
|
||||
|
||||
const width = document.getElementById('container').scrollWidth;
|
||||
const height = document.getElementById('container').scrollHeight || 500;
|
||||
const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
// the edges are linked to the center of source and target ndoes
|
||||
linkCenter: true,
|
||||
defaultNode: {
|
||||
type: 'circle',
|
||||
size: [40],
|
||||
color: '#5B8FF9',
|
||||
style: {
|
||||
fill: '#9EC9FF',
|
||||
lineWidth: 3,
|
||||
},
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: '#000',
|
||||
fontSize: 14,
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultEdge: {
|
||||
type: 'quadratic',
|
||||
labelCfg: {
|
||||
autoRotate: true,
|
||||
},
|
||||
},
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node'],
|
||||
},
|
||||
nodeStateStyles: {
|
||||
// style configurations for hover state
|
||||
hover: {
|
||||
fillOpacity: 0.8,
|
||||
},
|
||||
// style configurations for selected state
|
||||
selected: {
|
||||
lineWidth: 5,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render();
|
@ -1,6 +0,0 @@
|
||||
---
|
||||
title: Multiple Edges Between 2 Nodes
|
||||
order: 9
|
||||
---
|
||||
|
||||
Process the parellel edges whose end nodes are the same by the built-in Util method `G6.Util.processParallelEdges(data.edges)`. If the edges are loop, they will be asigned `type: 'loop'` with different position and loop height to avoid edge overlappings. If the edges are not loop, they will be assigned `type: quadratic` with different control points to avoid edge overlappings. To achieve better rendering result, we recommended configure the `linkCenter` to be `true` while instantiating the Graph.
|
@ -1,6 +0,0 @@
|
||||
---
|
||||
title: 两节点间存在多条边
|
||||
order: 9
|
||||
---
|
||||
|
||||
两节点间存在多条边时,可以使用 G6 内置的工具函数 `G6.Util.processParallelEdges(data.edges)` 处理。它将会自动检测边数据中两端点相同的边。若为自环边,则它们的 `type` 将会被设置为 `'loop'`,并且自动计算自环高度与位置以放置重叠;若不是自环边,则它们的 `type` 将会被设置为 `'quadratic'` 二次贝塞尔曲线,并自动计算控制点位置,以避免相互重叠。为达到更好的展示效果,建议在实例化图时,将 `linkCenter` 设置为 `true`。
|
@ -967,7 +967,6 @@ graph.on('node:click', async (ev) => {
|
||||
const unitRadius = 40;
|
||||
// re-place the clicked node far away the exisiting items
|
||||
// along the radius from center node to it
|
||||
debugger;
|
||||
const focus = graph.getNodeData(focusNode.id);
|
||||
const vx = nodeModel.data.x - focus.data.x;
|
||||
const vy = nodeModel.data.y - focus.data.y;
|
||||
|
@ -46,8 +46,10 @@
|
||||
"@antv/layout-wasm": "^1.3.4",
|
||||
"@antv/util": "^2.0.9",
|
||||
"@antv/vis-predict-engine": "^0.1.1",
|
||||
"@faker-js/faker": "^8.0.2",
|
||||
"@microsoft/api-extractor": "^7.33.6",
|
||||
"dumi": "^2.2.4",
|
||||
"google-translate-api-x": "^10.6.7",
|
||||
"insert-css": "^2.0.0",
|
||||
"stats.js": "^0.17.0",
|
||||
"typedoc": "^0.24.8",
|
||||
|
Loading…
Reference in New Issue
Block a user