import { Entity, EntityEvent, EntityTree } from "./entity"; import konva from "konva"; import { entityMount } from "./entity-server"; import { Emitter, Pos, RootMat } from "../type"; import { EditModeProps, injectSetCursor, injectConstant, EditModeChange, openEditModePacking, injectPointerEvents, PointerEvents, } from "./entity-root-server"; import { inRevise } from "../../shared"; import { Transform } from "konva/lib/Util"; export type RootEvent = EntityEvent & { triggerFocus: Entity; triggerDrag: Entity | null; addEntity: Entity; delEntity: Entity; setEntity: Entity; entityChangeBefore: void; entityChange: EditModeChange; entityChangeAfter: void; changeView: RootMat; changeViewPort: Pos; changeViewScale: RootMat["scale"]; changeViewPosition: RootMat["position"]; changeViewRotation: RootMat["rotation"]; }; export class Root extends Entity< null, konva.Layer, EntityTree> > { dragEntity: Entity | null = null; container?: HTMLDivElement; stage: konva.Stage; fixLayer: konva.Layer; tempLayer: konva.Layer; bus: Emitter; mat: Transform; invMat: Transform; tempComtainer = document.createElement("div"); constructor() { super({ name: "container", attrib: null, key: "root", }); this.root = this; this.stage = new konva.Stage({ container: document.createElement("div") }); this.fixLayer = new konva.Layer(); this.mat = this.stage.getTransform(); this.invMat = this.mat.copy().invert(); this.stage.add(this.fixLayer); this.setTeleport(this.stage); } history: null | { hasRecovery: boolean }; setHistory(history: null | { hasRecovery: boolean }) { this.history = history; } setCursor = injectSetCursor(this); initShape() { return new konva.Layer(); } trigger: PointerEvents; openPointerEvents() { this.trigger = injectPointerEvents(this); } closePointerEvents() { this.trigger && this.trigger.destory(); } private __editPacking: ReturnType; get hasEditMode() { return !!this.__editPacking; } editMode(props?: EditModeProps) { if (this.__editPacking) { throw "当前正在编辑模式"; } this.__editPacking = openEditModePacking(this, props); } leaveEditMode() { if (this.__editPacking) { this.__editPacking.complete(); this.__editPacking = null; } } interruptEditMode() { if (this.__editPacking) { this.__editPacking.interrupt(); this.__editPacking = null; } } getPixel(real: Pos) { return this.mat.point(real); } getReal(pixel: Pos) { return this.invMat.point(pixel); } constant = injectConstant(this); private __changeContainerRelease: () => void; mount(container: HTMLDivElement = this.tempComtainer): void { if (container === this.container && this.isMounted) return; if (!container) throw "mount 需要 container"; if (this.container) { this.__changeContainerRelease(); } const changeSize = () => { const w = container.offsetWidth; const h = container.offsetHeight; if (w !== this.stage.width() || h !== this.stage.height()) { this.stage.width(w); this.stage.height(h); this.bus.emit("changeViewPort", { x: w, y: h }); } }; if (container) { this.stage.setContainer(container); changeSize(); window.addEventListener("resize", changeSize); this.__changeContainerRelease = () => { window.removeEventListener("resize", changeSize); }; this.container = container; this.isMounted || entityMount(this); } } updateViewMat(transform: Transform) { const mat = transform.decompose(); const scale = { x: mat.scaleX, y: mat.scaleY, }; const position = { x: mat.x, y: mat.y, }; const rotation = mat.rotation; const scaleChange = inRevise(scale, this.shape.scale()); const positionChange = inRevise(position, this.shape.position()); const rotateChange = inRevise(rotation, this.shape.rotation()); this.shape.scale(scale); this.shape.rotate(rotation); this.shape.position(position); this.mat = transform.copy(); this.invMat = transform.copy().invert(); this.shape.draw(); if (scaleChange) { this.bus.emit("changeViewScale", scale); } if (positionChange) { this.bus.emit("changeViewPosition", position); } if (rotateChange) { this.bus.emit("changeViewRotation", rotation); } if (rotateChange || scaleChange || positionChange) { this.bus.emit("changeView", { scale: this.shape.scale(), position: this.shape.position(), rotation: this.shape.rotation(), }); } } }