StructV2/src/View/reconcile.ts
2021-12-15 20:37:16 +08:00

411 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { EventBus } from "../Common/eventBus";
import { Util } from "../Common/util";
import { Engine } from "../engine";
import { SVLink } from "../Model/SVLink";
import { SVModel } from "../Model/SVModel";
import { SVNode } from "../Model/SVNode";
import { SVAddressLabel, SVMarker, SVNodeAppendage } from "../Model/SVNodeAppendage";
import { Animations } from "./animation";
import { Renderer } from "./renderer";
export interface DiffResult {
CONTINUOUS: SVModel[];
APPEND: SVModel[];
REMOVE: SVModel[];
FREED: SVNode[];
LEAKED: SVModel[];
UPDATE: SVModel[];
ACCUMULATE_LEAK: SVModel[];
}
export class Reconcile {
private engine: Engine;
private renderer: Renderer;
private isFirstPatch: boolean;
constructor(engine: Engine, renderer: Renderer) {
this.engine = engine;
this.renderer = renderer;
this.isFirstPatch = true;
}
/**
* 获取上一次渲染存在的这一次渲染也存在的models
* @param prevModelList
* @param modelList
*/
private getContinuousModels(prevModelList: SVModel[], modelList: SVModel[]): SVModel[] {
const continuousModels = modelList.filter(item => prevModelList.find(prevModel => item.id === prevModel.id));
return continuousModels;
}
/**
* 获取新增的节点,这些节点有可能来自泄漏区(上一步的情况)
* @param prevModelList
* @param modelList
* @param accumulateLeakModels
* @returns
*/
private getAppendModels(prevModelList: SVModel[], modelList: SVModel[], accumulateLeakModels: SVModel[]): SVModel[] {
const appendModels = modelList.filter(item => !prevModelList.find(model => model.id === item.id));
appendModels.forEach(item => {
let removeIndex = accumulateLeakModels.findIndex(leakModel => item.id === leakModel.id);
if (removeIndex > -1) {
accumulateLeakModels.splice(removeIndex, 1);
}
});
return appendModels;
}
/**
* 获取被泄露的节点
* @param prevModelList
* @param modelList
* @returns
*/
private getLeakModels(prevModelList: SVModel[], modelList: SVModel[]): SVModel[] {
const potentialLeakModels: SVModel[] = prevModelList.filter(item =>
!modelList.find(model => model.id === item.id) && !item.freed
);
const leakModels: SVModel[] = [];
potentialLeakModels.forEach(item => {
if (item instanceof SVNode) {
item.leaked = true;
leakModels.push(item);
item.appendages.forEach(appendage => {
appendage.leaked = true;
leakModels.push(appendage);
});
}
});
potentialLeakModels.forEach(item => {
if (item instanceof SVLink && item.node.leaked !== false && item.target.leaked !== false) {
item.leaked = true;
leakModels.push(item);
}
});
leakModels.forEach(item => {
// 不能用上次的G6item了不然布局的时候会没有动画
item.G6Item = null;
});
return leakModels;
}
/**
* 找出需要移除的节点
* @param prevModelList
* @param modelList
*/
private getRemoveModels(prevModelList: SVModel[], modelList: SVModel[]): SVModel[] {
let removedModels: SVModel[] = [];
for (let i = 0; i < prevModelList.length; i++) {
let prevModel = prevModelList[i],
target = modelList.find(item => item.id === prevModel.id);
if (target === undefined && !prevModel.leaked) {
removedModels.push(prevModel);
}
}
return removedModels;
}
/**
* 找出重新指向的外部指针
* @param prevModelList
* @param modelList
* @returns
*/
private getReTargetMarkers(prevModelList: SVModel[], modelList: SVModel[]): SVMarker[] {
const prevMarkers: SVMarker[] = prevModelList.filter(item => item instanceof SVMarker) as SVMarker[],
markers: SVMarker[] = modelList.filter(item => item instanceof SVMarker) as SVMarker[];
return markers.filter(item => prevMarkers.find(prevItem => {
return prevItem.id === item.id && prevItem.target.id !== item.target.id
}));
}
/**
* 找出前后 label 发生变化的 model
* @param prevModelList
* @param modelList
* @returns
*/
private getLabelChangeModels(prevModelList: SVModel[], modelList: SVModel[]): SVModel[] {
let labelChangeModels: SVModel[] = [];
modelList.forEach(item => {
const prevItem = prevModelList.find(prevItem => prevItem.id === item.id);
if (prevItem === undefined) {
return;
}
const prevLabel = prevItem.get('label'),
label = item.get('label');
if (prevLabel !== label) {
labelChangeModels.push(item);
}
});
return labelChangeModels;
}
/**
* 获取被 free 的节点
* @param prevModelList
* @param modelList
* @returns
*/
private getFreedModels(prevModelList: SVModel[], modelList: SVModel[]): SVNode[] {
const freedNodes = modelList.filter(item => item instanceof SVNode && item.freed) as SVNode[];
freedNodes.forEach(item => {
const prev = prevModelList.find(prevModel => item.id === prevModel.id);
if (prev) {
item.set('label', prev.get('label'));
}
});
return freedNodes;
}
// ------------------------------------------------------------------------------------------------
/**
* 处理不变的models
* @param continuousModels
*/
private handleContinuousModels(continuousModels: SVModel[]) {
for (let i = 0; i < continuousModels.length; i++) {
let model = continuousModels[i];
if (model instanceof SVNode) {
const group = model.G6Item.getContainer();
group.attr({ opacity: 1 });
}
}
}
/**
* 处理新增的 models
* @param appendData
*/
private handleAppendModels(appendModels: SVModel[]) {
let { duration, timingFunction } = this.engine.animationOptions;
appendModels.forEach(item => {
if (item instanceof SVNodeAppendage) {
// 先不显示泄漏区节点上面的地址文本
if (item instanceof SVAddressLabel) {
// 先将透明度改为0隐藏掉
const AddressLabelG6Group = item.G6Item.getContainer();
AddressLabelG6Group.attr({ opacity: 0 });
}
else {
Animations.FADE_IN(item.G6Item, {
duration,
timingFunction
});
}
}
else {
Animations.APPEND(item.G6Item, {
duration,
timingFunction
});
}
});
}
/**
* 处理被移除 models
* @param removeData
*/
private handleRemoveModels(removeModels: SVModel[]) {
let { duration, timingFunction } = this.engine.animationOptions;
removeModels.forEach(item => {
Animations.REMOVE(item.G6Item, {
duration,
timingFunction,
callback: () => {
this.renderer.removeModel(item);
}
});
});
}
/**
* 处理泄漏区 models
* @param leakModels
*/
private handleLeakModels(leakModels: SVModel[]) {
let { duration, timingFunction } = this.engine.animationOptions;
leakModels.forEach(item => {
if (item instanceof SVAddressLabel) {
Animations.FADE_IN(item.G6Item, {
duration,
timingFunction
});
}
});
EventBus.emit('onLeak', leakModels);
}
/**
* 处理已经堆积的泄漏区 models
* @param accumulateModels
*/
private handleAccumulateLeakModels(accumulateModels: SVModel[]) {
accumulateModels.forEach(item => {
if (item.generalStyle) {
item.set('style', { ...item.generalStyle });
}
});
}
/**
* 处理被释放的节点 models
* @param freedModes
*/
private handleFreedModels(freedModes: SVNode[]) {
const { duration, timingFunction } = this.engine.animationOptions,
alpha = 0.4;
freedModes.forEach(item => {
const nodeGroup = item.G6Item.getContainer();
item.set('style', { fill: '#ccc' });
nodeGroup.attr({ opacity: alpha });
if (item.marker) {
const markerGroup = item.marker.G6Item.getContainer();
item.marker.set('style', { fill: '#ccc' });
markerGroup.attr({ opacity: alpha + 0.5 });
}
item.freedLabel.G6Item.toFront();
Animations.FADE_IN(item.freedLabel.G6Item, { duration, timingFunction });
});
EventBus.emit('onFreed', freedModes);
}
/**
* 处理发生变化的 models
* @param models
*/
private handleChangeModels(models: SVModel[]) {
const changeHighlightColor: string = this.engine.viewOptions.updateHighlight;
if (!changeHighlightColor || typeof changeHighlightColor !== 'string') {
return;
}
models.forEach(item => {
if (item.generalStyle === undefined) {
item.generalStyle = Util.objectClone(item.G6ModelProps.style);
}
if (item instanceof SVLink) {
item.set('style', {
stroke: changeHighlightColor
});
}
else {
item.set('style', {
fill: changeHighlightColor
});
}
});
}
/**
* 进行diff
* @param prevLayoutGroupTable
* @param layoutGroupTable
* @param accumulateLeakModels
* @returns
*/
public diff(prevModelList: SVModel[], modelList: SVModel[], accumulateLeakModels: SVModel[]): DiffResult {
const continuousModels: SVModel[] = this.getContinuousModels(prevModelList, modelList);
const leakModels: SVModel[] = this.getLeakModels(prevModelList, modelList);
const appendModels: SVModel[] = this.getAppendModels(prevModelList, modelList, accumulateLeakModels);
const removeModels: SVModel[] = this.getRemoveModels(prevModelList, modelList);
const updateModels: SVModel[] = [
...this.getReTargetMarkers(prevModelList, modelList),
...this.getLabelChangeModels(prevModelList, modelList),
...appendModels,
...leakModels
];
const freedModels: SVNode[] = this.getFreedModels(prevModelList, modelList);
return {
CONTINUOUS: continuousModels,
APPEND: appendModels,
REMOVE: removeModels,
FREED: freedModels,
LEAKED: leakModels,
UPDATE: updateModels,
ACCUMULATE_LEAK: [...accumulateLeakModels]
};
}
/**
* 执行调和操作
* @param diffResult
* @param isFirstRender
*/
public patch(diffResult: DiffResult) {
const {
APPEND,
REMOVE,
FREED,
LEAKED,
UPDATE,
CONTINUOUS,
ACCUMULATE_LEAK
} = diffResult;
this.handleAccumulateLeakModels(ACCUMULATE_LEAK);
// 第一次渲染的时候不高亮变化的元素
if (this.isFirstPatch === false) {
this.handleChangeModels(UPDATE);
}
this.handleContinuousModels(CONTINUOUS);
this.handleFreedModels(FREED);
this.handleAppendModels(APPEND);
this.handleLeakModels(LEAKED);
this.handleRemoveModels(REMOVE);
if (this.isFirstPatch) {
this.isFirstPatch = false;
}
}
public destroy() { }
}