spread.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import { Entity } from "../base/entity";
  2. import { KonvaEventObject } from "konva/lib/Node";
  3. import {
  4. bubbleTraversEntityTree,
  5. findEntityByShape,
  6. } from "../base/entity-server";
  7. import { Root } from "../base/entity-root";
  8. const canEntityReply = (entity: Entity) => {
  9. let canReply = true;
  10. bubbleTraversEntityTree(entity, (entity) => {
  11. if (entity.replyEvents === "none") {
  12. canReply = false;
  13. return "interrupt";
  14. } else if (entity.replyEvents === "all") {
  15. return "interrupt";
  16. }
  17. });
  18. return canReply;
  19. };
  20. // 仅限内部使用
  21. export const onEntity = (
  22. entity: Entity,
  23. event: string,
  24. cb: (ev: KonvaEventObject<any>) => void
  25. ) => {
  26. const handler = (ev: KonvaEventObject<any>) =>
  27. canEntityReply(entity) && cb(ev);
  28. const shape = entity === entity.root ? entity.root.shape : entity.shape;
  29. shape.on(event, handler);
  30. const destory = () => shape.off(event, handler);
  31. entity.bus.on("destroyBefore", destory);
  32. return destory;
  33. };
  34. export type TreeEvent = {
  35. paths: Entity[];
  36. target: Entity;
  37. cancelBubble: boolean;
  38. };
  39. export type TreeEventName = "mouseenter" | "mouseleave" | "click" | "touchend";
  40. export const emitEntityTree = (
  41. entity: Entity,
  42. name: TreeEventName,
  43. one = false
  44. ) => {
  45. const ev: TreeEvent = {
  46. cancelBubble: false,
  47. target: entity,
  48. paths: [entity],
  49. };
  50. if (one) {
  51. entity.bus.emit(name as any, ev);
  52. entity.bus.emit((name + ".capture") as any, ev);
  53. return;
  54. }
  55. let paths: Entity[] = [];
  56. bubbleTraversEntityTree(entity, (t) => {
  57. paths.push(t);
  58. });
  59. let prevStatus = "all";
  60. for (let i = paths.length - 1; i >= 0; i++) {
  61. const currStatus = paths[i].replyEvents;
  62. if (
  63. currStatus === "none" ||
  64. (prevStatus === "none" && currStatus === "auto")
  65. ) {
  66. paths.splice(i--, 1);
  67. prevStatus = "none";
  68. } else {
  69. prevStatus = "all";
  70. }
  71. }
  72. ev.paths = paths;
  73. for (const entity of paths) {
  74. entity.bus.emit(name as any, ev);
  75. if (ev.cancelBubble) break;
  76. }
  77. ev.cancelBubble = false;
  78. paths = paths.reverse();
  79. ev.paths = paths;
  80. for (const entity of paths) {
  81. entity.bus.emit((name + ".capture") as any, ev);
  82. if (ev.cancelBubble) break;
  83. }
  84. };
  85. // 将konva的时间转发到entity
  86. const entityTreeDistributor = (root: Root) => {
  87. const shape = root.stage;
  88. const listenEvents: TreeEventName[] = [];
  89. const handlerMap: { [key in TreeEventName]?: Entity[] } = {};
  90. const addEvent = (name: TreeEventName) => {
  91. if (listenEvents.includes(name)) return;
  92. listenEvents.push(name);
  93. shape.on(name, (ev: KonvaEventObject<any>) => {
  94. const self = findEntityByShape(root, ev.target);
  95. emitEntityTree(self, name);
  96. });
  97. };
  98. const delEvent = (name: TreeEventName) => {
  99. const ndx = listenEvents.indexOf(name);
  100. if (!~ndx) return;
  101. listenEvents.splice(ndx, 1);
  102. shape.off(name);
  103. };
  104. return {
  105. on: (entity: Entity, name: TreeEventName) => {
  106. if (!handlerMap[name]) {
  107. handlerMap[name] = [];
  108. }
  109. handlerMap[name].push(entity);
  110. addEvent(name);
  111. },
  112. off: (entity: Entity, key?: TreeEventName) => {
  113. const keys = (key ? [key] : Object.keys(handlerMap)) as TreeEventName[];
  114. const vals = key
  115. ? entity[key]
  116. ? [entity[key]]
  117. : []
  118. : Object.values(handlerMap);
  119. for (let i = 0; i < vals.length; i++) {
  120. const ckey = keys[i];
  121. const items = vals[i];
  122. let j = 0;
  123. for (; j < items.length; j++) {
  124. const centity = items[j];
  125. if (entity === centity) {
  126. items.splice(j--, 1);
  127. }
  128. }
  129. if (j === 0) {
  130. delete handlerMap[ckey];
  131. delEvent(ckey);
  132. }
  133. }
  134. },
  135. handlerMap: handlerMap,
  136. destory() {
  137. while (listenEvents.length) {
  138. delEvent(listenEvents[0]);
  139. }
  140. },
  141. };
  142. };
  143. const distributors = new WeakMap<
  144. Entity,
  145. ReturnType<typeof entityTreeDistributor>
  146. >();
  147. const getDistributor = (entity: Entity) => {
  148. let distributor = distributors.get(entity.root);
  149. if (!distributor) {
  150. distributor = entityTreeDistributor(entity.root);
  151. distributors.set(entity.root, distributor);
  152. }
  153. return distributor;
  154. };
  155. const getEventArgs = (key: string) => {
  156. const [name, ...args] = key.split(".");
  157. const useCapture = args.includes("capture");
  158. return [name as TreeEventName, useCapture] as const;
  159. };
  160. // 外部使用
  161. export const openOnEntityTree = (entity: Entity, key: string) => {
  162. const distributor = getDistributor(entity);
  163. const [name] = getEventArgs(key);
  164. const destory = () => closeOnEntityTree(entity, key);
  165. if (
  166. !distributor.handlerMap[name] ||
  167. !distributor.handlerMap[name].includes(entity)
  168. ) {
  169. distributor.on(entity, name);
  170. entity.bus.on("destroyBefore", destory);
  171. }
  172. return destory;
  173. };
  174. export const closeOnEntityTree = (entity: Entity, key?: string) => {
  175. const distributor = getDistributor(entity);
  176. const [name] = getEventArgs(key);
  177. distributor && distributor.off(entity, name);
  178. };