feat:添加多种数据结构同时渲染的支持
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
import { Bound, BoundingRect } from "../../Common/boundingRect";
|
||||
import { Engine } from "../../engine";
|
||||
import { ConstructList } from "../../Model/modelConstructor";
|
||||
import { Element, Model, Pointer } from "../../Model/modelData";
|
||||
import { AnimationOptions, InteractionOptions, LayoutOptions } from "../../options";
|
||||
import { Model, Pointer } from "../../Model/modelData";
|
||||
import { AnimationOptions, InteractionOptions, LayoutGroupOptions, LayoutOptions } from "../../options";
|
||||
import { SV } from "../../StructV";
|
||||
import { Animations } from "../animation";
|
||||
import { g6Behavior, Renderer } from "../renderer";
|
||||
|
||||
@@ -25,20 +24,45 @@ export class Container {
|
||||
this.DOMContainer = DOMContainer;
|
||||
this.animationsOptions = engine.animationOptions;
|
||||
this.interactionOptions = engine.interactionOptions;
|
||||
this.prevModelList = [];
|
||||
|
||||
const tooltip = new SV.G6.Tooltip({
|
||||
offsetX: 10,
|
||||
offsetY: 20,
|
||||
shouldBegin(event) {
|
||||
return event.item.getModel().SVModelType === 'element';
|
||||
},
|
||||
getContent(event) {
|
||||
const data = event.item.SVModel.data,
|
||||
wrapper = document.createElement('div');
|
||||
|
||||
wrapper.style.padding = '0 4px 0 4px';
|
||||
wrapper.innerHTML = `
|
||||
<h5>id: ${ event.item.SVModel.sourceId }</h5>
|
||||
<h5>data: ${ data? data: '' }</h5>
|
||||
`
|
||||
return wrapper;
|
||||
},
|
||||
itemTypes: ['node']
|
||||
});
|
||||
|
||||
this.renderer = new Renderer(engine, DOMContainer, {
|
||||
...g6Options,
|
||||
modes: {
|
||||
default: this.initBehaviors()
|
||||
}
|
||||
default: this.initBehaviors(this.engine.optionsTable)
|
||||
},
|
||||
plugins: [tooltip]
|
||||
});
|
||||
this.prevModelList = [];
|
||||
|
||||
this.afterInitRenderer();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化交互行为
|
||||
* @param optionsTable
|
||||
* @returns
|
||||
*/
|
||||
protected initBehaviors(): g6Behavior[] {
|
||||
protected initBehaviors(optionsTable: { [key: string]: LayoutGroupOptions }): g6Behavior[] {
|
||||
return ['drag-canvas', 'zoom-canvas'];
|
||||
}
|
||||
|
||||
@@ -119,6 +143,11 @@ export class Container {
|
||||
*/
|
||||
protected handleChangeModels(models: Model[]) { }
|
||||
|
||||
/**
|
||||
* 初始化渲染器之后的回调
|
||||
*/
|
||||
protected afterInitRenderer() { }
|
||||
|
||||
// ------------------------------------------ hook ---------------------------------------------
|
||||
|
||||
afterAppendModels(callback: (models: Model[]) => void) {
|
||||
@@ -135,11 +164,9 @@ export class Container {
|
||||
/**
|
||||
* 渲染函数
|
||||
* @param modelList
|
||||
* @param layoutFn
|
||||
*/
|
||||
public render(constructList: ConstructList, layoutFn: (elements: Element[], layoutOptions: LayoutOptions) => void) {
|
||||
const modelList: Model[] = [...constructList.element, ...constructList.link, ...constructList.pointer],
|
||||
appendModels: Model[] = this.getAppendModels(this.prevModelList, modelList),
|
||||
public render(modelList: Model[]) {
|
||||
const appendModels: Model[] = this.getAppendModels(this.prevModelList, modelList),
|
||||
removeModels: Model[] = this.getRemoveModels(this.prevModelList, modelList),
|
||||
changeModels: Model[] = [...appendModels, ...this.findReTargetPointer(modelList)];
|
||||
|
||||
|
||||
@@ -3,4 +3,9 @@ import { Container } from "./container";
|
||||
/**
|
||||
* 释放区可视化视图
|
||||
*/
|
||||
export class FreedContainer extends Container { };
|
||||
export class FreedContainer extends Container {
|
||||
|
||||
protected initBehaviors(): string[] {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
+124
-31
@@ -1,4 +1,5 @@
|
||||
import { Link, Model } from "../../Model/modelData";
|
||||
import { InteractionOptions, LayoutGroupOptions } from "../../options";
|
||||
import { Container } from "./container";
|
||||
|
||||
|
||||
@@ -8,50 +9,142 @@ import { Container } from "./container";
|
||||
*/
|
||||
export class MainContainer extends Container {
|
||||
|
||||
protected initBehaviors() {
|
||||
const interactionOptions = this.interactionOptions,
|
||||
dragNode: boolean | string[] = interactionOptions.dragNode,
|
||||
dragNodeFilter = node => {
|
||||
let model = node.item.getModel();
|
||||
protected initBehaviors(optionsTable: { [key: string]: LayoutGroupOptions }) {
|
||||
const dragNodeTable: { [key: string]: boolean | string[] } = { },
|
||||
selectNodeTable: { [key: string]: boolean | string[] } = { },
|
||||
interactionOptions: InteractionOptions = this.engine.interactionOptions,
|
||||
defaultModes = [];
|
||||
|
||||
if(node.item === null) {
|
||||
return false;
|
||||
}
|
||||
Object.keys(optionsTable).forEach(item => {
|
||||
dragNodeTable[item] = optionsTable[item].behavior.dragNode;
|
||||
selectNodeTable[item] = optionsTable[item].behavior.selectNode;
|
||||
});
|
||||
|
||||
if(model.modelType === 'pointer') {
|
||||
return false;
|
||||
}
|
||||
if(interactionOptions.drag) {
|
||||
defaultModes.push('drag-canvas');
|
||||
}
|
||||
|
||||
if(typeof dragNode === 'boolean') {
|
||||
return dragNode;
|
||||
}
|
||||
if(interactionOptions.zoom) {
|
||||
defaultModes.push('zoom-canvas');
|
||||
}
|
||||
|
||||
if(Array.isArray(dragNode) && dragNode.indexOf(model.modelName) > -1) {
|
||||
return true;
|
||||
}
|
||||
const dragNodeFilter = node => {
|
||||
let model = node.item.getModel();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const modeMap = {
|
||||
drag: 'drag-canvas',
|
||||
zoom: 'zoom-canvas',
|
||||
dragNode: {
|
||||
type: 'drag-node',
|
||||
shouldBegin: node => dragNodeFilter(node)
|
||||
if(node.item === null) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
defaultModes = [];
|
||||
|
||||
Object.keys(interactionOptions).forEach(item => {
|
||||
if(interactionOptions[item] && modeMap[item] !== undefined) {
|
||||
defaultModes.push(modeMap[item]);
|
||||
if(model.SVModelType === 'pointer') {
|
||||
return false;
|
||||
}
|
||||
|
||||
let dragNode = optionsTable[model.SVLayouter].behavior.dragNode;
|
||||
|
||||
if(typeof dragNode === 'boolean') {
|
||||
return dragNode;
|
||||
}
|
||||
|
||||
if(Array.isArray(dragNode) && dragNode.indexOf(model.SVModelName) > -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const selectNodeFilter = node => {
|
||||
let model = node.item.getModel();
|
||||
|
||||
if(node.item === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(model.SVModelType === 'pointer') {
|
||||
return false;
|
||||
}
|
||||
|
||||
let selectNode = optionsTable[model.SVLayouter].behavior.selectNode;
|
||||
|
||||
if(typeof selectNode === 'boolean') {
|
||||
return selectNode;
|
||||
}
|
||||
|
||||
if(Array.isArray(selectNode) && selectNode.indexOf(model.SVModelName) > -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
defaultModes.push({
|
||||
type: 'drag-node',
|
||||
shouldBegin: dragNodeFilter
|
||||
});
|
||||
|
||||
defaultModes.push({
|
||||
type: 'click-select',
|
||||
shouldBegin: selectNodeFilter
|
||||
});
|
||||
|
||||
return defaultModes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在初始化渲染器之后,修正节点拖拽时,外部指针没有跟着动的问题
|
||||
* @param dragNodeTable
|
||||
*/
|
||||
protected afterInitRenderer() {
|
||||
let g6Instance = this.getG6Instance(),
|
||||
pointerY = null,
|
||||
dragStartX = null,
|
||||
dragStartY = null;
|
||||
|
||||
g6Instance.on('node:dragstart', ev => {
|
||||
const model = ev.item.getModel(),
|
||||
dragNode = this.engine.optionsTable[model.SVLayouter].behavior.dragNode;
|
||||
|
||||
if(dragNode === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(Array.isArray(dragNode) && dragNode.find(item => item === model.SVModelName) === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
pointer = g6Instance.findById(model.externalPointerId);
|
||||
|
||||
if(pointer) {
|
||||
pointerX = pointer.getModel().x,
|
||||
pointerY = pointer.getModel().y;
|
||||
dragStartX = ev.canvasX;
|
||||
dragStartY = ev.canvasY;
|
||||
}
|
||||
});
|
||||
|
||||
g6Instance.on('node:dragend', ev => {
|
||||
pointer = null;
|
||||
pointerX = null,
|
||||
pointerY = null,
|
||||
dragStartX = null,
|
||||
dragStartY = null;
|
||||
});
|
||||
|
||||
g6Instance.on('node:drag', ev => {
|
||||
if(!pointer) {
|
||||
return;
|
||||
}
|
||||
|
||||
let dx = ev.canvasX - dragStartX,
|
||||
dy = ev.canvasY - dragStartY,
|
||||
zoom = g6Instance.getZoom();
|
||||
|
||||
pointer.updatePosition({
|
||||
x: pointerX + dx / zoom,
|
||||
y: pointerY + dy / zoom
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected handleChangeModels(models: Model[]) {
|
||||
const changeHighlightColor: string = this.interactionOptions.changeHighlight;
|
||||
|
||||
|
||||
+112
-47
@@ -1,16 +1,19 @@
|
||||
import { Bound, BoundingRect } from '../Common/boundingRect';
|
||||
import { Group } from '../Common/group';
|
||||
import { Engine } from '../engine';
|
||||
import { ConstructList } from '../Model/modelConstructor';
|
||||
import { LayoutGroupTable } from '../Model/modelConstructor';
|
||||
import { Element, Model, Pointer } from '../Model/modelData';
|
||||
import { LayoutOptions, PointerOption } from '../options';
|
||||
import { LayoutOptions, PointerOption, ViewOptions } from '../options';
|
||||
import { Container } from './container/container';
|
||||
|
||||
|
||||
export class Layouter {
|
||||
private engine: Engine;
|
||||
private engine: Engine;
|
||||
private viewOptions: ViewOptions;
|
||||
|
||||
constructor(engine: Engine) {
|
||||
this.engine = engine;
|
||||
this.viewOptions = this.engine.viewOptions;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +22,7 @@ export class Layouter {
|
||||
* @param elements
|
||||
* @param pointers
|
||||
*/
|
||||
private initLayoutValue(elements: Element[], pointers: Pointer[]) {
|
||||
private initLayoutValue(elements: Element[], pointers: Pointer[]) {
|
||||
[...elements, ...pointers].forEach(item => {
|
||||
item.set('rotation', item.get('rotation'));
|
||||
item.set({ x: 0, y: 0 });
|
||||
@@ -29,10 +32,11 @@ export class Layouter {
|
||||
/**
|
||||
* 布局外部指针
|
||||
* @param pointer
|
||||
* @param pointerOptions
|
||||
*/
|
||||
private layoutPointer(pointers: Pointer[]) {
|
||||
private layoutPointer(pointers: Pointer[], pointerOptions: { [key: string]: PointerOption }) {
|
||||
pointers.forEach(item => {
|
||||
const options: PointerOption = this.engine.pointerOptions[item.getType()],
|
||||
const options: PointerOption = pointerOptions[item.getType()],
|
||||
offset = options.offset || 8,
|
||||
anchor = options.anchor || 0;
|
||||
|
||||
@@ -52,57 +56,118 @@ export class Layouter {
|
||||
* @param container
|
||||
* @param models
|
||||
*/
|
||||
private fitCenter(container: Container, models: Model[]) {
|
||||
if(models.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const viewBound: BoundingRect = models.map(item => item.getBound()).reduce((prev, cur) => Bound.union(prev, cur));
|
||||
|
||||
private fitCenter(container: Container, group: Group) {
|
||||
let width = container.getG6Instance().getWidth(),
|
||||
height = container.getG6Instance().getHeight(),
|
||||
viewBound: BoundingRect = group.getBound(),
|
||||
centerX = width / 2, centerY = height / 2,
|
||||
boundCenterX = viewBound.x + viewBound.width / 2,
|
||||
boundCenterY = viewBound.y + viewBound.height / 2,
|
||||
dx = centerX - boundCenterX,
|
||||
dy = centerY - boundCenterY;
|
||||
|
||||
models.forEach(item => {
|
||||
item.set({
|
||||
x: item.get('x') + dx,
|
||||
y: item.get('y') + dy
|
||||
group.translate(dx, dy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对每个组内部的model进行布局
|
||||
* @param layoutGroupTable
|
||||
*/
|
||||
private layoutModels(layoutGroupTable: LayoutGroupTable): Group[] {
|
||||
const modelGroupList: Group[] = [];
|
||||
|
||||
layoutGroupTable.forEach((group, groupName) => {
|
||||
const options: LayoutOptions = group.options.layout,
|
||||
modelList: Model[] = group.modelList,
|
||||
modelGroup: Group = new Group();
|
||||
|
||||
modelList.forEach(item => {
|
||||
modelGroup.add(item);
|
||||
});
|
||||
|
||||
|
||||
this.initLayoutValue(group.element, group.pointer); // 初始化布局参数
|
||||
group.layouter.layout(group.element, options); // 布局节点
|
||||
this.layoutPointer(group.pointer, group.options.pointer); // 布局外部指针
|
||||
|
||||
modelGroupList.push(modelGroup);
|
||||
});
|
||||
|
||||
return modelGroupList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对所有组进行相互布局
|
||||
* @param container
|
||||
* @param modelGroupTable
|
||||
*/
|
||||
private layoutGroups(container: Container, modelGroupList: Group[]): Group {
|
||||
let wrapperGroup: Group = new Group(),
|
||||
group: Group,
|
||||
prevBound: BoundingRect,
|
||||
bound: BoundingRect,
|
||||
boundList: BoundingRect[] = [],
|
||||
maxHeight: number = -Infinity,
|
||||
dx = 0, dy = 0;
|
||||
|
||||
// 左往右布局
|
||||
for(let i = 0; i < modelGroupList.length; i++) {
|
||||
group = modelGroupList[i],
|
||||
bound = group.getPaddingBound(this.viewOptions.groupPadding);
|
||||
|
||||
if(prevBound) {
|
||||
dx = prevBound.x + prevBound.width - bound.x;
|
||||
}
|
||||
else {
|
||||
dx = bound.x;
|
||||
}
|
||||
|
||||
if(bound.height > maxHeight) {
|
||||
maxHeight = bound.height;
|
||||
}
|
||||
|
||||
group.translate(dx, 0);
|
||||
Bound.translate(bound, dx, 0);
|
||||
boundList.push(bound);
|
||||
wrapperGroup.add(group);
|
||||
prevBound = bound;
|
||||
}
|
||||
|
||||
// 居中对齐布局
|
||||
for(let i = 0; i < modelGroupList.length; i++) {
|
||||
group = modelGroupList[i];
|
||||
bound = boundList[i];
|
||||
|
||||
dy = maxHeight / 2 - bound.height / 2;
|
||||
group.translate(0, dy);
|
||||
Bound.translate(bound, 0, dy);
|
||||
}
|
||||
|
||||
return wrapperGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* 布局
|
||||
* @param container
|
||||
* @param layoutGroupTable
|
||||
*/
|
||||
public layoutAll(container: Container, layoutGroupTable: LayoutGroupTable) {
|
||||
layoutGroupTable.forEach(item => {
|
||||
item.modelList.forEach(model => {
|
||||
model.G6Item = model.shadowG6Item;
|
||||
});
|
||||
});
|
||||
|
||||
const modelGroupList: Group[] = this.layoutModels(layoutGroupTable),
|
||||
wrapperGroup: Group = this.layoutGroups(container, modelGroupList);
|
||||
|
||||
this.fitCenter(container, wrapperGroup);
|
||||
|
||||
layoutGroupTable.forEach(item => {
|
||||
item.modelList.forEach(model => {
|
||||
model.G6Item = model.renderG6Item;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 进行布局
|
||||
* @param container
|
||||
* @param constructList
|
||||
* @param layoutFn
|
||||
*/
|
||||
public layout(container: Container, constructList: ConstructList, layoutFn: (elements: Element[], layoutOptions: LayoutOptions) => void) {
|
||||
const options: LayoutOptions = this.engine.layoutOptions,
|
||||
modelList: Model[] = [...constructList.element, ...constructList.pointer, ...constructList.link];
|
||||
|
||||
// 首先初始化所有节点的坐标为0,且设定旋转
|
||||
modelList.forEach(item => {
|
||||
item.G6Item = item.shadowG6Item;
|
||||
});
|
||||
|
||||
// 初始化布局参数
|
||||
this.initLayoutValue(constructList.element, constructList.pointer);
|
||||
// 布局节点
|
||||
layoutFn(constructList.element, options);
|
||||
// 布局外部指针
|
||||
this.layoutPointer(constructList.pointer);
|
||||
|
||||
// 将视图调整到画布中心
|
||||
options.fitCenter && this.fitCenter(container, modelList);
|
||||
|
||||
modelList.forEach(item => {
|
||||
item.G6Item = item.renderG6Item;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -86,13 +86,14 @@ export class Renderer {
|
||||
this.g6Instance.changeData(renderData);
|
||||
}
|
||||
|
||||
if(this.engine.layoutOptions.fitView) {
|
||||
if(this.engine.viewOptions.fitView) {
|
||||
this.g6Instance.fitView();
|
||||
}
|
||||
|
||||
modelList.forEach(item => {
|
||||
item.renderG6Item = this.g6Instance.findById(item.id);
|
||||
item.G6Item = item.renderG6Item;
|
||||
item.renderG6Item.SVModel = item;
|
||||
});
|
||||
|
||||
// 把所有连线置顶
|
||||
|
||||
+119
-75
@@ -1,13 +1,14 @@
|
||||
import { Engine } from "../engine";
|
||||
import { Element, Link } from "../Model/modelData";
|
||||
import { EngineInitOptions, LayoutOptions } from "../options";
|
||||
import { Element, Link, Model } from "../Model/modelData";
|
||||
import { EngineOptions } from "../options";
|
||||
import { Container } from "./container/container";
|
||||
import { SV } from '../StructV';
|
||||
import { ConstructList } from "../Model/modelConstructor";
|
||||
import { MainContainer } from "./container/main";
|
||||
import { FreedContainer } from "./container/freed";
|
||||
import { LeakContainer } from "./container/leak";
|
||||
import { Layouter } from "./layouter";
|
||||
import { LayoutGroup, LayoutGroupTable } from "../Model/modelConstructor";
|
||||
import { Util } from "../Common/util";
|
||||
|
||||
|
||||
export class ViewManager {
|
||||
@@ -17,9 +18,7 @@ export class ViewManager {
|
||||
private freedContainer: Container;
|
||||
private leakContainer: Container;
|
||||
|
||||
private prevConstructList: ConstructList = { element:[], pointer: [], link: [] };
|
||||
private freedConstructList: ConstructList = { element:[], pointer: [], link: [] };
|
||||
private leakConstructList: ConstructList = { element:[], pointer: [], link: [] };
|
||||
private prevLayoutGroupTable: LayoutGroupTable;
|
||||
|
||||
private shadowG6Instance;
|
||||
|
||||
@@ -27,8 +26,9 @@ export class ViewManager {
|
||||
this.engine = engine;
|
||||
this.layouter = new Layouter(engine);
|
||||
this.mainContainer = new MainContainer(engine, DOMContainer);
|
||||
this.prevLayoutGroupTable = null;
|
||||
|
||||
const options: EngineInitOptions = this.engine.initOptions;
|
||||
const options: EngineOptions = this.engine.engineOptions;
|
||||
|
||||
if(options.freedContainer) {
|
||||
this.freedContainer = new FreedContainer(engine, options.freedContainer, { fitCenter: true });
|
||||
@@ -47,38 +47,49 @@ export class ViewManager {
|
||||
* 对每一个 model 在离屏 Canvas 上构建 G6 item,用作布局
|
||||
* @param constructList
|
||||
*/
|
||||
private build(constructList: ConstructList) {
|
||||
constructList.element.map(item => item.cloneProps()).forEach(item => this.shadowG6Instance.addItem('node', item));
|
||||
constructList.pointer.map(item => item.cloneProps()).forEach(item => this.shadowG6Instance.addItem('node', item));
|
||||
constructList.link.map(item => item.cloneProps()).forEach(item => this.shadowG6Instance.addItem('edge', item));
|
||||
|
||||
constructList.element.forEach(item => {
|
||||
item.shadowG6Item = this.shadowG6Instance.findById(item.id);
|
||||
});
|
||||
constructList.pointer.forEach(item => {
|
||||
item.shadowG6Item = this.shadowG6Instance.findById(item.id);
|
||||
});
|
||||
constructList.link.forEach(item => {
|
||||
item.shadowG6Item = this.shadowG6Instance.findById(item.id);
|
||||
private build(layoutGroupTable: LayoutGroupTable) {
|
||||
layoutGroupTable.forEach(group => {
|
||||
group.element.map(item => item.cloneProps()).forEach(item => this.shadowG6Instance.addItem('node', item));
|
||||
group.pointer.map(item => item.cloneProps()).forEach(item => this.shadowG6Instance.addItem('node', item));
|
||||
group.link.map(item => item.cloneProps()).forEach(item => this.shadowG6Instance.addItem('edge', item));
|
||||
|
||||
group.element.forEach(item => {
|
||||
item.shadowG6Item = this.shadowG6Instance.findById(item.id);
|
||||
});
|
||||
group.pointer.forEach(item => {
|
||||
item.shadowG6Item = this.shadowG6Instance.findById(item.id);
|
||||
});
|
||||
group.link.forEach(item => {
|
||||
item.shadowG6Item = this.shadowG6Instance.findById(item.id);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取被 free 的节点
|
||||
* @param constructList
|
||||
* @param layoutGroupTable
|
||||
* @returns
|
||||
*/
|
||||
private getFreedConstructList(constructList: ConstructList): ConstructList {
|
||||
const freedList: ConstructList = {
|
||||
element: constructList.element.filter(item => item.free),
|
||||
pointer: [],
|
||||
link: []
|
||||
};
|
||||
private getFreedConstructList(layoutGroupTable: LayoutGroupTable): Model[] {
|
||||
let freedList: Model[] = [],
|
||||
freedGroup = null,
|
||||
freedGroupName = null;
|
||||
|
||||
freedList.element.forEach(fItem => {
|
||||
constructList.element.splice(constructList.element.findIndex(item => item.id === fItem.id), 1);
|
||||
constructList.link.splice(constructList.link.findIndex(item => item.element.id === fItem.id || item.target.id === fItem.id));
|
||||
constructList.pointer.splice(constructList.pointer.findIndex(item => item.target.id === fItem.id));
|
||||
for(let group in layoutGroupTable) {
|
||||
let freedElements: Model[] = layoutGroupTable[group].element.filter(item => item.freed);
|
||||
|
||||
if(freedElements.length) {
|
||||
freedGroupName = group;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
freedGroup = layoutGroupTable[freedGroupName];
|
||||
|
||||
freedList.forEach(fItem => {
|
||||
freedGroup.element.splice(freedGroup.element.findIndex(item => item.id === fItem.id), 1);
|
||||
freedGroup.link.splice(freedGroup.link.findIndex(item => item.element.id === fItem.id || item.target.id === fItem.id));
|
||||
freedGroup.pointer.splice(freedGroup.pointer.findIndex(item => item.target.id === fItem.id));
|
||||
});
|
||||
|
||||
return freedList;
|
||||
@@ -90,47 +101,63 @@ export class ViewManager {
|
||||
* @param prevConstructList
|
||||
* @returns
|
||||
*/
|
||||
private getLeakConstructList(prevConstructList: ConstructList, constructList: ConstructList): ConstructList {
|
||||
const elements: Element[] = prevConstructList.element.filter(item => !constructList.element.find(n => n.id === item.id)),
|
||||
links: Link[] = prevConstructList.link.filter(item => !constructList.link.find(n => n.id === item.id)),
|
||||
elementIds: string[] = elements.map(item => item.id);
|
||||
private getLeakConstructList(prevLayoutGroupTable: LayoutGroupTable, layoutGroupTable: LayoutGroupTable): LayoutGroupTable {
|
||||
const leakLayoutGroupTable = new Map<string, LayoutGroup>();
|
||||
|
||||
elements.forEach(item => {
|
||||
item.set('style', {
|
||||
fill: '#ccc'
|
||||
prevLayoutGroupTable.forEach((item, groupName) => {
|
||||
let prevGroup = item,
|
||||
curGroup = layoutGroupTable.get(groupName),
|
||||
elements: Element[] = [],
|
||||
links: Link[] = [],
|
||||
elementIds: string[] = [];
|
||||
|
||||
if(curGroup) {
|
||||
elements = prevGroup.element.filter(item => !curGroup.element.find(n => n.id === item.id)).filter(item => item.freed === false),
|
||||
links = prevGroup.link.filter(item => !curGroup.link.find(n => n.id === item.id)),
|
||||
elementIds = elements.map(item => item.id);
|
||||
}
|
||||
|
||||
elements.forEach(item => {
|
||||
item.set('style', {
|
||||
fill: '#ccc'
|
||||
});
|
||||
});
|
||||
|
||||
for(let i = 0; i < links.length; i++) {
|
||||
let sourceId = links[i].element.id,
|
||||
targetId = links[i].target.id;
|
||||
|
||||
links[i].set('style', {
|
||||
stroke: '#333'
|
||||
});
|
||||
|
||||
if(elementIds.find(item => item === sourceId) === undefined || elementIds.find(item => item === targetId) === undefined) {
|
||||
links.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
leakLayoutGroupTable.set(groupName, {
|
||||
element: elements,
|
||||
link: links,
|
||||
pointer: [],
|
||||
layouter: prevGroup.layouter,
|
||||
options: prevGroup.options,
|
||||
modelList: [...elements, ...links]
|
||||
});
|
||||
});
|
||||
|
||||
for(let i = 0; i < links.length; i++) {
|
||||
let sourceId = links[i].element.id,
|
||||
targetId = links[i].target.id;
|
||||
|
||||
links[i].set('style', {
|
||||
stroke: '#333'
|
||||
});
|
||||
|
||||
if(elementIds.find(item => item === sourceId) === undefined || elementIds.find(item => item === targetId) === undefined) {
|
||||
links.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
element: elements,
|
||||
link: links,
|
||||
pointer: []
|
||||
};
|
||||
return leakLayoutGroupTable;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 对主视图进行重新布局
|
||||
* @param constructList
|
||||
* @param layoutFn
|
||||
* @param layoutGroupTable
|
||||
*/
|
||||
reLayout(constructList: ConstructList, layoutFn: (elements: Element[], layoutOptions: LayoutOptions) => void) {
|
||||
this.layouter.layout(this.mainContainer, constructList, layoutFn);
|
||||
reLayout(layoutGroupTable: LayoutGroupTable) {
|
||||
this.layouter.layoutAll(this.mainContainer, layoutGroupTable);
|
||||
}
|
||||
|
||||
|
||||
@@ -150,11 +177,23 @@ export class ViewManager {
|
||||
|
||||
/**
|
||||
* 重新调整容器尺寸
|
||||
* @param containerName
|
||||
* @param width
|
||||
* @param height
|
||||
*/
|
||||
resize(width: number, height: number) {
|
||||
this.mainContainer.getG6Instance().changeSize(width, height);
|
||||
resize(containerName: string, width: number, height: number) {
|
||||
if(containerName === 'main') {
|
||||
this.mainContainer.getG6Instance().changeSize(width, height);
|
||||
}
|
||||
|
||||
if(containerName === 'freed') {
|
||||
this.freedContainer.getG6Instance().changeSize(width, height);
|
||||
}
|
||||
|
||||
if(containerName === 'leak') {
|
||||
this.leakContainer.getG6Instance().changeSize(width, height);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,31 +201,36 @@ export class ViewManager {
|
||||
* @param models
|
||||
* @param layoutFn
|
||||
*/
|
||||
renderAll(constructList: ConstructList, layoutFn: (elements: Element[], layoutOptions: LayoutOptions) => void) {
|
||||
renderAll(layoutGroupTable: LayoutGroupTable) {
|
||||
this.shadowG6Instance.clear();
|
||||
|
||||
this.build(constructList);
|
||||
this.build(layoutGroupTable);
|
||||
|
||||
this.freedConstructList = this.getFreedConstructList(constructList);
|
||||
this.leakConstructList = this.getLeakConstructList(this.prevConstructList, constructList);
|
||||
|
||||
this.build(this.leakConstructList);
|
||||
let freedList = this.getFreedConstructList(layoutGroupTable),
|
||||
leakLayoutGroupTable = null;
|
||||
|
||||
if(this.leakContainer && this.prevLayoutGroupTable) {
|
||||
leakLayoutGroupTable = this.getLeakConstructList(this.prevLayoutGroupTable, layoutGroupTable);
|
||||
this.build(leakLayoutGroupTable);
|
||||
}
|
||||
|
||||
if(this.freedContainer) {
|
||||
this.freedContainer.render(this.freedConstructList, layoutFn);
|
||||
this.freedContainer.render(freedList);
|
||||
}
|
||||
|
||||
// 进行布局(设置model的x,y)
|
||||
this.layouter.layout(this.mainContainer, constructList, layoutFn);
|
||||
this.mainContainer.render(constructList, layoutFn);
|
||||
this.layouter.layoutAll(this.mainContainer, layoutGroupTable);
|
||||
|
||||
if(this.leakContainer) {
|
||||
const modelList: Model[] = Util.convertGroupTable2ModelList(layoutGroupTable);
|
||||
this.mainContainer.render(modelList);
|
||||
|
||||
if(this.leakContainer && this.prevLayoutGroupTable) {
|
||||
this.mainContainer.afterRemoveModels(() => {
|
||||
this.leakContainer.render(this.leakConstructList, layoutFn);
|
||||
this.leakContainer.render(Util.convertGroupTable2ModelList(leakLayoutGroupTable));
|
||||
});
|
||||
}
|
||||
|
||||
this.prevConstructList = constructList;
|
||||
this.prevLayoutGroupTable = layoutGroupTable;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user