|
@@ -1,94 +1,136 @@
|
|
|
-import { Entity, EntityShape } from "../base/entity";
|
|
|
+import { Entity } from "../base/entity";
|
|
|
import { KonvaEventObject } from "konva/lib/Node";
|
|
|
import {
|
|
|
bubbleTraversEntityTree,
|
|
|
findEntityByShape,
|
|
|
-} from "../base/entity-util";
|
|
|
-import { Stage } from "konva/lib/Stage";
|
|
|
+} from "../base/entity-server";
|
|
|
+import { Root } from "../base/entity-root";
|
|
|
|
|
|
-const getEventArgs = (key: string) => {
|
|
|
- const [name, ...args] = key.split(".");
|
|
|
- const useCapture = args.includes("capture");
|
|
|
- return [name, useCapture] as const;
|
|
|
+const canEntityReply = (entity: Entity) => {
|
|
|
+ let canReply = true;
|
|
|
+ bubbleTraversEntityTree(entity, (entity) => {
|
|
|
+ if (entity.replyEvents === "none") {
|
|
|
+ canReply = false;
|
|
|
+ return "interrupt";
|
|
|
+ } else if (entity.replyEvents === "all") {
|
|
|
+ return "interrupt";
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return canReply;
|
|
|
};
|
|
|
|
|
|
-// 将konva的时间转发到entity
|
|
|
-const entityTreeDistributor = (
|
|
|
- root: Entity,
|
|
|
- shape: EntityShape | Stage = root.shape
|
|
|
+// 仅限内部使用
|
|
|
+export const onEntity = (
|
|
|
+ entity: Entity,
|
|
|
+ event: string,
|
|
|
+ cb: (ev: KonvaEventObject<any>) => void
|
|
|
) => {
|
|
|
- const handlerMap: { [key in string]: Entity[] } = {};
|
|
|
- const addedKeys: string[] = [];
|
|
|
+ const handler = (ev: KonvaEventObject<any>) =>
|
|
|
+ canEntityReply(entity) && cb(ev);
|
|
|
+ const shape = entity === entity.root ? entity.root.shape : entity.shape;
|
|
|
+ shape.on(event, handler);
|
|
|
+ const destory = () => shape.off(event, handler);
|
|
|
+ entity.bus.on("destroyBefore", destory);
|
|
|
+ return destory;
|
|
|
+};
|
|
|
+
|
|
|
+export type TreeEvent = {
|
|
|
+ paths: Entity[];
|
|
|
+ target: Entity;
|
|
|
+ cancelBubble: boolean;
|
|
|
+};
|
|
|
+export type TreeEventName = "mouseenter" | "mouseleave" | "click" | "touchend";
|
|
|
+
|
|
|
+export const emitEntityTree = (
|
|
|
+ entity: Entity,
|
|
|
+ name: TreeEventName,
|
|
|
+ one = false
|
|
|
+) => {
|
|
|
+ const ev: TreeEvent = {
|
|
|
+ cancelBubble: false,
|
|
|
+ target: entity,
|
|
|
+ paths: [entity],
|
|
|
+ };
|
|
|
+ if (one) {
|
|
|
+ entity.bus.emit(name as any, ev);
|
|
|
+ entity.bus.emit((name + ".capture") as any, ev);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ let paths: Entity[] = [];
|
|
|
+ bubbleTraversEntityTree(entity, (t) => {
|
|
|
+ paths.push(t);
|
|
|
+ });
|
|
|
+
|
|
|
+ let prevStatus = "all";
|
|
|
+ for (let i = paths.length - 1; i >= 0; i++) {
|
|
|
+ const currStatus = paths[i].replyEvents;
|
|
|
+ if (
|
|
|
+ currStatus === "none" ||
|
|
|
+ (prevStatus === "none" && currStatus === "auto")
|
|
|
+ ) {
|
|
|
+ paths.splice(i--, 1);
|
|
|
+ prevStatus = "none";
|
|
|
+ } else {
|
|
|
+ prevStatus = "all";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ev.paths = paths;
|
|
|
+ for (const entity of paths) {
|
|
|
+ entity.bus.emit(name as any, ev);
|
|
|
+ if (ev.cancelBubble) break;
|
|
|
+ }
|
|
|
+
|
|
|
+ ev.cancelBubble = false;
|
|
|
+ paths = paths.reverse();
|
|
|
+ ev.paths = paths;
|
|
|
+ for (const entity of paths) {
|
|
|
+ entity.bus.emit((name + ".capture") as any, ev);
|
|
|
+ if (ev.cancelBubble) break;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 将konva的时间转发到entity
|
|
|
+const entityTreeDistributor = (root: Root) => {
|
|
|
+ const shape = root.stage;
|
|
|
+ const listenEvents: TreeEventName[] = [];
|
|
|
+ const handlerMap: { [key in TreeEventName]?: Entity[] } = {};
|
|
|
+
|
|
|
+ const addEvent = (name: TreeEventName) => {
|
|
|
+ if (listenEvents.includes(name)) return;
|
|
|
+ listenEvents.push(name);
|
|
|
|
|
|
- const addEvent = (key: string) => {
|
|
|
- const [name] = getEventArgs(key);
|
|
|
- if (addedKeys.includes(name)) return;
|
|
|
shape.on(name, (ev: KonvaEventObject<any>) => {
|
|
|
const self = findEntityByShape(root, ev.target);
|
|
|
- const paths: Entity[] = [];
|
|
|
- bubbleTraversEntityTree(self, (t) => {
|
|
|
- paths.push(t);
|
|
|
- });
|
|
|
-
|
|
|
- const bubbles: Entity[] = [];
|
|
|
- const captures: Entity[] = [];
|
|
|
- for (const key in handlerMap) {
|
|
|
- const [cname, useCapture] = getEventArgs(key);
|
|
|
- if (name === cname) {
|
|
|
- if (useCapture) {
|
|
|
- captures.push(...handlerMap[key]);
|
|
|
- } else {
|
|
|
- bubbles.push(...handlerMap[key]);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- const bubblePaths = bubbles
|
|
|
- .filter((entity) => paths.includes(entity))
|
|
|
- .sort((a, b) => paths.indexOf(a) - paths.indexOf(b));
|
|
|
- for (const entity of bubblePaths) {
|
|
|
- entity.bus.emit(name as any, ev);
|
|
|
- if (ev.cancelBubble) break;
|
|
|
- }
|
|
|
- ev.cancelBubble = false;
|
|
|
-
|
|
|
- const capturePaths = captures
|
|
|
- .filter((entity) => paths.includes(entity))
|
|
|
- .sort((a, b) => paths.indexOf(b) - paths.indexOf(a));
|
|
|
- for (const entity of capturePaths) {
|
|
|
- entity.bus.emit((name + ".capture") as any, ev);
|
|
|
- if (ev.cancelBubble) break;
|
|
|
- }
|
|
|
- ev.cancelBubble = false;
|
|
|
+ emitEntityTree(self, name);
|
|
|
});
|
|
|
- addedKeys.push(name);
|
|
|
};
|
|
|
|
|
|
- const delEvent = (key: string) => {
|
|
|
- const [name] = getEventArgs(key);
|
|
|
- const ndx = addedKeys.indexOf(name);
|
|
|
+ const delEvent = (name: TreeEventName) => {
|
|
|
+ const ndx = listenEvents.indexOf(name);
|
|
|
if (!~ndx) return;
|
|
|
+ listenEvents.splice(ndx, 1);
|
|
|
shape.off(name);
|
|
|
- addedKeys.splice(ndx, 1);
|
|
|
};
|
|
|
|
|
|
return {
|
|
|
- on: (entity: Entity, key: string) => {
|
|
|
- if (!handlerMap[key]) {
|
|
|
- handlerMap[key] = [];
|
|
|
+ on: (entity: Entity, name: TreeEventName) => {
|
|
|
+ if (!handlerMap[name]) {
|
|
|
+ handlerMap[name] = [];
|
|
|
}
|
|
|
- handlerMap[key].push(entity);
|
|
|
- addEvent(key);
|
|
|
+
|
|
|
+ handlerMap[name].push(entity);
|
|
|
+ addEvent(name);
|
|
|
},
|
|
|
- off: (entity: Entity, key?: string) => {
|
|
|
- const keys = key ? [key] : Object.keys(handlerMap);
|
|
|
+ off: (entity: Entity, key?: TreeEventName) => {
|
|
|
+ const keys = (key ? [key] : Object.keys(handlerMap)) as TreeEventName[];
|
|
|
const vals = key
|
|
|
? entity[key]
|
|
|
? [entity[key]]
|
|
|
: []
|
|
|
: Object.values(handlerMap);
|
|
|
|
|
|
- const delKeys: string[] = [];
|
|
|
for (let i = 0; i < vals.length; i++) {
|
|
|
const ckey = keys[i];
|
|
|
const items = vals[i];
|
|
@@ -102,23 +144,14 @@ const entityTreeDistributor = (
|
|
|
}
|
|
|
if (j === 0) {
|
|
|
delete handlerMap[ckey];
|
|
|
- delKeys.push(ckey);
|
|
|
- }
|
|
|
- }
|
|
|
- if (!delKeys.length) return;
|
|
|
-
|
|
|
- const ckeys = Object.keys(handlerMap);
|
|
|
- for (const delKey of delKeys) {
|
|
|
- const delName = getEventArgs(delKey)[0];
|
|
|
- if (!ckeys.some((ckey) => getEventArgs(ckey)[0] === delName)) {
|
|
|
- delEvent(delKey);
|
|
|
+ delEvent(ckey);
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
- exixtsEvents: addedKeys,
|
|
|
+ handlerMap: handlerMap,
|
|
|
destory() {
|
|
|
- while (addedKeys.length) {
|
|
|
- delEvent(addedKeys[0]);
|
|
|
+ while (listenEvents.length) {
|
|
|
+ delEvent(listenEvents[0]);
|
|
|
}
|
|
|
},
|
|
|
};
|
|
@@ -137,16 +170,29 @@ const getDistributor = (entity: Entity) => {
|
|
|
return distributor;
|
|
|
};
|
|
|
|
|
|
-const canHandlerEvents = [""];
|
|
|
-export const onEntity = (entity: Entity, key: string) => {
|
|
|
+const getEventArgs = (key: string) => {
|
|
|
+ const [name, ...args] = key.split(".");
|
|
|
+ const useCapture = args.includes("capture");
|
|
|
+ return [name as TreeEventName, useCapture] as const;
|
|
|
+};
|
|
|
+
|
|
|
+// 外部使用
|
|
|
+export const openOnEntityTree = (entity: Entity, key: string) => {
|
|
|
const distributor = getDistributor(entity);
|
|
|
- distributor.on(entity, key);
|
|
|
- entity.bus.on("mountBefore", () => {
|
|
|
- distributor.off(entity, key);
|
|
|
- });
|
|
|
+ const [name] = getEventArgs(key);
|
|
|
+ const destory = () => closeOnEntityTree(entity, key);
|
|
|
+ if (
|
|
|
+ !distributor.handlerMap[name] ||
|
|
|
+ !distributor.handlerMap[name].includes(entity)
|
|
|
+ ) {
|
|
|
+ distributor.on(entity, name);
|
|
|
+ entity.bus.on("destroyBefore", destory);
|
|
|
+ }
|
|
|
+ return destory;
|
|
|
};
|
|
|
|
|
|
-export const offEntity = (entity: Entity, key?: string) => {
|
|
|
+export const closeOnEntityTree = (entity: Entity, key?: string) => {
|
|
|
const distributor = getDistributor(entity);
|
|
|
- distributor.off(entity, key);
|
|
|
+ const [name] = getEventArgs(key);
|
|
|
+ distributor && distributor.off(entity, name);
|
|
|
};
|