feat: container of plugins with dom id; fix: combo zIndex problem; fix: webworker updateLayoutCfg problem;

This commit is contained in:
Yanyan-Wang 2020-11-09 10:16:48 +08:00 committed by Yanyan Wang
parent 906d1653c0
commit f033c9f8e2
23 changed files with 352 additions and 251 deletions

View File

@ -5,8 +5,12 @@
- feat: graphin force;
- feat: updateChildren API for TreeGraph;
- feat: louvain clustering algorithm;
- feat: container of plugins with dom id;
- fix: text redidual problem, closes: #2045 #2193;
- fix: graph on callback parameter type problem, closes: #2250;
- fix: combo zIndex problem;
- fix: webworker updateLayoutCfg problem;
- fix: drag-canvas and click node on mobile;
#### 3.8.5
- fix: get fontFamily of the window in global leads to DOM depending when using bigfish;

View File

@ -92,6 +92,7 @@ export default {
if (dragCombos.length === 0) {
this.targets.push(item);
} else {
this.targets = combos;
}

View File

@ -1731,7 +1731,7 @@ export default class Graph extends EventEmitter implements IGraph {
* @param {String | INode | ICombo} item Combo id
* @param {string | undefined} parentId combo idundefined combo
*/
public updateComboTree(item: string | INode | ICombo, parentId?: string | undefined) {
public updateComboTree(item: string | INode | ICombo, parentId?: string | undefined, stack: boolean = true) {
const self = this;
this.set('comboSorted', false);
let uItem: INode | ICombo;
@ -1744,8 +1744,11 @@ export default class Graph extends EventEmitter implements IGraph {
const model = uItem.getModel();
const oldParentId = (model.comboId as string) || (model.parentId as string);
let type = '';
if (uItem.getType) type = uItem.getType();
// 若 item 是 Combo且 parentId 是其子孙 combo 的 id则警告并终止
if (parentId && uItem.getType && uItem.getType() === 'combo') {
if (parentId && type === 'combo') {
const comboTrees = this.get('comboTrees');
let valid = true;
let itemSubTree;
@ -1775,7 +1778,34 @@ export default class Graph extends EventEmitter implements IGraph {
}
}
// 当 combo 存在parentId 或 comboId 时,才将其移除
if (stack && this.get('enabledStack')) {
const beforeData: GraphData = {}, afterData: GraphData = {};
if (type === 'combo') {
beforeData.combos = [{
id: model.id,
parentId: (model as ComboConfig).parentId
}];
afterData.combos = [{
id: model.id,
parentId
}];
} else if (type === 'node') {
beforeData.nodes = [{
id: model.id,
parentId: (model as NodeConfig).comboId
}];
afterData.nodes = [{
id: model.id,
parentId
}];
}
this.pushStack('updateComboTree', {
before: beforeData,
after: afterData
});
}
// 当 combo 存在 parentId 或 comboId 时,才将其移除
if (model.parentId || model.comboId) {
const combo = this.findById((model.parentId || model.comboId) as string) as ICombo;
if (combo) {
@ -1783,9 +1813,6 @@ export default class Graph extends EventEmitter implements IGraph {
}
}
let type = '';
if (uItem.getType) type = uItem.getType();
if (type === 'combo') {
model.parentId = parentId;
} else if (type === 'node') {

View File

@ -220,7 +220,7 @@ export interface IGraph extends EventEmitter {
* @param {string | INode | ICombo} item Combo id
* @param {string | undefined} parentId combo idundefined combo
*/
updateComboTree(item: string | INode | ICombo, parentId?: string | undefined): void;
updateComboTree(item: string | INode | ICombo, parentId?: string | undefined, stack?: boolean): void;
/**
* combo

View File

@ -15,6 +15,7 @@ import {
Indexable,
ComboConfig,
ITEM_TYPE,
TreeGraphData
} from '../types';
// item 的配置项
@ -133,7 +134,7 @@ export interface IItemBase {
* / / Combo
* @return {Object}
*/
getModel(): NodeConfig | EdgeConfig | ComboConfig;
getModel(): NodeConfig | EdgeConfig | ComboConfig | TreeGraphData;
/**
*

View File

@ -209,9 +209,12 @@ export default class GraphinForceGPULayout extends BaseLayout {
centerGravities.push(nodeGravity[2]);
})
// 每个几点的额外属性占两格分别是mass, degree, nodeSterngth, centerX, centerY, gravity, 0, 0
const nodeAttributeArray = arrayToTextureData([
masses, self.degrees, nodeStrengths,
// 每个节点的额外属性占两个数组各一格nodeAttributeArray1 中是mass, degree, nodeSterngth, 0
const nodeAttributeArray1 = arrayToTextureData([
masses, self.degrees, nodeStrengths
]);
// nodeAttributeArray2 中是centerX, centerY, gravity, 0,
const nodeAttributeArray2 = arrayToTextureData([
centerXs, centerYs, centerGravities
]);
@ -236,7 +239,7 @@ export default class GraphinForceGPULayout extends BaseLayout {
// TODO: 最终的预编译代码放入到 graphinForceShader.ts 中直接引入,不再需要下面三行
const compiler = new Compiler();
const graphinForceBundle = compiler.compileBundle(graphinForceCode);
console.log(graphinForceBundle);
console.log(graphinForceBundle.toString());
const onLayoutEnd = self.onLayoutEnd;
@ -250,7 +253,8 @@ export default class GraphinForceGPULayout extends BaseLayout {
u_minMovement: self.minMovement,
u_coulombDisScale: self.coulombDisScale,
u_factor: self.factor,
u_NodeAttributeArray: nodeAttributeArray,
u_NodeAttributeArray1: nodeAttributeArray1,
u_NodeAttributeArray2: nodeAttributeArray2,
MAX_EDGE_PER_VERTEX: maxEdgePerVetex,
VERTEX_COUNT: numParticles,
u_interval: self.interval // 每次迭代更新,首次设置为 interval在 onIterationCompleted 中更新

View File

@ -1,4 +1,3 @@
export const graphinForceCode = `
import { globalInvocationID } from 'g-webgpu';
@ -26,7 +25,10 @@ class GraphinForce {
u_factor: float;
@in
u_NodeAttributeArray: vec4[];
u_NodeAttributeArray1: vec4[];
@in
u_NodeAttributeArray2: vec4[];
@in
u_EdgeAttributeArray: vec4[];
@ -48,8 +50,8 @@ class GraphinForce {
const n_dist = dist * this.u_coulombDisScale;
const direx = vx / dist;
const direy = vy / dist;
const attributesi = this.u_NodeAttributeArray[2 * i];
const attributesj = this.u_NodeAttributeArray[2 * j];
const attributesi = this.u_NodeAttributeArray1[2 * i];
const attributesj = this.u_NodeAttributeArray1[(int)2 * j];
const massi = attributesi[0];
const nodeStrengthi = attributesi[2];
const nodeStrengthj = attributesj[2];
@ -63,14 +65,14 @@ class GraphinForce {
return [ax, ay];
}
calcGravity(i: int, currentNode: vec4, attributes: vec4): vec2 {
// note: attributes = [mass, degree, nodeSterngth, centerX, centerY, gravity, 0, 0]
calcGravity(i: int, currentNode: vec4, attributes2: vec4): vec2 {
// note: attributes2 = [centerX, centerY, gravity, 0]
const vx = currentNode[0] - attributes[3];
const vy = currentNode[1] - attributes[4];
const vx = currentNode[0] - attributes2[0];
const vy = currentNode[1] - attributes2[1];
const ax = vx * attributes[5];
const ay = vy * attributes[5];
const ax = vx * attributes[3];
const ay = vy * attributes[3];
@ -85,10 +87,10 @@ class GraphinForce {
return [ax, ay];
}
calcAttractive(i: int, currentNode: vec4, attributes: vec4): vec2 {
// note: attributes = [mass, degree, nodeSterngth, centerX, centerY, gravity, 0, 0]
calcAttractive(i: int, currentNode: vec4, attributes1: vec4): vec2 {
// note: attributes = [mass, degree, nodeSterngth, 0]
const mass = attributes[0];
const mass = attributes1[0];
let ax = 0, ay = 0;
const arr_offset = int(floor(currentNode[2] + 0.5));
const length = int(floor(currentNode[3] + 0.5));
@ -131,8 +133,9 @@ class GraphinForce {
return;
}
// 每个节点属性占两小格
const nodeAttributes = this.u_NodeAttributeArray[2 * i];
// 每个节点属性占两个数组中各一格
const nodeAttributes1 = this.u_NodeAttributeArray1[2 * i];
const nodeAttributes2 = this.u_NodeAttributeArray2[2 * i];
// repulsive
const repulsive = this.calcRepulsive(i, currentNode);
@ -140,17 +143,16 @@ class GraphinForce {
ay += repulsive[1];
// attractive
const attractive = this.calcAttractive(i, currentNode, nodeAttributes);
const attractive = this.calcAttractive(i, currentNode, nodeAttributes1);
ax += attractive[0];
ay += attractive[1];
// gravity
const gravity = this.calcGravity(i, currentNode, nodeAttributes);
const gravity = this.calcGravity(i, currentNode, nodeAttributes2);
ax -= gravity[0];
ay -= gravity[1];
// speed
const interval = 0.02; // max(0.02, 0.22 - 0.002 * this.u_iter);
const param = this.u_interval * this.u_damping;
let vx = ax * param;
let vy = ay * param;

View File

@ -5,7 +5,7 @@ import Graph from '../graph/graph';
import { IG6GraphEvent } from '../types';
export interface IPluginBaseConfig {
container?: HTMLDivElement | null;
container?: HTMLDivElement | string | null;
className?: string;
graph?: Graph;
[key: string]: any;

View File

@ -81,7 +81,10 @@ export default class ImageMiniMap extends Base {
const { graph } = cfgs;
if (this.destroyed) return;
const containerDOM = this.get('container');
let containerDOM = this.get('container');
if (isString(containerDOM)) {
containerDOM = document.getElementById(containerDOM) as HTMLDivElement;
}
const viewport = createDOM(
`<div class=${cfgs.viewportClassName}
style='position:absolute;

View File

@ -77,6 +77,9 @@ export default class Menu extends Base {
if (!container) {
container = this.get('graph').get('container');
}
if (isString(container)) {
container = document.getElementById(container) as HTMLDivElement;
}
container.appendChild(menu);
this.set('menu', menu);
@ -193,6 +196,9 @@ export default class Menu extends Base {
if (!container) {
container = this.get('graph').get('container');
}
if (isString(container)) {
container = document.getElementById(container) as HTMLDivElement;
}
container.removeChild(menu);
}
}

View File

@ -14,6 +14,7 @@ import { VALUE_CHANGE } from './constant'
import { GraphData, IG6GraphEvent, ShapeStyle, TimeBarType } from '../../types';
import { Interval } from './trend';
import { ControllerCfg } from './controllerBtn';
import { isString } from '@antv/util';
// simple 版本默认高度
const DEFAULT_SIMPLE_HEIGHT = 8
@ -109,7 +110,7 @@ export default class TimeBar extends Base {
const { width, height } = this._cfgs
const className: string = this.get('className') || 'g6-component-timebar';
const container: HTMLDivElement | null = this.get('container');
let container: HTMLDivElement | null | string = this.get('container');
const graphContainer = this.get('graph').get('container');
@ -118,6 +119,9 @@ export default class TimeBar extends Base {
timeBarContainer = createDOM(`<div class='${className}'></div>`);
modifyCSS(timeBarContainer, { position: 'relative' });
} else {
if (isString(container)) {
container = document.getElementById(container) as HTMLDivElement;
}
timeBarContainer = container;
}
@ -308,6 +312,9 @@ export default class TimeBar extends Base {
if (!container) {
container = this.get('graph').get('container');
}
if (isString(container)) {
container = document.getElementById(container) as HTMLDivElement;
}
container.removeChild(timeBarContainer);
}
}

View File

@ -104,6 +104,9 @@ export default class ToolBar extends Base {
if (!container) {
container = this.get('graph').get('container');
}
if (isString(container)) {
container = document.getElementById(container) as HTMLDivElement;
}
container!.appendChild(toolBarDOM);
this.set('toolBar', toolBarDOM);
@ -249,6 +252,15 @@ export default class ToolBar extends Base {
});
});
break;
case 'updateComboTree':
Object.keys(data).forEach(key => {
const array = data[key];
if (!array) return;
array.forEach(model => {
graph.updateComboTree(model.id, model.parentId, false);
});
});
break;
default:
}
}
@ -334,6 +346,15 @@ export default class ToolBar extends Base {
});
break;
}
case 'updateComboTree':
Object.keys(data).forEach(key => {
const array = data[key];
if (!array) return;
array.forEach(model => {
graph.updateComboTree(model.id, model.parentId, false);
});
});
break;
default:
}
}
@ -387,6 +408,9 @@ export default class ToolBar extends Base {
if (!container) {
container = this.get('graph').get('container');
}
if (isString(container)) {
container = document.getElementById(container) as HTMLDivElement;
}
container.removeChild(toolBar);
}

View File

@ -79,6 +79,9 @@ export default class Tooltip extends Base {
if (!container) {
container = this.get('graph').get('container');
}
if (isString(container)) {
container = document.getElementById(container) as HTMLDivElement;
}
modifyCSS(tooltip, { position: 'absolute', visibility: 'hidden' });
container.appendChild(tooltip);
@ -196,6 +199,9 @@ export default class Tooltip extends Base {
if (!container) {
container = this.get('graph').get('container');
}
if (isString(container)) {
container = document.getElementById(container) as HTMLDivElement;
}
container.removeChild(tooltip);
}
}

View File

@ -400,6 +400,7 @@ export interface TreeGraphData {
[key: string]: ShapeStyle;
};
stateStyles?: StateStyles;
[key: string]: unknown;
}
export interface NodeConfig extends ModelConfig {

View File

@ -530,8 +530,9 @@ export const plainCombosToTrees = (array: ComboConfig[], nodes?: NodeConfig[]) =
});
// assign the depth for each element
let maxDepth = 0;
result.forEach((tree: ComboTree) => {
tree.depth = 0;
tree.depth = maxDepth + 10;
traverse<ComboTree>(tree, (child) => {
let parent;
const itemType = addedMap[child.id].itemType;
@ -541,11 +542,12 @@ export const plainCombosToTrees = (array: ComboConfig[], nodes?: NodeConfig[]) =
parent = addedMap[child.parentId];
}
if (parent) {
if (itemType === 'node') child.depth = parent.depth + 1;
else child.depth = parent.depth + 2;
if (itemType === 'node') child.depth = maxDepth + 1;
else child.depth = maxDepth + 10;
} else {
child.depth = 0;
child.depth = maxDepth + 10;
}
if (maxDepth < child.depth) maxDepth = child.depth;
const oriNodeModel = nodeMap[child.id];
if (oriNodeModel) {
oriNodeModel.depth = child.depth;

View File

@ -16,6 +16,19 @@ import {
} from '../types';
import { each } from '@antv/util';
/**
*
* @param {string} attributeName
* @param {number} min
* @param {number} max
* @return {boolean} bool
*/
export const compare = (attributeName: string) => {
return (m, n) => {
return m[attributeName] - n[attributeName];
}
};
/**
*
* @param {number} value

View File

@ -1,6 +1,7 @@
import React, { useEffect } from 'react';
import G6 from '../../../src';
import { IGraph } from '../../../src/interface/graph';
import { NodeConfig } from '../../../src/types';
let graph: IGraph = null;
let showNodes = [];
@ -256,7 +257,7 @@ const DecisionTree = () => {
graph.on('node:mouseenter', (e) => {
const item = e.item;
const model = item.getModel();
const model = item.getModel() as NodeConfig;
if (model.level === 0) {
return;
}
@ -270,14 +271,14 @@ const DecisionTree = () => {
});
graph.setItemState(item, 'dark', false);
model.light = true;
const tags = model.tags;
const tags: string[] = model.tags as string[];
const findTagsMap = new Map();
let mid = 0;
let fTag = '';
// if the model is F node, find the leaves of it
if (!model.isLeaf && model.level !== 0) {
fTag = model.tag;
fTag = model.tag as string;
nodeItems.forEach((item) => {
const itemModel = item.getModel();
if (!itemModel.isLeaf) return;
@ -350,7 +351,7 @@ const DecisionTree = () => {
curShowNodes = [];
curShowEdges = [];
const item = e.item;
const model = item.getModel();
const model = item.getModel() as NodeConfig;
if (!model.isLeaf && model.level !== 0) {
return;
}
@ -431,8 +432,8 @@ const DecisionTree = () => {
}
if (isChild) {
const randomAngle = Math.random() * 2 * Math.PI;
node.x = model.x + (Math.cos(randomAngle) * model.size) / 2 + 10;
node.y = model.y + (Math.sin(randomAngle) * model.size) / 2 + 10;
node.x = model.x + (Math.cos(randomAngle) * (model.size as number)) / 2 + 10;
node.y = model.y + (Math.sin(randomAngle) * (model.size as number)) / 2 + 10;
// const dist = (model.x - node.x) * (model.x - node.x) + (model.y - node.y) * (model.y - node.y);
if (!node.style) node.style = {};

View File

@ -2,6 +2,7 @@ import React, { useEffect } from 'react';
import G6 from '../../../src';
import { IGraph } from '../../../src/interface/graph';
import { GraphData } from '../../../src/types';
import { ICombo } from '../../../src/interface/item';
let graph: IGraph = null;
@ -327,7 +328,7 @@ const DagreCombo = () => {
// }, 500);
graph.on('combo:click', function (e) {
graph.collapseExpandCombo(e.item);
graph.collapseExpandCombo(e.item as ICombo);
graph.refreshPositions();
});

View File

@ -7,37 +7,19 @@ let graph: IGraph = null;
const data: GraphData = {
nodes: [
{
id: 'node1',
label: 'node1',
x: 250,
y: 150,
comboId: 'combo',
},
{
id: 'node2',
label: 'node2',
x: 350,
y: 150,
comboId: 'combo',
},
],
combos: [
{
id: 'combo',
label: 'Combo ',
},
{
id: 'combo1',
label: 'Combo',
},
{ id: 'node1', x: 350, y: 200, comboId: 'combo1', label: 'node1' },
{ id: 'node2', x: 350, y: 250, comboId: 'combo1', label: 'node2' },
{ id: 'node3', x: 100, y: 200, comboId: 'combo3', label: 'node3' },
],
edges: [
{
id: 'edge1',
source: 'combo',
target: 'combo1',
},
{ 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' },
],
};

View File

@ -2,6 +2,7 @@ import React, { useEffect } from 'react';
import G6 from '../../../src';
import { IGraph } from '../../../src/interface/graph';
import { GraphData } from '../../../src/types';
import { ICombo } from '../../../src/interface/item';
let graph: IGraph = null;
@ -140,7 +141,7 @@ const Edges2 = () => {
graph.data(data);
graph.render();
graph.on('combo:click', function (e) {
graph.collapseExpandCombo(e.item);
graph.collapseExpandCombo(e.item as ICombo);
graph.refreshPositions();
});

View File

@ -1,5 +1,6 @@
import React, { Component } from 'react';
import G6 from '../../../src';
import { IEdge } from '../../../src/interface/item';
// import "./styles.css";
export default class ForceLayout extends Component {
@ -823,7 +824,7 @@ export default class ForceLayout extends Component {
const part1NodeMap = new Map();
const part2NodeMap = new Map();
// separate the nodes and init the positions
nodes.forEach(function(node, i) {
nodes.forEach(function (node, i) {
if (node.name === "MySQL") {
part1Nodes.push(node);
part1NodeMap.set(node.id, i);
@ -843,7 +844,7 @@ export default class ForceLayout extends Component {
// if (direction === "vertical") {
// begin = center[0] - height / 2;
// }
part1Nodes.forEach(function(p1n, i) {
part1Nodes.forEach(function (p1n, i) {
if (direction === "horizontal") {
p1n.x = part1Pos;
p1n.y = begin + i * (nodeSep + nodeSize);
@ -852,7 +853,7 @@ export default class ForceLayout extends Component {
p1n.y = part1Pos;
}
});
part2Nodes.forEach(function(p2n, i) {
part2Nodes.forEach(function (p2n, i) {
if (direction === "horizontal") {
p2n.x = part2Pos;
p2n.y = begin + i * (nodeSep + nodeSize);
@ -902,7 +903,7 @@ export default class ForceLayout extends Component {
const part3NodeMap = new Map();
const part4NodeMap = new Map();
// separate the nodes and init the positions
nodes.forEach(function(node, i) {
nodes.forEach(function (node, i) {
if (node.name === "MySQL") {
part1Nodes.push(node);
part1NodeMap.set(node.id, i);
@ -941,27 +942,27 @@ export default class ForceLayout extends Component {
// if (direction === "vertical") {
// begin = center[0] - height / 2;
// }
part1Nodes.forEach(function(p1n, i) {
part1Nodes.forEach(function (p1n, i) {
if (direction === "rect") {
p1n.x = xbegin - nodeSize;
p1n.y = hbegin + i * (nodeSep + nodeSize);
// console.log(p1n);
}
});
part2Nodes.forEach(function(p2n, i) {
part2Nodes.forEach(function (p2n, i) {
if (direction === "rect") {
p2n.x = xbegin + width;
p2n.y = hbegin + i * (nodeSep + nodeSize);
// console.log(p2n);
}
});
part3Nodes.forEach(function(p3n, i) {
part3Nodes.forEach(function (p3n, i) {
if (direction === "rect") {
p3n.x = xbegin + i * (nodeSep + nodeSize);
p3n.y = part3Pos;
}
});
part4Nodes.forEach(function(p4n, i) {
part4Nodes.forEach(function (p4n, i) {
if (direction === "rect") {
p4n.x = xbegin + i * (nodeSep + nodeSize);
p4n.y = part4Pos;
@ -979,121 +980,121 @@ export default class ForceLayout extends Component {
};
G6.registerNode(
'icon-node',
{
options: {
size: [60, 20],
stroke: '#91d5ff',
fill: '#91d5ff',
},
draw(cfg, group) {
const styles = this.getShapeStyle(cfg);
const { labelCfg = {} } = cfg;
const keyShape = group.addShape('rect', {
attrs: {
...styles,
x: 0,
y: 0,
},
});
/**
* leftIcon
* {
* style: ShapeStyle;
* img: ''
* }
*/
// console.log('cfg.leftIcon', cfg.leftIcon);
// if (cfg.leftIcon) {
// const { style, img } = cfg.leftIcon;
group.addShape('rect', {
attrs: {
x: 1,
y: 1,
width: 38,
height: styles.height - 2,
fill: '#ffffff',
// ...style,
},
});
// group.addShape('rect', {
// attrs: {
// x: 180,
// y: 1,
// width: 22,
// height: styles.height - 2,
// fill: '#ffffff',
// // ...style,
// },
// });
group.addShape('image', {
attrs: {
x: 1,
y: 1,
width: 38,
height: 30,
// img:
// img ||
// 'https://g.alicdn.com/cm-design/arms-trace/1.0.155/styles/armsTrace/images/TAIR.png',
},
name: 'image-shape',
});
// }
// 如果不需要动态增加或删除元素,则不需要 add 这两个 marker
group.addShape('marker', {
attrs: {
x: -6,
y: 15,
r: 6,
stroke: '#73d13d',
cursor: 'pointer',
symbol: EXPAND_ICON,
},
name: 'add-tableData',
});
group.addShape('marker', {
attrs: {
x: 46,
y: 15,
r: 6,
stroke: '#73d13d',
cursor: 'pointer',
symbol: EXPAND_ICON,
},
name: 'add-fieldData',
});
group.addShape('marker', {
attrs: {
x: 190,
y: 22,
r: 6,
stroke: '#ffffff',
cursor: 'pointer',
symbol: EXPAND_ICON,
},
name: 'show-tableData',
});
if (cfg.label) {
group.addShape('text', {
attrs: {
...labelCfg.style,
text: cfg.label,
x: 100,
y: 20,
},
});
}
return keyShape;
},
'icon-node',
{
options: {
size: [60, 20],
stroke: '#91d5ff',
fill: '#91d5ff',
},
'rect',
);
draw(cfg, group) {
const styles = this.getShapeStyle(cfg);
const { labelCfg = {} } = cfg;
const keyShape = group.addShape('rect', {
attrs: {
...styles,
x: 0,
y: 0,
},
});
/**
* leftIcon
* {
* style: ShapeStyle;
* img: ''
* }
*/
// console.log('cfg.leftIcon', cfg.leftIcon);
// if (cfg.leftIcon) {
// const { style, img } = cfg.leftIcon;
group.addShape('rect', {
attrs: {
x: 1,
y: 1,
width: 38,
height: styles.height - 2,
fill: '#ffffff',
// ...style,
},
});
// group.addShape('rect', {
// attrs: {
// x: 180,
// y: 1,
// width: 22,
// height: styles.height - 2,
// fill: '#ffffff',
// // ...style,
// },
// });
group.addShape('image', {
attrs: {
x: 1,
y: 1,
width: 38,
height: 30,
// img:
// img ||
// 'https://g.alicdn.com/cm-design/arms-trace/1.0.155/styles/armsTrace/images/TAIR.png',
},
name: 'image-shape',
});
// }
// 如果不需要动态增加或删除元素,则不需要 add 这两个 marker
group.addShape('marker', {
attrs: {
x: -6,
y: 15,
r: 6,
stroke: '#73d13d',
cursor: 'pointer',
symbol: EXPAND_ICON,
},
name: 'add-tableData',
});
group.addShape('marker', {
attrs: {
x: 46,
y: 15,
r: 6,
stroke: '#73d13d',
cursor: 'pointer',
symbol: EXPAND_ICON,
},
name: 'add-fieldData',
});
group.addShape('marker', {
attrs: {
x: 190,
y: 22,
r: 6,
stroke: '#ffffff',
cursor: 'pointer',
symbol: EXPAND_ICON,
},
name: 'show-tableData',
});
if (cfg.label) {
group.addShape('text', {
attrs: {
...labelCfg.style,
text: cfg.label,
x: 100,
y: 20,
},
});
}
return keyShape;
},
},
'rect',
);
G6.registerEdge(
'circle-running',
@ -1140,7 +1141,7 @@ export default class ForceLayout extends Component {
// 3.图实例化
const width = document.getElementById('container').scrollWidth;
const height = document.getElementById('container').scrollHeight || 500;
const minimap = new G6.Minimap({
size: [300, 300],
});
@ -1157,14 +1158,14 @@ export default class ForceLayout extends Component {
// },
offset: 30
}, {
type: 'edge-tooltip',
// formatText: function formatText(model, e) {
// const edge = e.item;
// return ('来源:' + edge.getSource().getModel().name +
// '<br/>去向:' + edge.getTarget().getModel().name)
// },
offset: 30
}, 'activate-relations'],
type: 'edge-tooltip',
// formatText: function formatText(model, e) {
// const edge = e.item;
// return ('来源:' + edge.getSource().getModel().name +
// '<br/>去向:' + edge.getTarget().getModel().name)
// },
offset: 30
}, 'activate-relations'],
},
layout: {
type: 'circular',
@ -1292,15 +1293,15 @@ export default class ForceLayout extends Component {
// },
// },
// });
}
}
})
// 7.连线的交互事件
graph.on('edge:mouseenter', (evt) => {
const { item } = evt;
graph.setItemState(item, 'hover', true);
// 获取连线起点和终点坐标的对象后调用函数
const edge = evt.item;
getCubicController (edge.getSource().getModel(), edge.getTarget().getModel());
const edge: IEdge = evt.item as IEdge;
getCubicController(edge.getSource().getModel(), edge.getTarget().getModel());
});
graph.on('edge:mouseleave', (evt) => {
const { item } = evt;
@ -1312,17 +1313,17 @@ export default class ForceLayout extends Component {
// console.log(item.get('model').style);
// item.get('model').style = {fill: "black"};
// 获取连线起点和终点坐标的对象后调用函数
const edge = evt.item;
const edge: IEdge = evt.item as IEdge;
console.log(edge.getSource().getModel().name);
console.log(edge.getSource().getModel());
console.log(edge.getTarget().getModel().name);
console.log(edge.getTarget().getModel());
var temp = getCubicController (edge.getSource().getModel(), edge.getTarget().getModel());
var temp = getCubicController(edge.getSource().getModel(), edge.getTarget().getModel());
console.log(temp);
})
function circleFun () {
graph.updateLayout({type: 'circular', preventOverlap: true, radius: 200});
function circleFun() {
graph.updateLayout({ type: 'circular', preventOverlap: true, radius: 200 });
document.getElementById('radial').style.backgroundColor = '#e2e2e2';
document.getElementById('bigraph').style.backgroundColor = '#e2e2e2';
document.getElementById('grid').style.backgroundColor = '#e2e2e2';
@ -1333,8 +1334,8 @@ export default class ForceLayout extends Component {
document.getElementById('dagre').style.backgroundColor = '#e2e2e2 ';
document.getElementById('rect').style.backgroundColor = '#e2e2e2';
}
function radialFun () {
graph.updateLayout({type: 'radial', preventOverlap: true, nodeSize: 203, strictRadial: true, linkDistance: 50, nodeSpacing: 30});
function radialFun() {
graph.updateLayout({ type: 'radial', preventOverlap: true, nodeSize: 203, strictRadial: true, linkDistance: 50, nodeSpacing: 30 });
document.getElementById('circular').style.backgroundColor = '#e2e2e2 ';
document.getElementById('bigraph').style.backgroundColor = '#e2e2e2';
document.getElementById('grid').style.backgroundColor = '#e2e2e2';
@ -1345,8 +1346,8 @@ export default class ForceLayout extends Component {
document.getElementById('dagre').style.backgroundColor = '#e2e2e2 ';
document.getElementById('rect').style.backgroundColor = '#e2e2e2';
}
function bigraphFun () {
graph.updateLayout({type: 'bigraph-layout'});
function bigraphFun() {
graph.updateLayout({ type: 'bigraph-layout' });
document.getElementById('radial').style.backgroundColor = '#e2e2e2';
document.getElementById('grid').style.backgroundColor = '#e2e2e2';
document.getElementById('circular').style.backgroundColor = '#e2e2e2 ';
@ -1357,7 +1358,7 @@ export default class ForceLayout extends Component {
document.getElementById('dagre').style.backgroundColor = '#e2e2e2 ';
document.getElementById('rect').style.backgroundColor = '#e2e2e2';
}
function gridFun () {
function gridFun() {
graph.updateLayout({
type: 'grid',
begin: [20, 20],
@ -1378,7 +1379,7 @@ export default class ForceLayout extends Component {
document.getElementById('dagre').style.backgroundColor = '#e2e2e2 ';
document.getElementById('rect').style.backgroundColor = '#e2e2e2';
}
function fruchtermanFun () {
function fruchtermanFun() {
graph.updateLayout({
type: 'fruchterman',
gravity: 5,
@ -1394,7 +1395,7 @@ export default class ForceLayout extends Component {
document.getElementById('dagre').style.backgroundColor = '#e2e2e2 ';
document.getElementById('rect').style.backgroundColor = '#e2e2e2';
}
function concentricFun () {
function concentricFun() {
graph.updateLayout({
type: 'concentric',
preventOverlap: true,
@ -1411,7 +1412,7 @@ export default class ForceLayout extends Component {
document.getElementById('dagre').style.backgroundColor = '#e2e2e2 ';
document.getElementById('rect').style.backgroundColor = '#e2e2e2';
}
function MDSFun () {
function MDSFun() {
graph.updateLayout({
type: 'mds',
linkDistance: 200
@ -1426,7 +1427,7 @@ export default class ForceLayout extends Component {
document.getElementById('dagre').style.backgroundColor = '#e2e2e2 ';
document.getElementById('rect').style.backgroundColor = '#e2e2e2';
}
function DagreFun () {
function DagreFun() {
graph.updateLayout({
type: 'dagre',
rankdir: 'LR',
@ -1445,8 +1446,8 @@ export default class ForceLayout extends Component {
document.getElementById('dagre').style.backgroundColor = '#99FFFF ';
document.getElementById('rect').style.backgroundColor = '#e2e2e2';
}
function rectFun () {
graph.updateLayout({type: 'rect-layout'});
function rectFun() {
graph.updateLayout({ type: 'rect-layout' });
document.getElementById('radial').style.backgroundColor = '#e2e2e2';
document.getElementById('grid').style.backgroundColor = '#e2e2e2';
document.getElementById('circular').style.backgroundColor = '#e2e2e2 ';
@ -1457,36 +1458,36 @@ export default class ForceLayout extends Component {
document.getElementById('dagre').style.backgroundColor = '#e2e2e2 ';
document.getElementById('rect').style.backgroundColor = '#99FFFF';
}
function getCubicController (source, target) {
function getCubicController(source, target) {
let c1, c2;
let distance = (target.y - source.y)/5;
if( distance > 0 ) distance = Math.max(distance, 80);
if( distance < 0 ) distance = Math.min(distance, -80);
let distance = (target.y - source.y) / 5;
if (distance > 0) distance = Math.max(distance, 80);
if (distance < 0) distance = Math.min(distance, -80);
let p1 = [
source.x,
source.y
source.y
];
let p4 = [
target.x,
target.y
target.x,
target.y
]
if( target.y > source.y ) {
c1 = [ source.x, source.y + distance ];
c2 = [ target.x, target.y - distance ];
if (target.y > source.y) {
c1 = [source.x, source.y + distance];
c2 = [target.x, target.y - distance];
}
else {
c1 = [ source.x, source.y - distance ];
c2 = [ target.x, target.y + distance ];
c1 = [source.x, source.y - distance];
c2 = [target.x, target.y + distance];
}
return {
c1,
c2
}
}
}

View File

@ -6,7 +6,8 @@ let graph: IGraph = null;
const data = {
nodes: [{
id: '1'
id: '1',
comboId: 'c1'
}, {
id: '2'
},],
@ -14,6 +15,11 @@ const data = {
source: '1',
target: '2'
}],
combos: [{
id: 'c1'
}, {
id: 'c2'
}]
};
const data2 = {
nodes: [{
@ -35,7 +41,14 @@ const ToolBar = () => {
const container = React.useRef();
useEffect(() => {
if (!graph) {
const toolbar = new G6.ToolBar();
const toolbarDiv = document.createElement('div');
toolbarDiv.id = 'toolbarContainer';
const graphContainer = container.current as HTMLElement;
graphContainer.parentElement.appendChild(toolbarDiv);
const toolbar = new G6.ToolBar({
container: 'toolbarContainer'
});
graph = new Graph({
container: container.current as string | HTMLElement,
width: 500,
@ -44,7 +57,7 @@ const ToolBar = () => {
// 设置为true启用 redo & undo 栈功能
enabledStack: true,
modes: {
default: ['zoom-canvas', 'drag-node', { type: 'brush-select', }],
default: ['zoom-canvas', 'drag-node', { type: 'brush-select', }, 'drag-combo'],
},
defaultNode: {
size: 50

View File

@ -28,8 +28,8 @@ interface IFlowCharts {
handleEdgeClick?: (item: IEdge, graph: IGraph) => void;
handleNodeHover?: (item: INode, graph: IGraph) => void;
handleNodeUnHover?: (item: INode, graph: IGraph) => void;
handleEdgeHover?: (item: INode, graph: IGraph) => void;
handleEdgeUnHover?: (item: INode, graph: IGraph) => void;
handleEdgeHover?: (item: IEdge, graph: IGraph) => void;
handleEdgeUnHover?: (item: IEdge, graph: IGraph) => void;
collapseExpand?: boolean;
}
@ -375,7 +375,7 @@ const FlowComponent: React.SFC<IFlowCharts> = ({
}
graph.on('node:mouseenter', (evt) => {
const { item } = evt;
const item: INode = evt.item as INode;
graph.setItemState(item, 'hover', true);
if (handleNodeHover) {
handleNodeHover(item, graph);
@ -383,7 +383,7 @@ const FlowComponent: React.SFC<IFlowCharts> = ({
});
graph.on('node:mouseleave', (evt) => {
const { item } = evt;
const item: INode = evt.item as INode;
graph.setItemState(item, 'hover', false);
if (handleNodeUnHover) {
handleNodeUnHover(item, graph);
@ -391,20 +391,21 @@ const FlowComponent: React.SFC<IFlowCharts> = ({
});
graph.on('node:click', (evt) => {
const { item, target } = evt;
const item: INode = evt.item as INode;
const { target } = evt;
const targetType = target.get('type');
const name = target.get('name');
// 增加元素
if (targetType === 'marker') {
const model = item.getModel();
const model: TreeGraphData = item.getModel() as TreeGraphData;
if (name === 'add-item') {
if (!model.children) {
model.children = [];
}
model.children.push({
id: Math.random(),
label: Math.random(),
id: `${Math.random()}`,
label: `${Math.random()}`
});
graph.updateChild(model, model.id);
} else if (name === 'remove-item') {
@ -418,7 +419,7 @@ const FlowComponent: React.SFC<IFlowCharts> = ({
});
graph.on('edge:mouseenter', (evt) => {
const { item } = evt;
const item: IEdge = evt.item as IEdge;
graph.setItemState(item, 'hover', true);
if (handleEdgeHover) {
handleEdgeHover(item, graph);
@ -426,7 +427,7 @@ const FlowComponent: React.SFC<IFlowCharts> = ({
});
graph.on('edge:mouseleave', (evt) => {
const { item } = evt;
const item: IEdge = evt.item as IEdge;
graph.setItemState(item, 'hover', false);
if (handleEdgeUnHover) {
handleEdgeUnHover(item, graph);
@ -434,7 +435,7 @@ const FlowComponent: React.SFC<IFlowCharts> = ({
});
graph.on('edge:click', (evt) => {
const { item } = evt;
const item: IEdge = evt.item as IEdge;
if (handleEdgeClick) {
handleEdgeClick(item, graph);
}