初步架构完成

This commit is contained in:
Phenom 2021-02-03 18:44:10 +08:00
parent 6c7b0d8a07
commit b886f33d9e
11 changed files with 406 additions and 52 deletions

View File

@ -36,6 +36,14 @@ export const Util = {
zrender.util.merge(origin, dest, true); zrender.util.merge(origin, dest, true);
}, },
/**
*
* @param object
*/
clone(object) {
return zrender.util.clone(object);
},
/** /**
* *
* @param list * @param list

View File

@ -44,16 +44,16 @@ export interface ElementStatus {
export class Element { export class Element {
id: any; id: number;
elementId: string = null; elementId: string = null;
elementLabel: string = null; elementLabel: string = null;
elementStatus: ElementStatus = null; elementStatus: ElementStatus = null;
zrShape: zrShape[] = []; shapes: Shape[] | Shape = null;
relativeLinks: Link[] = []; relativeLinks: Link[] = [];
relativePointers: Pointer[] = []; relativePointers: Pointer[] = [];
isDirty: boolean= false; isDirty: boolean = false;
// 给sourceElement的部分 // 给sourceElement的部分
[key: string]: any; [key: string]: any;

View File

@ -1,7 +1,7 @@
import { Engine } from "../engine"; import { Engine } from "../engine";
import { SourceElement, Sources } from "../sources"; import { SourceElement, Sources } from "../sources";
import { Shape, ShapeStatus } from "../View/shape"; import { Shape, ShapeStatus } from "../View/shape";
import { ZrShapeConstructor } from "../View/shapeScheduler"; import { ShapeScheduler, ZrShapeConstructor } from "../View/shapeScheduler";
import { Element } from "./element"; import { Element } from "./element";
@ -12,6 +12,8 @@ export type ElementConstructor = { new(elementLabel: string, sourceElement: Sour
export class ElementScheduler { export class ElementScheduler {
private engine: Engine; private engine: Engine;
private shapeScheduler: ShapeScheduler;
// 元素队列 // 元素队列
private elementList: Element[] = []; private elementList: Element[] = [];
// 元素容器即源数据经element包装后的结构 // 元素容器即源数据经element包装后的结构
@ -25,8 +27,9 @@ export class ElementScheduler {
} }
}; };
constructor(engine: Engine) { constructor(engine: Engine, shapeScheduler: ShapeScheduler) {
this.engine = engine; this.engine = engine;
this.shapeScheduler = shapeScheduler;
} }
/** /**
@ -103,24 +106,31 @@ export class ElementScheduler {
element.applyShapeOptions(shapeOptions); element.applyShapeOptions(shapeOptions);
if(Array.isArray(zrShapeConstructors)) { if(Array.isArray(zrShapeConstructors)) {
shapes = zrShapeConstructors.map((item, index) => new Shape(`${elementId}(${index})`, item, element)); shapes = zrShapeConstructors.map(function(item, index) {
return this.shapeScheduler.createShape(`${elementId}(${index})`, item, element)
});
this.shapeScheduler.packShapes(shapes);
} }
else { else {
shapes = new Shape(`elementId`, zrShapeConstructors, element); shapes = this.shapeScheduler.createShape(`elementId`, zrShapeConstructors, element);
} }
element.defineShape(shapes, element.elementStatus); element.shapes = shapes;
element.renderShape(shapes, element.elementStatus);
return element; return element;
} }
/**
* element对应的图形
*/
public updateShapes() { public updateShapes() {
for(let i = 0; i < this.elementList.length; i++) { for(let i = 0; i < this.elementList.length; i++) {
let ele = this.elementList[i]; let ele = this.elementList[i];
if(ele.isDirty) { if(ele.isDirty) {
ele.renderShape(); ele.renderShape(ele.zrShapes, ele.elementStatus);
} }
} }
} }
@ -150,6 +160,6 @@ export class ElementScheduler {
*/ */
public reset() { public reset() {
this.elementList.length = 0; this.elementList.length = 0;
this.elementContainer = {}; this.elementContainer = { };
} }
}; };

View File

@ -1,37 +1,36 @@
import { LinkTarget } from "../sources"; import { LinkTarget } from "../sources";
import { Shape } from "../View/shape";
import { zrShape } from "../View/shapeScheduler"; import { zrShape } from "../View/shapeScheduler";
import { Element, Style } from "./element"; import { Element, ElementStatus, Style } from "./element";
import { LabelStyle } from "./pointer"; import { LabelStyle } from "./pointer";
export interface LinkOptions { export interface LinkOptions {
style: Style; style: Style;
labelStyle: LabelStyle; labelStyle: LabelStyle;
}; };
export interface LinkStatus extends ElementStatus {
points: [number, number][];
};
export class Link { export class Link {
// 连线 id
id: string; id: string;
// 连线起始 element element: Element = null;
element: Element; target: Element = null;
// 连线目标 element linkLabel: string = null;
target: Element; linkStatus: LinkStatus;
// 连线类型名称
linkName: string; shapes: Shape[] | Shape = [];
// 连线图形实例 index: number = -1;
zrShapes: zrShape[]; sourceLinkTarget: LinkTarget = null;
// 连线序号
index: number;
// 连线在源数据的声明
sourceLinkTarget: LinkTarget;
isDirty: boolean = false; isDirty: boolean = false;
constructor() { constructor() { }
}
/** /**
* *
@ -40,4 +39,12 @@ export class Link {
setDirty(isDirty: boolean) { setDirty(isDirty: boolean) {
this.isDirty = isDirty; this.isDirty = isDirty;
} }
/**
* element映射的图形
* @param shapes
* @param elementStatus
* @override
*/
renderShape(shapes: Shape[] | Shape, elementStatus: ElementStatus) { }
}; };

View File

@ -1,19 +1,15 @@
import { ShapeStatus } from "../View/shape"; import { ShapeStatus } from "../View/shape";
import { ZrShapeConstructor } from "../View/shapeScheduler"; import { ZrShapeConstructor } from "../View/shapeScheduler";
import { Element, Style } from "./element"; import { Element } from "./element";
import { Link } from "./link"; import { Link } from "./link";
export interface LinkOptions {
style: Style;
};
export class LinkScheduler { export class LinkScheduler {
private links: Link[] = []; private links: Link[] = [];
private prevLinks: Link[] = []; private prevLinks: Link[] = [];
private linkMap: { private linkMap: {
[key: string]: { [key: string]: {
linkConstructor: { new(): Link }, linkConstructor: { new(): Link },
@ -30,11 +26,18 @@ export class LinkScheduler {
* *
* @param elementList * @param elementList
*/ */
constructLinks(elementList: Element[]) { public constructLinks(elementList: Element[]) {
} }
setLinkMap( /**
*
* @param linkLabel
* @param linkConstructor
* @param zrShapeConstructors
* @param shapeOptions
*/
public setLinkMap(
linkLabel: string, linkLabel: string,
linkConstructor: { new(): Link }, linkConstructor: { new(): Link },
zrShapeConstructors: ZrShapeConstructor[] | ZrShapeConstructor, zrShapeConstructors: ZrShapeConstructor[] | ZrShapeConstructor,
@ -47,7 +50,20 @@ export class LinkScheduler {
}; };
} }
reset() { /**
* element对应的图形
*/
public updateShapes() {
for(let i = 0; i < this.links.length; i++) {
let link = this.links[i];
if(link.isDirty) {
link.renderShape(link.shapes, link.linkStatus);
}
}
}
public reset() {
this.links.length = 0; this.links.length = 0;
} }
} }

View File

@ -1,5 +1,6 @@
import { Shape } from "../View/shape";
import { zrShape } from "../View/shapeScheduler"; import { zrShape } from "../View/shapeScheduler";
import { Style } from "./element"; import { ElementStatus, Style } from "./element";
export interface LabelStyle extends Style { export interface LabelStyle extends Style {
@ -19,7 +20,7 @@ export class Pointer {
// 指针 id // 指针 id
id: string; id: string;
// 指针图形实例 // 指针图形实例
zrShapes: zrShape[]; shape: Shape;
// 指针类型名称 // 指针类型名称
pointerLabel: string; pointerLabel: string;
// 被该指针合并的其他指针 // 被该指针合并的其他指针
@ -30,9 +31,9 @@ export class Pointer {
// 指针标签内容 // 指针标签内容
text: string; text: string;
// 指针标签图形实例 // 指针标签图形实例
textZrShapes: Text[]; textZrShapes: Shape[];
// 逗号图形实例 // 逗号图形实例
commaShapes: Text[]; commaShapes: Shape[];
// 目标 element // 目标 element
target: Element; target: Element;
@ -50,4 +51,12 @@ export class Pointer {
setDirty(isDirty: boolean) { setDirty(isDirty: boolean) {
this.isDirty = isDirty; this.isDirty = isDirty;
} }
/**
* element映射的图形
* @param shapes
* @param elementStatus
* @override
*/
renderShape(shapes: Shape[] | Shape, elementStatus: ElementStatus) { }
}; };

225
src/View/reconciler.ts Normal file
View File

@ -0,0 +1,225 @@
import { Util } from "../Common/util";
import { Style } from "../Model/element";
import { Shape } from "./shape";
import { ShapeScheduler } from "./shapeScheduler";
export enum patchType {
ADD,
REMOVE,
POSITION,
PATH,
ROTATION,
SIZE,
STYLE
}
export interface patchInfo {
type: number;
shape: Shape;
}
export class Reconciler {
private shapeScheduler: ShapeScheduler;
constructor(shapeScheduler: ShapeScheduler) {
this.shapeScheduler = shapeScheduler;
}
/**
*
* @param oldStyle
* @param newStyle
*/
reconcileStyle(oldStyle: Style, newStyle: Style): {name: string, old: any, new: any }[] {
let styleName: {name: string, old: any, new: any }[] = [];
Object.keys(newStyle).map(prop => {
if(newStyle[prop] !== oldStyle[prop]) {
styleName.push({
name: prop,
old: oldStyle[prop],
new: newStyle[prop]
});
}
});
return styleName;
}
/**
* differ
* @param shape
*/
reconcileShape(shape: Shape) {
let patchList: patchInfo[] = [];
if(shape.isDirty === false) return;
// 比较图形路径
if(JSON.stringify(shape.prevShapeStatus) !== JSON.stringify(shape.shapeStatus.points)) {
patchList.push({
type: patchType.PATH,
shape
});
}
// 比较图形坐标位置
if(shape.prevShapeStatus.x !== shape.shapeStatus.x || shape.prevShapeStatus.y !== shape.shapeStatus.y) {
patchList.push({
type: patchType.POSITION,
shape
});
}
// 比较旋转角度
if(shape.prevShapeStatus.rotation !== shape.shapeStatus.rotation) {
patchList.push({
type: patchType.ROTATION,
shape
});
}
// 比较尺寸
if(shape.prevShapeStatus.width !== shape.shapeStatus.width || shape.prevShapeStatus.height !== shape.shapeStatus.height) {
patchList.push({
type: patchType.SIZE,
shape
});
}
// 比较样式
let style = this.reconcileStyle(shape.prevShapeStatus.style, shape.shapeStatus.style);
if(style.length) {
patchList.push({
type: patchType.STYLE,
shape
});
}
// 对变化进行更新
this.patch(patchList);
}
/**
*
* @param container
* @param shapeList
*/
reconcileShapeList(container: { [key: string]: Shape[] }, shapeList: Shape[]) {
let patchList: patchInfo[] = [];
for(let i = 0; i < shapeList.length; i++) {
let shape = shapeList[i],
name = shape.type;
// 若发现存在于新视图模型而不存在于旧视图模型的图形,则该图形都标记为 ADD
if(container[name] === undefined) {
patchList.push({
type: patchType.ADD,
shape
});
}
else {
let oldShape = container[name].find(item => item.id === shape.id);
// 若旧图形列表存在对应的图形,进行 shape 间 differ
if(oldShape) {
oldShape.isReconcilerVisited = true;
this.reconcileShape(shape);
}
// 若发现存在于新视图模型而不存在于旧视图模型的图形,则该图形都标记为 ADD
else {
patchList.push({
type: patchType.ADD,
shape
});
}
}
}
// 在旧视图容器中寻找未访问过的图形,表明该图形该图形需要移除
Object.keys(container).forEach(key => {
container[key].forEach(shape => {
if(shape.isReconcilerVisited === false) {
patchList.push({
type: patchType.REMOVE,
shape,
});
}
shape.isReconcilerVisited = false;
});
});
this.patch(patchList);
}
/**
*
* @param patchList
*/
patch(patchList: patchInfo[]) {
let patch: patchInfo,
shape: Shape,
i;
for(i = 0; i < patchList.length; i++) {
patch = patchList[i];
shape = patch.shape;
switch(patch.type) {
case patchType.ADD: {
this.shapeScheduler.appendShape(shape);
this.shapeScheduler.emitAnimation(shape, 'append');
break;
}
case patchType.REMOVE: {
this.shapeScheduler.removeShape(shape);
this.shapeScheduler.emitAnimation(shape, 'remove');
break;
}
case patchType.PATH: {
shape.prevShapeStatus.points = shape.shapeStatus.points;
this.shapeScheduler.emitAnimation(shape, 'path');
}
case patchType.POSITION: {
shape.prevShapeStatus.x = shape.shapeStatus.x;
shape.prevShapeStatus.y = shape.shapeStatus.y;
this.shapeScheduler.emitAnimation(shape, 'position');
break;
}
case patchType.ROTATION: {
shape.prevShapeStatus.rotation = shape.shapeStatus.rotation;
this.shapeScheduler.emitAnimation(shape, 'rotation');
break;
}
case patchType.SIZE: {
shape.prevShapeStatus.width = shape.shapeStatus.width;
shape.prevShapeStatus.height = shape.shapeStatus.height;
this.shapeScheduler.emitAnimation(shape, 'size');
break;
}
case patchType.STYLE: {
shape.prevShapeStatus.style = Util.clone(shape.shapeStatus.style);
this.shapeScheduler.emitAnimation(shape, 'style');
break;
}
default: {
break;
}
}
}
}
}

View File

@ -0,0 +1,14 @@
export class Renderer {
constructor() {
}
applyAnimation() {
}
}

View File

@ -1,6 +1,6 @@
import { Util } from "../Common/util"; import { Util } from "../Common/util";
import { Element, Style } from "../Model/element"; import { Element, Style } from "../Model/element";
import { zrShape, ZrShapeConstructor } from "./shapeScheduler"; import { Group, zrShape, ZrShapeConstructor } from "./shapeScheduler";
export interface ShapeStatus { export interface ShapeStatus {
@ -11,6 +11,7 @@ export interface ShapeStatus {
width: number; width: number;
height: number; height: number;
content: string; content: string;
points: [number, number][];
style: Style; style: Style;
}; };
@ -22,7 +23,8 @@ export class Shape {
zrConstructor: ZrShapeConstructor = null; zrConstructor: ZrShapeConstructor = null;
zrShape: zrShape = null; zrShape: zrShape = null;
targetElement: Element = null; targetElement: Element = null;
parentGroup: Group = null;
shapeStatus: ShapeStatus = { shapeStatus: ShapeStatus = {
x: 0, y: 0, x: 0, y: 0,
rotation: 0, rotation: 0,
@ -30,6 +32,7 @@ export class Shape {
height: 0, height: 0,
zIndex: 1, zIndex: 1,
content: '', content: '',
points: [],
style: { style: {
fill: '#000', fill: '#000',
text: '', text: '',
@ -43,12 +46,17 @@ export class Shape {
} }
}; };
prevShapeStatus: ShapeStatus = null;
isDirty: boolean = false;
isReconcilerVisited: boolean = false;
constructor(id: string, zrConstructor: ZrShapeConstructor, element: Element) { constructor(id: string, zrConstructor: ZrShapeConstructor, element: Element) {
this.id = id; this.id = id;
this.type = Util.getClassName(zrConstructor); this.type = Util.getClassName(zrConstructor);
this.targetElement = element; this.targetElement = element;
this.zrConstructor = zrConstructor; this.zrConstructor = zrConstructor;
this.zrShape = new zrConstructor(); this.zrShape = new zrConstructor();
this.prevShapeStatus = Util.clone(this.shapeStatus);
} }
/** /**

View File

@ -6,6 +6,7 @@ import { Element } from "../Model/element";
export type zrShape = any; export type zrShape = any;
export type Group = any;
export type ZrShapeConstructor = { new(): zrShape }; export type ZrShapeConstructor = { new(): zrShape };
@ -14,6 +15,7 @@ export class ShapeScheduler {
private shapeList: Shape[] = []; private shapeList: Shape[] = [];
private shapeTable: { [key: string]: Shape[] } = {}; private shapeTable: { [key: string]: Shape[] } = {};
private parentGroupList: Group[] = [];
private appendList: Shape[] = []; private appendList: Shape[] = [];
private removeList: Shape[] = []; private removeList: Shape[] = [];
@ -27,7 +29,7 @@ export class ShapeScheduler {
* @param zrShapeConstructors * @param zrShapeConstructors
* @param element * @param element
*/ */
createShape(id: string, zrShapeConstructors: ZrShapeConstructor, element: Element): Shape { public createShape(id: string, zrShapeConstructors: ZrShapeConstructor, element: Element): Shape {
let shapeType = Util.getClassName(zrShapeConstructors), let shapeType = Util.getClassName(zrShapeConstructors),
shape = this.getReuseShape(id, shapeType); shape = this.getReuseShape(id, shapeType);
@ -38,11 +40,28 @@ export class ShapeScheduler {
return shape; return shape;
} }
/**
*
* @param shapes
*/
public packShapes(shapes: Shape[]){
let group: Group = new zrender.Group(),
shape: Shape;
for(let i = 0; i < shapes.length; i++) {
shape = shapes[i];
group.add(shape.zrShape);
shape.parentGroup = group;
}
this.parentGroupList.push(group);
}
/** /**
* *
* @param shape * @param shape
*/ */
private appendShape(shape: Shape) { public appendShape(shape: Shape) {
let shapeType = shape.type; let shapeType = shape.type;
if(this.shapeTable[shapeType] === undefined) { if(this.shapeTable[shapeType] === undefined) {
@ -58,7 +77,7 @@ export class ShapeScheduler {
* *
* @param shape * @param shape
*/ */
private removeShape(shape: Shape) { public removeShape(shape: Shape) {
let shapeType = shape.type; let shapeType = shape.type;
Util.removeFromList(this.shapeTable[shapeType], item => item.id === shape.id); Util.removeFromList(this.shapeTable[shapeType], item => item.id === shape.id);
@ -71,6 +90,15 @@ export class ShapeScheduler {
this.removeList.push(shape); this.removeList.push(shape);
} }
/**
*
* @param shape
* @param animationType
*/
public emitAnimation(shape: Shape, animationType: string) {
}
/** /**
* *
* @param id * @param id

View File

@ -37,15 +37,19 @@ export class Engine {
constructor(DOMContainer: HTMLElement, engineName: string) { constructor(DOMContainer: HTMLElement, engineName: string) {
this.engineName = engineName; this.engineName = engineName;
this.DOMContainer = DOMContainer; this.DOMContainer = DOMContainer;
this.elementScheduler = new ElementScheduler(this); this.shapeScheduler = new ShapeScheduler(this);
this.elementScheduler = new ElementScheduler(this, this.shapeScheduler);
this.linkScheduler = new LinkScheduler(); this.linkScheduler = new LinkScheduler();
this.pointerScheduler = new PointerScheduler(); this.pointerScheduler = new PointerScheduler();
this.shapeScheduler = new ShapeScheduler(this);
this.containerWidth = this.DOMContainer.offsetWidth; this.containerWidth = this.DOMContainer.offsetWidth;
this.containerHeight = this.DOMContainer.offsetHeight; this.containerHeight = this.DOMContainer.offsetHeight;
} }
/**
*
* @param sourceData
*/
public render(sourceData: Sources) { public render(sourceData: Sources) {
if(sourceData === undefined || sourceData === null) { if(sourceData === undefined || sourceData === null) {
@ -58,9 +62,7 @@ export class Engine {
this.sources = sourceData; this.sources = sourceData;
this.stringifySources = stringifySources; this.stringifySources = stringifySources;
this.elementScheduler.constructElements(sourceData); this.constructModel(sourceData);
this.linkScheduler.constructLinks([]);
this.pointerScheduler.constructPointers([]);
this.layoutFunction(this.elementScheduler.getElementContainer(), this.containerWidth, this.containerHeight); this.layoutFunction(this.elementScheduler.getElementContainer(), this.containerWidth, this.containerHeight);
} }
@ -80,7 +82,13 @@ export class Engine {
this.elementScheduler.setElementMap(elementLabel, elementConstructor, zrShapeConstructors, shapeOptions); this.elementScheduler.setElementMap(elementLabel, elementConstructor, zrShapeConstructors, shapeOptions);
} }
/**
* Link模型
* @param linkLabel
* @param linkConstructor
* @param zrShapeConstructors
* @param shapeOptions
*/
public applyLink( public applyLink(
linkLabel: string, linkConstructor: { new(): Link }, linkLabel: string, linkConstructor: { new(): Link },
zrShapeConstructors: ZrShapeConstructor[] | ZrShapeConstructor, zrShapeConstructors: ZrShapeConstructor[] | ZrShapeConstructor,
@ -89,6 +97,13 @@ export class Engine {
this.linkScheduler.setLinkMap(linkLabel, linkConstructor, zrShapeConstructors, shapeOptions); this.linkScheduler.setLinkMap(linkLabel, linkConstructor, zrShapeConstructors, shapeOptions);
} }
/**
* Pointer模型
* @param pointerLabel
* @param pointerConstructor
* @param zrShapeConstructors
* @param shapeOptions
*/
public applyPointer( public applyPointer(
pointerLabel: string, pointerConstructor: { new(): Pointer }, pointerLabel: string, pointerConstructor: { new(): Pointer },
zrShapeConstructors: ZrShapeConstructor[] | ZrShapeConstructor, zrShapeConstructors: ZrShapeConstructor[] | ZrShapeConstructor,
@ -105,6 +120,20 @@ export class Engine {
this.layoutFunction = layoutFunction; this.layoutFunction = layoutFunction;
} }
/**
*
* @param sourceData
*/
private constructModel(sourceData: Sources) {
this.elementScheduler.constructElements(sourceData);
this.linkScheduler.constructLinks([]);
this.pointerScheduler.constructPointers([]);
}
private updateShapes() {
}
/** /**
* *
*/ */