entity-utils.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. import { KonvaEventObject } from "konva/lib/Node";
  2. import { hasTouchEvents } from "../env";
  3. import { Container } from "../packages";
  4. import { EntityType, Entity } from "../packages/entity";
  5. import { Attrib, ShapeType } from "../type";
  6. import { getTouchOffset } from "./act";
  7. import { createLineByDire, getDireDist } from "./math";
  8. import { inRevise } from "./public";
  9. import { disableMouse, enableMouse } from "./shape-mose";
  10. import { generateId, getChangeAllPoart } from "./util";
  11. const getExtendsProps = (parent: Entity<any, any>) => {
  12. return parent
  13. ? {
  14. reactive: parent.props.reactive,
  15. readonly: parent.props.reactive,
  16. }
  17. : {};
  18. };
  19. export const entityFactory = <
  20. T extends Attrib,
  21. S extends ShapeType,
  22. C extends EntityType<T, S, { attrib: T }>
  23. >(
  24. attrib: T,
  25. Type: C,
  26. parent?: Entity<any, any>,
  27. extra?: (self: InstanceType<C>) => void,
  28. created?: (self: InstanceType<C>) => void
  29. ) => {
  30. const entity = new Type({
  31. attrib,
  32. ...getExtendsProps(parent),
  33. }) as InstanceType<C>;
  34. extra && extra(entity);
  35. if (parent) {
  36. entity.container = parent.container;
  37. entity.setParent(parent);
  38. }
  39. entity.init();
  40. entity.mount(entity.teleport);
  41. if (parent.isMounted) {
  42. entity.mounted();
  43. }
  44. created && created(entity);
  45. return entity;
  46. };
  47. export type IncEntitysFactory<T extends Attrib, E extends Entity<T, any>> = (
  48. attribs: T[] | T
  49. ) => IncEntitys<T, E>;
  50. export type IncEntitys<T extends Attrib, E extends Entity<T, any>> = {
  51. adds: E[];
  52. dels: E[];
  53. upds: E[];
  54. };
  55. // 增量工厂
  56. export const incEntitysFactoryGenerate = <
  57. T extends Attrib,
  58. S extends ShapeType,
  59. C extends EntityType<T, S, { attrib: T }>
  60. >(
  61. Type: C,
  62. parent?: Entity<any, any>,
  63. extra?: (self: InstanceType<C>) => void,
  64. created?: (self: InstanceType<C>) => void
  65. ) => {
  66. let oldAttribs: T[] = [];
  67. const findAttrib = (attribs: T[], id: Attrib["id"]) =>
  68. attribs.find((attrib) => attrib.id === id);
  69. const cache: { [key in Attrib["id"]]: InstanceType<C> } = {};
  70. const destory = (id: Attrib["id"]) => {
  71. const delEntity = cache[id];
  72. delEntity.destory();
  73. delete cache[id];
  74. return delEntity;
  75. };
  76. const add = (attrib: T) => {
  77. const addEntity = entityFactory(attrib, Type, parent, extra, created);
  78. cache[attrib.id] = addEntity;
  79. return addEntity;
  80. };
  81. return (attribsRaw: T | T[]) => {
  82. const attribs = Array.isArray(attribsRaw) ? attribsRaw : [attribsRaw];
  83. const { addPort, delPort, changePort } = getChangeAllPoart(
  84. attribs,
  85. oldAttribs
  86. );
  87. const dels = delPort.map(destory);
  88. const adds = addPort.map((id) => add(findAttrib(attribs, id)));
  89. const upds = changePort.map((id) => {
  90. const newAttrib = findAttrib(attribs, id);
  91. if (inRevise(newAttrib, cache[id].attrib)) {
  92. console.log("setAttrib");
  93. cache[id].setAttrib(findAttrib(attribs, id));
  94. }
  95. return cache[id];
  96. });
  97. oldAttribs = [...attribs];
  98. return {
  99. adds,
  100. dels,
  101. upds,
  102. };
  103. };
  104. };
  105. export type CopyProps<T extends Entity = Entity> = {
  106. entity: T;
  107. count?: number;
  108. dire?: number[];
  109. size: number[];
  110. factoryAttrib(pos: number[]): any;
  111. };
  112. export const copyEntityAttrib = <T extends Entity = Entity>(
  113. props: CopyProps<T>
  114. ) => {
  115. const count = props.count || 1;
  116. const dire = props.dire || [1, 0];
  117. const halfSize = props.size;
  118. const entity = props.entity;
  119. const start = entity.shape.getPosition();
  120. const unit = getDireDist(halfSize);
  121. const items = entity.container.getSameLevelData(entity) as Attrib[];
  122. for (let i = 0; i < count; i++) {
  123. const line = createLineByDire(dire, [start.x, start.y], unit * (i + 1));
  124. const newAttrib = {
  125. ...props.factoryAttrib([line[2], line[3]]),
  126. id: generateId(items),
  127. };
  128. items.push(newAttrib as any);
  129. }
  130. };
  131. const interrupts = new WeakMap<Container, () => void>();
  132. export const addEntityAttrib = (
  133. container: Container,
  134. factoryAttrib: (pos: number[]) => any
  135. ) => {
  136. const oldInterrupt = interrupts.get(container);
  137. if (oldInterrupt) {
  138. oldInterrupt();
  139. }
  140. let finished = false;
  141. const finish = () => {
  142. if (finished) return;
  143. if (hasTouchEvents) {
  144. container.stage.off("touchend.addEntity");
  145. } else {
  146. container.stage.off("click.addEntity");
  147. }
  148. enableMouse();
  149. interrupts.delete(container);
  150. finished = true;
  151. };
  152. const complete = (data: any) => {
  153. finish();
  154. resolve(data);
  155. };
  156. const interrupt = () => {
  157. finish();
  158. reject("cancel");
  159. };
  160. let resolve: (data: any) => void, reject: (msg: string) => void;
  161. const promise = new Promise<any>((rs, rj) => {
  162. resolve = rs;
  163. reject = rj;
  164. const clickHandler = (ev: KonvaEventObject<any>) => {
  165. let offset = ev.evt;
  166. if (ev.evt instanceof TouchEvent) {
  167. offset = getTouchOffset(ev);
  168. }
  169. const pos = container.getRealFromStage([offset.offsetX, offset.offsetY]);
  170. complete(factoryAttrib(pos));
  171. };
  172. disableMouse();
  173. interrupts.set(container, interrupt);
  174. if (hasTouchEvents) {
  175. container.stage.on("touchend.addEntity", clickHandler);
  176. } else {
  177. container.stage.on("click.addEntity", clickHandler);
  178. }
  179. });
  180. return {
  181. promise,
  182. interrupt,
  183. };
  184. };