123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494 |
- import { Entity } from "../base/entity";
- import { Root } from "./entity-root";
- import { Pos } from "../type";
- import { Transform } from "konva/lib/Util";
- import {
- canEntityReply,
- onEntity,
- openOnEntityTree,
- TreeEvent,
- } from "../event";
- import { findEntityByShape } from "./entity-server";
- import { debounce, getChangePart, mergeFuns } from "../../shared";
- // 指定某些entity可编辑
- export const openOnlyMode = (
- root: Root,
- _entitys: Entity[],
- includeNews = true
- ) => {
- const entitys: (Entity | null)[] = [];
- const prevEntitysReply: Entity["replyEvents"][] = [];
- const setEntityReply = (entity: Entity, reply: Entity["replyEvents"]) => {
- const ndx = entitys.length;
- entitys[ndx] = entity;
- prevEntitysReply[ndx] = entity.replyEvents;
- entity.replyEvents = reply;
- };
- root.children.forEach((item) => setEntityReply(item, "none"));
- _entitys.forEach((item) => setEntityReply(item, "all"));
- const delHandler = (entity: Entity) => {
- const ndx = entitys.indexOf(entity);
- prevEntitysReply[ndx] = null;
- entitys[ndx] = null;
- };
- const addHandler = (entity: Entity) => {
- if (includeNews) {
- setEntityReply(entity, "all");
- } else if (root.children.includes(entity)) {
- setEntityReply(entity, "none");
- }
- };
- root.bus.on("delEntity", delHandler);
- root.bus.on("addEntity", addHandler);
- return () => {
- root.bus.off("delEntity", delHandler);
- root.bus.off("addEntity", addHandler);
- entitys.forEach((entity, ndx) => {
- if (entity) {
- entity.replyEvents = prevEntitysReply[ndx];
- }
- });
- };
- };
- export type EditModeProps = {
- entitys?: Entity[];
- only?: boolean;
- includeNews?: boolean;
- };
- export type EditModeChange = {
- addEntitys?: Entity[];
- setEntitys?: Entity[];
- delEntitys?: Entity[];
- };
- export const openEditMode = (
- root: Root,
- main: () => any,
- props: EditModeProps = {
- entitys: [],
- only: false,
- includeNews: false,
- }
- ) => {
- if (!props.entitys) props.entitys = [];
- if (!props.only) props.only = false;
- if (!props.includeNews) props.includeNews = false;
- root.bus.emit("entityChangeBefore");
- const quitOnlyMode =
- props.only && openOnlyMode(root, props.entitys, props.includeNews);
- let state = "normal";
- let resolve: () => void;
- const interrupt = () => {
- state = "interrupt";
- resolve();
- };
- const destoryAutoEmit = root.history && autoEmitDataChange(root);
- const interruptPromise = new Promise<void>((r) => (resolve = r));
- const draw = Promise.any([interruptPromise, Promise.resolve(main())]).then(
- () => {
- quitOnlyMode && quitOnlyMode();
- destoryAutoEmit && destoryAutoEmit();
- setTimeout(() => {
- root.bus.emit("entityChangeAfter");
- });
- return { state };
- }
- );
- return {
- draw,
- interrupt,
- };
- };
- export const openEditModePacking = (root: Root, props?: EditModeProps) => {
- let complete: () => void;
- const { interrupt } = openEditMode(
- root,
- () => {
- return new Promise<void>((r) => (complete = r));
- },
- props
- );
- return {
- interrupt,
- complete,
- };
- };
- const autoedRoots = new WeakMap<
- Root,
- {
- quete: number;
- destory: () => void;
- pause: () => void;
- continue: () => void;
- }
- >();
- export const hasAutoEmitDataChange = (root: Root) => {
- return autoedRoots.has(root);
- };
- export const pauseAutoEmitDataChange = (root: Root) => {
- if (autoedRoots.has(root)) {
- autoedRoots.get(root).pause();
- }
- };
- export const continueAutoEmitDataChange = (root: Root) => {
- if (autoedRoots.has(root)) {
- autoedRoots.get(root).continue();
- }
- };
- export const autoEmitDataChange = (root: Root) => {
- if (autoedRoots.has(root)) {
- const old = autoedRoots.get(root);
- old.quete++;
- return old.destory;
- }
- let pause = false;
- const addHandler = (entity: Entity) =>
- pause ||
- (!root.history?.hasRecovery &&
- root.bus.emit("entityChange", { addEntitys: [entity] }));
- const delHandler = (entity: Entity) =>
- pause ||
- (!root.history?.hasRecovery &&
- root.bus.emit("entityChange", { delEntitys: [entity] }));
- const changeEntitys = new Set<Entity>();
- const setHandler = (entity: Entity) => {
- if (!pause) {
- if (!entity.root.dragEntity) {
- !root.history?.hasRecovery &&
- root.bus.emit("entityChange", { setEntitys: [entity] });
- } else {
- changeEntitys.add(entity);
- }
- }
- };
- const triggerDragHandler = (entity: Entity) => {
- if (!entity) {
- changeEntitys.forEach(setHandler);
- }
- };
- root.bus.on("addEntity", addHandler);
- root.bus.on("delEntity", delHandler);
- root.bus.on("setEntity", setHandler);
- root.bus.on("triggerDrag", triggerDragHandler);
- const destory = () => {
- if (--autoedRoots.get(root).quete === 0) {
- root.bus.off("setEntity", setHandler);
- root.bus.off("delEntity", delHandler);
- root.bus.off("addEntity", addHandler);
- root.bus.off("triggerDrag", triggerDragHandler);
- autoedRoots.delete(root);
- }
- };
- autoedRoots.set(root, {
- quete: 1,
- destory,
- pause: () => (pause = true),
- continue: () => (pause = false),
- });
- return destory;
- };
- const cursorResources = {
- pic_pen_a: "/cursors/pic_pen_a.ico",
- pic_pen_r: "/cursors/pic_pen_r.ico",
- pic_pen: "/cursors/pic_pen.ico",
- };
- export const addCursorResource = (key: string, url: string) => {
- cursorResources[key] = url;
- };
- export const injectSetCursor = (root: Root) => {
- const cursorStack = [];
- const setCursorStyle = (ico: string) => {
- const url = ico in cursorResources ? cursorResources[ico] : null;
- root.container.style.cursor = url ? `url("${ico}"), auto` : ico;
- };
- return (ico: string) => {
- const ndx = cursorStack.length;
- cursorStack[ndx] = ico;
- setCursorStyle(ico);
- return () => {
- cursorStack[ndx] = null;
- let last = cursorStack.length - 1;
- for (; last >= 0; last--) {
- if (cursorStack[last] !== null) {
- break;
- }
- }
- if (last === -1) {
- setCursorStyle("inherit");
- cursorStack.length = 0;
- } else if (last < ndx) {
- setCursorStyle(cursorStack[last]);
- }
- };
- };
- };
- export const injectConstant = (root: Root) => {
- const origin: { [key in string]: number | Pos } = {};
- const current: { [key in string]: number | Pos } = {};
- let mat: Transform;
- let scale: Pos;
- let position: Pos;
- let rCos: number, rSin: number;
- root.bus.on("mounted", function handler() {
- mat = root.stage.getTransform().invert();
- scale = root.stage.scale();
- position = root.stage.position();
- let radians = root.stage.rotation() * (Math.PI / 180);
- rCos = Math.cos(radians);
- rSin = Math.sin(radians);
- root.bus.off("mounted", handler);
- });
- const invView = (key: string) => {
- if (key.startsWith("fix:")) {
- if (typeof origin[key] === "number") {
- current[key] = mat.point({ x: origin[key], y: 0 }).x;
- } else {
- current[key] = mat.point(origin[key]);
- }
- }
- };
- root.bus.on("changeView", () => {
- mat = root.stage.getTransform().invert();
- Object.keys(origin).forEach(invView);
- });
- const invScale = (key: string) => {
- if (key.startsWith("fixScale:")) {
- if (typeof origin[key] === "number") {
- current[key] = origin[key] / scale.x;
- } else {
- current[key] = {
- x: origin[key].x / scale.x,
- y: origin[key].y / scale.y,
- };
- }
- }
- };
- root.bus.on("changeViewScale", (nscale) => {
- scale = nscale;
- Object.keys(origin).forEach(invScale);
- });
- const invPosition = (key: string) => {
- if (key.startsWith("fixPosition:")) {
- if (typeof origin[key] === "number") {
- current[key] = origin[key] - position.x;
- } else {
- current[key] = {
- x: origin[key].x - position.x,
- y: origin[key].y - position.y,
- };
- }
- }
- };
- root.bus.on("changeViewPosition", (nposition) => {
- position = nposition;
- Object.keys(origin).forEach(invPosition);
- });
- const invRotation = (key: string) => {
- if (key.startsWith("fixRotation:")) {
- const p = origin[key];
- if (typeof p !== "number") {
- current[key] = {
- x: p.x * rCos - p.y * rSin,
- y: p.x * rSin + p.y * rCos,
- };
- }
- }
- };
- root.bus.on("changeViewRotation", (rotation) => {
- let radians = rotation * (Math.PI / 180);
- rCos = Math.cos(radians);
- rSin = Math.sin(radians);
- Object.keys(origin).forEach(invRotation);
- });
- return {
- set(key: string, val: number) {
- origin[key] = val;
- invView(key);
- invPosition(key);
- invRotation(key);
- invScale(key);
- },
- get<T extends number | Pos>(key: string): T {
- return (current[key] || origin[key]) as T;
- },
- };
- };
- const rootStack: Root[] = [];
- export const pushRoot = (root: Root) => rootStack.push(root);
- export const popRoot = () => rootStack.pop();
- export const currentRoot = () => rootStack[rootStack.length - 1];
- export const currentConstant = new Proxy(
- {},
- {
- get(_, p) {
- return currentRoot().constant.get(p as string);
- },
- }
- );
- export const injectPointerEvents = (root: Root) => {
- const store = {
- hovers: new Set<Entity>(),
- focus: new Set<Entity>(),
- drag: null as Entity,
- };
- const oldStore = {
- hovers: [] as Entity[],
- focus: [] as Entity[],
- drag: null as Entity,
- };
- const emit = debounce(() => {
- const hovers = [...store.hovers];
- const focus = [...store.focus];
- const hoverChange = getChangePart(hovers, oldStore.hovers);
- const focusChange = getChangePart(focus, oldStore.focus);
- hoverChange.addPort.forEach((entity) => entity.bus.emit("hover"));
- hoverChange.delPort.forEach((entity) => entity.bus.emit("leave"));
- focusChange.addPort.forEach((entity) => entity.bus.emit("focus"));
- focusChange.delPort.forEach((entity) => entity.bus.emit("blur"));
- if (oldStore.drag !== store.drag) {
- oldStore.drag && oldStore.drag.bus.emit("drop");
- store.drag && store.drag.bus.emit("drag");
- }
- oldStore.drag = store.drag;
- oldStore.hovers = hovers;
- oldStore.focus = focus;
- }, 16);
- const needReleases = [
- openOnEntityTree(root, "mouseover"),
- openOnEntityTree(root, "mouseout"),
- openOnEntityTree(root, "click"),
- openOnEntityTree(root, "touchend"),
- onEntity(root, "dragstart", (ev) => {
- const hit = findEntityByShape(root, ev.target);
- if (canEntityReply(hit)) {
- store.drag = hit;
- root.dragEntity = hit;
- root.bus.emit("triggerDrag", hit);
- emit();
- }
- }),
- onEntity(root, "dragend", () => {
- if (store.drag && canEntityReply(store.drag)) {
- store.drag = null;
- root.dragEntity = null;
- root.bus.emit("triggerDrag", null);
- emit();
- }
- }),
- ];
- const enterHandler = ({ paths }: TreeEvent) => {
- paths.forEach((entity) => {
- if (canEntityReply(entity)) {
- store.hovers.add(entity);
- }
- });
- emit();
- };
- const leaveHandler = ({ paths }: TreeEvent) => {
- paths.forEach((entity) => {
- if (canEntityReply(entity)) {
- store.hovers.delete(entity);
- }
- });
- emit();
- };
- const clickHandler = ({ paths }: TreeEvent) => {
- store.focus.clear();
- if (canEntityReply(paths[0])) {
- root.bus.emit("triggerFocus", paths[0]);
- }
- paths.forEach((entity) => {
- if (canEntityReply(entity)) {
- store.focus.add(entity);
- }
- });
- emit();
- };
- root.bus.on("mouseover" as any, enterHandler);
- root.bus.on("mouseout" as any, leaveHandler);
- root.bus.on("click", clickHandler);
- root.bus.on("touchend", clickHandler);
- const destory = () => {
- mergeFuns(needReleases)();
- root.bus.off("mouseover" as any, enterHandler);
- root.bus.off("mouseout" as any, leaveHandler);
- root.bus.off("click", clickHandler);
- root.bus.off("touchend", clickHandler);
- };
- root.bus.on("destroyBefore", destory);
- return {
- focus(...entitys: Entity[]) {
- store.focus.clear();
- entitys.forEach((entity) => store.focus.add(entity));
- emit();
- },
- blur(...entitys: Entity[]) {
- entitys.forEach((entity) => store.focus.delete(entity));
- emit();
- },
- hover(...entitys: Entity[]) {
- store.hovers.clear();
- entitys.forEach((entity) => store.hovers.add(entity));
- emit();
- },
- leave(...entitys: Entity[]) {
- entitys.forEach((entity) => store.hovers.delete(entity));
- emit();
- },
- drag(entity: Entity) {
- store.drag = entity;
- emit();
- },
- drop(entity: Entity) {
- if (store.drag === entity) {
- store.drag = entity;
- emit();
- }
- },
- destory,
- };
- };
- export type PointerEvents = ReturnType<typeof injectPointerEvents>;
|