import { watch } from "vue"; import { Attrib, ShapeType } from "../type"; import { Layer } from "konva/lib/Layer"; import { Entity, EntityProps, EntityType } from "./entity"; import { Stage } from "konva/lib/Stage"; import { IncEntitysFactory, incEntitysFactoryGenerate, } from "../shared/entity-utils"; import { Shape } from "konva/lib/Shape"; import { Group } from "konva/lib/Group"; import { getAbsoluteTransform } from "../shared"; import mitt, { Emitter } from "mitt"; export type ContainerProps< T extends string = string, R extends Attrib = Attrib, S extends ShapeType = ShapeType, C extends EntityType = EntityType, TYPES extends { [key in T]: C } = { [key in T]: C }, DATA extends { [key in T]?: R | R[] } = { [key in T]?: R | R[] } > = { types: TYPES; data?: DATA; dom: HTMLDivElement; } & Omit, "attrib">; export class Container< T extends string = string, R extends Attrib = Attrib, S extends ShapeType = ShapeType, C extends EntityType = EntityType, TYPES extends { [key in T]: C } = { [key in T]: C }, DATA extends { [key in T]?: R | R[] } = { [key in T]?: R | R[] }, ENTRYS extends { [key in T]: InstanceType[] } = { [key in T]: InstanceType[]; } > extends Entity { bus: Emitter<{ active: Entity; dataChange: void; dataChangeBefore: void; dataChangeAfter: void; }>; tempLayer: Layer; config: ContainerProps; entrys = {} as ENTRYS; stage: Stage; incFactorys = {} as { [key in T]: IncEntitysFactory>; }; constructor(config: ContainerProps) { for (const key in config.types) { config.data[key as any] = config.data[key] || []; } for (const key in config.data) { if (!(key in config.types)) { delete config.data[key]; } } super({ name: "container", attrib: { id: "0", data: config.data }, ...config, }); this.config = config; this.container = this; const { dom } = this.config; this.stage = new Stage({ container: dom, width: dom.offsetWidth, height: dom.offsetHeight, }); this.bus = mitt(); // this.tempLayer = new Layer(); // this.stage.add(this.tempLayer); } initShape() { const viewLayer = new Layer(); return viewLayer; } init() { this.initIncFactory(); super.init(); this.mount(this.stage); this.mounted(); } initIncFactory() { const types = this.config.types as any; let active: Entity | null = null; for (const key in types) { this.incFactorys[key] = incEntitysFactoryGenerate( types[key], this, (instance) => { if (!this.entrys[key]) { this.entrys[key] = []; } this.entrys[key].push(instance); }, (instance) => { if (!instance.actShape?.active) return; const entity = instance as Entity; entity.enableActive((actived) => { if (actived) { if (active !== entity) { this.bus.emit("active", entity); active = entity; } } else { if (active === entity) { this.bus.emit("active", null); active = null; } } }); const destory = entity.destory.bind(entity); entity.destory = () => { if (active === entity) { this.bus.emit("active", null); active = null; } return destory(); }; } ); } } diffRedraw() { const data = this.attrib.data as any; const result = {} as { [key in T]: ReturnType>>; }; for (const key in data) { if (key in this.incFactorys) { result[key] = this.incFactorys[key](data[key]); } } return result; } initReactive() { return watch( () => { const result = {} as { [key in T]: string | string[] }; for (const key in this.attrib.data as any) { const child = this.attrib.data[key]; result[key] = Array.isArray(child) ? child.map(({ id }) => id) : child.id; } return result; }, this.diffRedraw.bind(this), { immediate: true, flush: "sync" } ); } // 临时绘制 tempDraw(...shapes: Entity[]) { const tels = shapes.map((shape) => shape.teleport); shapes.forEach((shape) => { shape.mount(this.tempLayer); }); return () => { shapes.forEach((shape, ndx) => { shape.mount(tels[ndx]); }); }; } getPixelFromStage(pos: number[]) { const tf = getAbsoluteTransform(this.stage, true); const { x, y } = tf.point({ x: pos[0], y: pos[1] }); return [x, y]; } getRealFromStage(pos: number[]) { const tf = getAbsoluteTransform(this.stage, true).invert(); const { x, y } = tf.point({ x: pos[0], y: pos[1] }); return [x, y]; } private prevCursor: string; setCursor(ico: string | null) { const defs = ["move", "inherit", "pointer"]; ico = defs.includes(ico) ? ico : ico ? `url("${ico}"), auto` : "inherit"; this.config.dom.style.cursor = ico; this.prevCursor = ico; return () => { if (this.prevCursor === ico) { this.config.dom.style.cursor = "inherit"; this.prevCursor = "inherit"; } }; } getSameLevelData( entity: T ): T extends { attrib: infer A } ? A[] : never { const parentAttrib = this.attrib.data; for (const key in parentAttrib) { if ( Array.isArray(parentAttrib[key]) && ~(parentAttrib[key] as Attrib[]).indexOf(entity.attrib) ) { return parentAttrib[key] as any; } } } }