entity-server.ts 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. import { Layer } from "konva/lib/Layer";
  2. import { debounce, mergeFuns } from "../../shared";
  3. import { Entity, EntityShape, EntityTransmit } from "./entity";
  4. import { Root } from "./entity-root";
  5. import { Stage } from "konva/lib/Stage";
  6. import { contain } from "../helper/shape";
  7. import {
  8. autoEmitDataChange,
  9. hasAutoEmitDataChange,
  10. } from "./entity-root-server";
  11. import { canEntityReply } from "../event";
  12. import { Pos } from "../type";
  13. import { KonvaEventObject } from "konva/lib/Node";
  14. import { getOffset } from "../helper/dom";
  15. export const traversEntityTree = (
  16. entity: Entity,
  17. call: (entity: Entity, inverse: boolean) => void | "interrupt",
  18. inverse: boolean | "all" = false
  19. ) => {
  20. if (!inverse || inverse === "all") {
  21. if (call(entity, false) === "interrupt") {
  22. return "interrupt";
  23. }
  24. }
  25. const eqed: Entity[] = [];
  26. while (true) {
  27. const child = entity.children.find((child) => !eqed.includes(child));
  28. if (child) {
  29. if (traversEntityTree(child, call, inverse) === "interrupt") {
  30. return "interrupt";
  31. }
  32. eqed.push(child);
  33. } else {
  34. break;
  35. }
  36. }
  37. if (inverse || inverse === "all") {
  38. if (call(entity, true) === "interrupt") {
  39. return "interrupt";
  40. }
  41. }
  42. };
  43. export const bubbleTraversEntityTree = (
  44. entity: Entity,
  45. call: (entity: Entity, inverse: boolean) => void | "interrupt",
  46. inverse: boolean | "all" = false
  47. ) => {
  48. if (!entity) return;
  49. if (!inverse || inverse === "all") {
  50. if (call(entity, false) === "interrupt") return "interrupt";
  51. }
  52. if (bubbleTraversEntityTree(entity.parent, call, inverse) === "interrupt") {
  53. return "interrupt";
  54. }
  55. if (inverse || inverse === "all") {
  56. if (call(entity, true) === "interrupt") return "interrupt";
  57. }
  58. };
  59. export const findEntity = <T extends Entity>(
  60. entity: Entity,
  61. name: string
  62. ): T => {
  63. let find: T;
  64. traversEntityTree(entity, (t) => {
  65. if (t.name === name) {
  66. find = t as T;
  67. return "interrupt";
  68. }
  69. });
  70. return find;
  71. };
  72. export const findEntityByShape = <T extends Entity>(
  73. entity: Entity,
  74. shape: EntityShape
  75. ) => {
  76. let find: T;
  77. if (shape instanceof Stage) {
  78. if (entity instanceof Root && entity.stage === shape) {
  79. return entity;
  80. } else {
  81. return null;
  82. }
  83. }
  84. const checked: EntityShape[] = [];
  85. traversEntityTree(
  86. entity,
  87. (child) => {
  88. if (contain(child.shape, shape, checked)) {
  89. find = child as T;
  90. return "interrupt";
  91. } else {
  92. checked.push(child.shape);
  93. }
  94. },
  95. true
  96. );
  97. return find;
  98. };
  99. export const getEntityNdx = (entity: Entity) => {
  100. const parent = entity.parent;
  101. if (!parent) return null;
  102. const zIndex = entity.getZIndex();
  103. const level = parent.children;
  104. for (let i = level.length - 1; i >= 0; i--) {
  105. if (level[i] !== entity && level[i].getZIndex() <= zIndex) {
  106. return i;
  107. }
  108. }
  109. return -1;
  110. };
  111. export const summarizeEntity = (entity: Entity) => {
  112. if (!entity.isMounted) return;
  113. const packNdx = getEntityNdx(entity);
  114. if (packNdx === null) return;
  115. const packChild = entity.children;
  116. const oldNdx = packChild.indexOf(entity);
  117. if (oldNdx !== packNdx + 1) {
  118. let rep = entity;
  119. for (let i = packNdx + 1; i < packChild.length; i++) {
  120. const temp = packChild[i];
  121. packChild[i] = rep;
  122. rep = temp;
  123. }
  124. }
  125. const parentShape = entity.getTeleport();
  126. const levelShapes = parentShape.children;
  127. const shapeNdx =
  128. packNdx === -1 ? 0 : levelShapes.indexOf(packChild[packNdx].getShape()) + 1;
  129. const oldShapeNdx = levelShapes.indexOf(entity.shape);
  130. if (oldShapeNdx !== shapeNdx) {
  131. if (shapeNdx !== 0) {
  132. let repShape = entity.shape;
  133. for (let i = shapeNdx; i < levelShapes.length; i++) {
  134. const temp = levelShapes[i];
  135. parentShape.add(repShape);
  136. repShape.zIndex(i);
  137. repShape = temp;
  138. }
  139. } else {
  140. entity.shape.zIndex(0);
  141. }
  142. }
  143. };
  144. export const entityInit = <T extends Entity>(entity: T) => {
  145. entity.bus.emit("createBefore");
  146. entity.shape = entity.initShape();
  147. entity.shape.id(entity.name);
  148. entity.bus.emit("created");
  149. let releases = entity.needReleases();
  150. releases = Array.isArray(releases) ? releases : [releases];
  151. if (releases.length) {
  152. entity.bus.on("destroyBefore", () => {
  153. mergeFuns(releases)();
  154. });
  155. }
  156. };
  157. export const entityMount = <T extends Entity>(entity: T) => {
  158. traversEntityTree(entity, (entity) => {
  159. entity.bus.emit("mountBefore");
  160. if (entity.parent) {
  161. const transmit = getEntityTransmitProps(entity.parent);
  162. for (const key in transmit) {
  163. entity[key] = transmit[key];
  164. }
  165. }
  166. entity.diffRedraw();
  167. });
  168. traversEntityTree(
  169. entity,
  170. (entity) => {
  171. setEntityShapePosition(entity);
  172. entity.isMounted = true;
  173. entity.bus.emit("mounted");
  174. },
  175. true
  176. );
  177. };
  178. const setEntityShapePosition = (entity: Entity) => {
  179. const parentShape = (entity.getTeleport() || entity.parent?.shape) as Layer;
  180. if (parentShape) {
  181. parentShape.add(entity.shape);
  182. }
  183. summarizeEntity(entity);
  184. };
  185. export const setEntityParent = <T extends Entity>(
  186. parent: T["parent"] | null,
  187. entity: T
  188. ) => {
  189. if (entity.parent) {
  190. const ndx = entity.parent.children.indexOf(entity);
  191. ~ndx && entity.parent.children.splice(ndx, 1);
  192. }
  193. entity.parent = parent;
  194. parent && entity.parent.children.push(entity);
  195. entity.isMounted && setEntityShapePosition(entity);
  196. };
  197. const getEntityTransmitProps = (parent: Entity): EntityTransmit => {
  198. return {
  199. root: parent.root,
  200. };
  201. };
  202. export const mountEntityTree = <T extends Entity>(
  203. parent: T["parent"] | null,
  204. entity: T
  205. ) => {
  206. if (parent) {
  207. setEntityParent(parent, entity);
  208. }
  209. entityMount(entity);
  210. if (entity.root) {
  211. entity.root.bus.emit("addEntity", entity);
  212. if (
  213. entity.root.history &&
  214. !entity.root.history.hasRecovery &&
  215. !hasAutoEmitDataChange(entity.root)
  216. ) {
  217. entity.root.bus.emit("entityChange", { addEntitys: [this] });
  218. }
  219. }
  220. return entity;
  221. };
  222. export type DragHandlers = (ev: KonvaEventObject<any>) => {
  223. move: (move: Pos, ev: MouseEvent | TouchEvent) => void;
  224. end?: (ev: KonvaEventObject<any>) => void;
  225. };
  226. export const openEntityDrag = <T extends Entity>(
  227. entity: T,
  228. getHandlers: DragHandlers,
  229. trasnform = true
  230. ) => {
  231. const shape = entity instanceof Root ? entity.stage : entity.shape;
  232. shape.draggable(true);
  233. let canReply = false;
  234. let moveHandler: ReturnType<DragHandlers>["move"];
  235. let endHandler: ReturnType<DragHandlers>["end"];
  236. let destoryAutoEmit: () => void;
  237. let start: Pos;
  238. shape.on("dragstart.drag", (ev) => {
  239. canReply = canEntityReply(entity);
  240. if (canReply) {
  241. const handlers = getHandlers(ev);
  242. moveHandler = handlers.move;
  243. endHandler = handlers.end;
  244. if (entity.root.history) {
  245. destoryAutoEmit = autoEmitDataChange(entity.root);
  246. }
  247. start = getOffset(ev.evt);
  248. }
  249. });
  250. shape.dragBoundFunc((pos, ev) => {
  251. if (canReply) {
  252. const end = getOffset(ev);
  253. const move = {
  254. x: end.x - start.x,
  255. y: end.y - start.y,
  256. };
  257. moveHandler(trasnform ? entity.root.invMat.point(pos) : move, ev);
  258. }
  259. return shape.absolutePosition();
  260. });
  261. shape.on("dragend.drag", (ev) => {
  262. moveHandler = null;
  263. canReply = false;
  264. start = null;
  265. endHandler && endHandler(ev);
  266. destoryAutoEmit && destoryAutoEmit();
  267. });
  268. return () => {
  269. shape.draggable(false);
  270. shape.off("dragstart.drag dragend.drag");
  271. };
  272. };
  273. export type EntityPointerStatus = {
  274. drag: boolean;
  275. hover: boolean;
  276. focus: boolean;
  277. };
  278. export const openOnEntityPointerStatus = (entity: Entity) => {
  279. const status: EntityPointerStatus = {
  280. drag: false,
  281. hover: false,
  282. focus: false,
  283. };
  284. const emti = debounce(() => {
  285. entity.bus.emit("pointerStatus", status);
  286. }, 16);
  287. const handler = (partial: Partial<EntityPointerStatus>) => {
  288. Object.assign(status, partial);
  289. emti();
  290. };
  291. const focusHandler = () => handler({ focus: true });
  292. const blurHandler = () => handler({ focus: false });
  293. const hoverHandler = () => handler({ hover: true });
  294. const leaveHandler = () => handler({ hover: false });
  295. const dragHandler = () => handler({ drag: true });
  296. const dropHandler = () => handler({ drag: false });
  297. entity.bus.on("focus", focusHandler);
  298. entity.bus.on("blur", blurHandler);
  299. entity.bus.on("hover", hoverHandler);
  300. entity.bus.on("leave", leaveHandler);
  301. entity.bus.on("drag", dragHandler);
  302. entity.bus.on("drop", dropHandler);
  303. const destory = () => {
  304. entity.bus.off("focus", focusHandler);
  305. entity.bus.off("blur", blurHandler);
  306. entity.bus.off("hover", hoverHandler);
  307. entity.bus.off("leave", leaveHandler);
  308. entity.bus.off("drag", dragHandler);
  309. entity.bus.off("drop", dropHandler);
  310. };
  311. entity.bus.on("destroyBefore", destory);
  312. return destory;
  313. };