fix: 添加力导向布局
This commit is contained in:
parent
2cdc35ff30
commit
1f9ddfc672
71
demoV2/Layouter/Force.js
Normal file
71
demoV2/Layouter/Force.js
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SV.registerLayout('Force', {
|
||||||
|
defineOptions() {
|
||||||
|
return {
|
||||||
|
node: {
|
||||||
|
default: {
|
||||||
|
type: 'force-node',
|
||||||
|
label: '[data]',
|
||||||
|
size: 20,
|
||||||
|
labelOptions: {
|
||||||
|
style: { fontSize: 20 }
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
stroke: 'red',
|
||||||
|
fill: 'red'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
next: {
|
||||||
|
type: 'line',
|
||||||
|
sourceAnchor: 0,
|
||||||
|
targetAnchor: 0,
|
||||||
|
style: {
|
||||||
|
stroke: '#333',
|
||||||
|
lineAppendWidth: 6,
|
||||||
|
cursor: 'pointer',
|
||||||
|
// endArrow: 'default',
|
||||||
|
startArrow: {
|
||||||
|
path: G6.Arrow.circle(2, -1),
|
||||||
|
fill: '#333'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
marker: {
|
||||||
|
headExternal: {
|
||||||
|
type: 'pointer',
|
||||||
|
anchor: 3,
|
||||||
|
style: {
|
||||||
|
fill: '#f08a5d'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
external: {
|
||||||
|
type: 'pointer',
|
||||||
|
anchor: 0,
|
||||||
|
style: {
|
||||||
|
fill: '#f08a5d'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
indexLabel: {
|
||||||
|
index: { position: 'bottom' },
|
||||||
|
indexRight: { position: 'right' }
|
||||||
|
},
|
||||||
|
behavior: {
|
||||||
|
dragNode: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
layout(e) {
|
||||||
|
console.log("here is the layout of Force")
|
||||||
|
// e.forEach((item, index) => {
|
||||||
|
// console.log(item.getBound());
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -2,7 +2,7 @@ import { Util } from "../Common/util";
|
|||||||
import { Style } from "../options";
|
import { Style } from "../options";
|
||||||
import { BoundingRect } from "../Common/boundingRect";
|
import { BoundingRect } from "../Common/boundingRect";
|
||||||
import { EdgeConfig, Item, NodeConfig } from "@antv/g6-core";
|
import { EdgeConfig, Item, NodeConfig } from "@antv/g6-core";
|
||||||
import { Graph } from "_@antv_g6-pc@0.5.0@@antv/g6-pc";
|
import { Graph } from "@antv/g6";
|
||||||
import merge from 'merge';
|
import merge from 'merge';
|
||||||
|
|
||||||
|
|
||||||
|
47
src/RegisteredShape/force.ts
Normal file
47
src/RegisteredShape/force.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { Util } from "../Common/util";
|
||||||
|
|
||||||
|
|
||||||
|
export default Util.registerShape('force-node', {
|
||||||
|
draw(cfg, group) {
|
||||||
|
// cfg.size = cfg.size;
|
||||||
|
const size = 15;
|
||||||
|
|
||||||
|
const wrapperRect = group.addShape('circle', {
|
||||||
|
attrs: {
|
||||||
|
r: size,
|
||||||
|
stroke: 'rgb(35, 120, 180)',
|
||||||
|
// cursor: cfg.style.cursor,
|
||||||
|
fill: 'rgb(31, 119, 180)'
|
||||||
|
},
|
||||||
|
name: 'wrapper'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (cfg.label) {
|
||||||
|
const style = (cfg.labelCfg && cfg.labelCfg.style) || {};
|
||||||
|
group.addShape('text', {
|
||||||
|
attrs: {
|
||||||
|
x: 0, // 居中
|
||||||
|
y: 0,
|
||||||
|
textAlign: 'center',
|
||||||
|
textBaseline: 'middle',
|
||||||
|
text: cfg.label,
|
||||||
|
fill: style.fill || '#000',
|
||||||
|
fontSize: style.fontSize || 10,
|
||||||
|
cursor: cfg.style.cursor
|
||||||
|
},
|
||||||
|
name: 'text',
|
||||||
|
draggable: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrapperRect;
|
||||||
|
},
|
||||||
|
|
||||||
|
getAnchorPoints() {
|
||||||
|
return [
|
||||||
|
[0.5, 0.5],
|
||||||
|
[0, 0.5],
|
||||||
|
[1, 0]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}, 'rect');
|
@ -5,6 +5,7 @@ import G6 from '@antv/g6';
|
|||||||
import Pointer from "./RegisteredShape/pointer";
|
import Pointer from "./RegisteredShape/pointer";
|
||||||
import LinkListNode from "./RegisteredShape/linkListNode";
|
import LinkListNode from "./RegisteredShape/linkListNode";
|
||||||
import BinaryTreeNode from "./RegisteredShape/binaryTreeNode";
|
import BinaryTreeNode from "./RegisteredShape/binaryTreeNode";
|
||||||
|
import ForceNode from "./RegisteredShape/force";
|
||||||
import CLenQueuePointer from "./RegisteredShape/clenQueuePointer";
|
import CLenQueuePointer from "./RegisteredShape/clenQueuePointer";
|
||||||
import TwoCellNode from "./RegisteredShape/twoCellNode";
|
import TwoCellNode from "./RegisteredShape/twoCellNode";
|
||||||
import ArrayNode from "./RegisteredShape/arrayNode";
|
import ArrayNode from "./RegisteredShape/arrayNode";
|
||||||
@ -20,7 +21,7 @@ import { SVNode } from "./Model/SVNode";
|
|||||||
|
|
||||||
|
|
||||||
export interface StructV {
|
export interface StructV {
|
||||||
(DOMContainer: HTMLElement, engineOptions: EngineOptions): Engine;
|
(DOMContainer: HTMLElement, engineOptions: EngineOptions, isForce: boolean): Engine;
|
||||||
Group: typeof Group;
|
Group: typeof Group;
|
||||||
Bound: typeof Bound;
|
Bound: typeof Bound;
|
||||||
Vector: typeof Vector,
|
Vector: typeof Vector,
|
||||||
@ -42,8 +43,8 @@ export interface StructV {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const SV: StructV = function(DOMContainer: HTMLElement, engineOptions: EngineOptions = { }) {
|
export const SV: StructV = function(DOMContainer: HTMLElement, engineOptions: EngineOptions = { }, isForce: boolean) {
|
||||||
return new Engine(DOMContainer, engineOptions);
|
return new Engine(DOMContainer, engineOptions, isForce);
|
||||||
}
|
}
|
||||||
|
|
||||||
SV.Group = Group;
|
SV.Group = Group;
|
||||||
@ -61,6 +62,7 @@ SV.registeredShape = [
|
|||||||
Cursor,
|
Cursor,
|
||||||
ArrayNode,
|
ArrayNode,
|
||||||
CLenQueuePointer,
|
CLenQueuePointer,
|
||||||
|
ForceNode
|
||||||
];
|
];
|
||||||
|
|
||||||
SV.registerShape = Util.registerShape;
|
SV.registerShape = Util.registerShape;
|
||||||
|
@ -20,7 +20,7 @@ export class Renderer {
|
|||||||
private g6Instance: Graph; // g6 实例
|
private g6Instance: Graph; // g6 实例
|
||||||
private shadowG6Instance: Graph;
|
private shadowG6Instance: Graph;
|
||||||
|
|
||||||
constructor(engine: Engine, DOMContainer: HTMLElement, behaviorsModes: Modes) {
|
constructor(engine: Engine, DOMContainer: HTMLElement, behaviorsModes: Modes, isForce: boolean) {
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
|
|
||||||
const enable: boolean = this.engine.animationOptions.enable,
|
const enable: boolean = this.engine.animationOptions.enable,
|
||||||
@ -41,6 +41,19 @@ export class Renderer {
|
|||||||
container: DOMContainer.cloneNode() as HTMLElement
|
container: DOMContainer.cloneNode() as HTMLElement
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const forceOption = {
|
||||||
|
type: 'force',
|
||||||
|
linkDistance: 100, // 边长
|
||||||
|
preventOverlap: true, // boolean,防止节点重叠
|
||||||
|
// alphaDecay: 0, // 迭代阈值的衰减率。范围 [0, 1]。默认值0.028
|
||||||
|
workEnabled: true, // 启用以防布局计算时间过长阻塞页面交互
|
||||||
|
nodeStrength: -1, // 节点作用力,正数标识引力,负数表示斥力
|
||||||
|
nodeSpacing: (d) => { // 设置了防止重叠后,节点边缘间距的最小值
|
||||||
|
return 20;
|
||||||
|
},
|
||||||
|
center: [DOMContainer.offsetWidth / 2, DOMContainer.offsetHeight / 3],
|
||||||
|
};
|
||||||
|
const layout = isForce ? forceOption : null;
|
||||||
// 初始化g6实例
|
// 初始化g6实例
|
||||||
this.g6Instance = new Graph({
|
this.g6Instance = new Graph({
|
||||||
container: DOMContainer,
|
container: DOMContainer,
|
||||||
@ -52,10 +65,32 @@ export class Renderer {
|
|||||||
duration: duration,
|
duration: duration,
|
||||||
easing: timingFunction
|
easing: timingFunction
|
||||||
},
|
},
|
||||||
fitView: false,
|
|
||||||
modes: behaviorsModes,
|
modes: behaviorsModes,
|
||||||
plugins: [tooltip]
|
plugins: [tooltip],
|
||||||
|
layout,
|
||||||
});
|
});
|
||||||
|
/**
|
||||||
|
* 固定被拖拽节点
|
||||||
|
*/
|
||||||
|
function refreshDragedNodePosition(e) {
|
||||||
|
const model = e.item.get('model');
|
||||||
|
model.fx = e.x;
|
||||||
|
model.fy = e.y;
|
||||||
|
}
|
||||||
|
this.g6Instance.on('node:dragstart', (e) => {
|
||||||
|
this.g6Instance.layout();
|
||||||
|
refreshDragedNodePosition(e);
|
||||||
|
});
|
||||||
|
this.g6Instance.on('node:drag', (e) => {
|
||||||
|
refreshDragedNodePosition(e);
|
||||||
|
});
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.onresize = () => {
|
||||||
|
if (!this.g6Instance || this.g6Instance.get('destroyed')) return;
|
||||||
|
if (!DOMContainer || !DOMContainer.scrollWidth || !DOMContainer.scrollHeight) return;
|
||||||
|
this.g6Instance.changeSize(DOMContainer.scrollWidth, DOMContainer.scrollHeight);
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,12 +30,12 @@ export class ViewContainer {
|
|||||||
public clickSelectNode: SVNode; // 点击选中的节点
|
public clickSelectNode: SVNode; // 点击选中的节点
|
||||||
|
|
||||||
|
|
||||||
constructor(engine: Engine, DOMContainer: HTMLElement) {
|
constructor(engine: Engine, DOMContainer: HTMLElement, isForce: boolean) {
|
||||||
const behaviorsModes: Modes = InitG6Behaviors(engine, this);
|
const behaviorsModes: Modes = InitG6Behaviors(engine, this);
|
||||||
|
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
this.layoutProvider = new LayoutProvider(engine, this);
|
this.layoutProvider = new LayoutProvider(engine, this);
|
||||||
this.renderer = new Renderer(engine, DOMContainer, behaviorsModes);
|
this.renderer = new Renderer(engine, DOMContainer, behaviorsModes, isForce);
|
||||||
this.reconcile = new Reconcile(engine, this.renderer);
|
this.reconcile = new Reconcile(engine, this.renderer);
|
||||||
this.layoutGroupTable = new Map();
|
this.layoutGroupTable = new Map();
|
||||||
this.prevModelList = [];
|
this.prevModelList = [];
|
||||||
|
@ -18,7 +18,7 @@ export class Engine {
|
|||||||
public animationOptions: AnimationOptions;
|
public animationOptions: AnimationOptions;
|
||||||
public behaviorOptions: BehaviorOptions;
|
public behaviorOptions: BehaviorOptions;
|
||||||
|
|
||||||
constructor(DOMContainer: HTMLElement, engineOptions: EngineOptions) {
|
constructor(DOMContainer: HTMLElement, engineOptions: EngineOptions, isForce: boolean) {
|
||||||
this.engineOptions = Object.assign({}, engineOptions);
|
this.engineOptions = Object.assign({}, engineOptions);
|
||||||
|
|
||||||
this.viewOptions = Object.assign({
|
this.viewOptions = Object.assign({
|
||||||
@ -43,7 +43,7 @@ export class Engine {
|
|||||||
}, engineOptions.behavior);
|
}, engineOptions.behavior);
|
||||||
|
|
||||||
this.modelConstructor = new ModelConstructor(this);
|
this.modelConstructor = new ModelConstructor(this);
|
||||||
this.viewContainer = new ViewContainer(this, DOMContainer);
|
this.viewContainer = new ViewContainer(this, DOMContainer, isForce);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,13 +51,13 @@ export class Engine {
|
|||||||
* @param sources
|
* @param sources
|
||||||
* @param force
|
* @param force
|
||||||
*/
|
*/
|
||||||
public render(source: Sources, force: boolean = false) {
|
public render(source: Sources) {
|
||||||
if (source === undefined || source === null) {
|
if (source === undefined || source === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
``
|
``
|
||||||
let stringSource = JSON.stringify(source);
|
let stringSource = JSON.stringify(source);
|
||||||
if (force === false && this.prevStringSource === stringSource) {
|
if (this.prevStringSource === stringSource) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user