shape-mose.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. import { Attrib, ShapeStyles, ShapeType } from "../type";
  2. import { Entity } from "../packages/entity";
  3. import { Shape } from "konva/lib/Shape";
  4. import { Group } from "konva/lib/Group";
  5. import { getAbsoluteTransform } from "./shape-helper";
  6. import { KonvaEventObject } from "konva/lib/Node";
  7. let mouseDisabled = false;
  8. const dragShapes: (Shape | Group)[] = [];
  9. export const disableMouse = () => {
  10. mouseDisabled = true;
  11. dragShapes.forEach((shape) => shape.draggable(false));
  12. };
  13. export const enableMouse = () => {
  14. mouseDisabled = false;
  15. dragShapes.forEach((shape) => shape.draggable(true));
  16. };
  17. export const openShapeMouseStyles = <T extends Shape | Group>(
  18. shape: T,
  19. styles: ShapeStyles,
  20. namespace = "mouse-style"
  21. ) => {
  22. shape.listening(true);
  23. const useEvents: string[] = [];
  24. const useOn = (name: string, cb: (ev: KonvaEventObject<any>) => void) => {
  25. shape.on(name, cb);
  26. useEvents.push(name);
  27. };
  28. let enter = false;
  29. let draging = false;
  30. let active = false;
  31. const mouseHandler = (ev: KonvaEventObject<any>) => {
  32. const api = draging
  33. ? "draging"
  34. : active
  35. ? "active"
  36. : enter
  37. ? "hover"
  38. : "common";
  39. if (!mouseDisabled) {
  40. styles[api] && styles[api](ev);
  41. }
  42. };
  43. if (styles.hover) {
  44. useOn(`mouseenter.${namespace}`, (ev) => {
  45. enter = true;
  46. mouseHandler(ev);
  47. });
  48. useOn(`mouseleave.${namespace}`, (ev) => {
  49. if (!draging) {
  50. enter = false;
  51. mouseHandler(ev);
  52. }
  53. });
  54. }
  55. if (styles.active) {
  56. useOn(`click.${namespace} touchend.${namespace}`, (ev) => {
  57. if (!styles.active) return;
  58. if (draging) return;
  59. active = true;
  60. mouseHandler(ev);
  61. setTimeout(() => {
  62. const stage = shape.getStage();
  63. if (!stage) return;
  64. stage.on(
  65. `click.${namespace}${shape.id()} touchend.${namespace}${shape.id()}`,
  66. (evt) => {
  67. if (evt.target !== shape) {
  68. active = false;
  69. mouseHandler(evt);
  70. }
  71. setTimeout(() => {
  72. if (!stage) return;
  73. stage.off(
  74. `click.${namespace}${shape.id()} touchend.${namespace}${shape.id()}`
  75. );
  76. });
  77. }
  78. );
  79. });
  80. });
  81. }
  82. useOn(`dragstart.${namespace}`, (ev) => {
  83. draging = true;
  84. mouseHandler(ev);
  85. });
  86. useOn(`dragend.${namespace}`, (ev) => {
  87. setTimeout(() => {
  88. draging = false;
  89. mouseHandler(ev);
  90. }, 16);
  91. });
  92. return () => {
  93. shape.listening(false);
  94. shape.off(useEvents.join(" "));
  95. };
  96. };
  97. export type DragHandlers<T> = {
  98. readyHandler?: (ev: KonvaEventObject<any>) => T;
  99. moveHandler: (
  100. move: number[],
  101. readyData: T,
  102. ev: KonvaEventObject<any>
  103. ) => void;
  104. endHandler?: (readyData: T, ev: KonvaEventObject<any>) => void;
  105. };
  106. export const openShapeDrag = <T extends Shape | Group, D>(
  107. shape: T,
  108. handler: DragHandlers<D>,
  109. transform = true,
  110. tfIncludeSelf = false
  111. ) => {
  112. let readlyData = null as D;
  113. if (!mouseDisabled) {
  114. shape.draggable(true);
  115. }
  116. dragShapes.push(shape);
  117. shape.dragBoundFunc((pos, ev) => {
  118. if (!mouseDisabled) {
  119. let move = pos;
  120. if (transform) {
  121. const tf = getAbsoluteTransform(shape, tfIncludeSelf).invert();
  122. move = tf.point(pos);
  123. }
  124. handler.moveHandler([move.x, move.y], readlyData, ev);
  125. }
  126. return shape.absolutePosition();
  127. });
  128. if (handler.readyHandler) {
  129. shape.on("dragstart.drag", (ev) => {
  130. if (!mouseDisabled) {
  131. readlyData = handler.readyHandler(ev);
  132. }
  133. });
  134. }
  135. if (handler.endHandler) {
  136. shape.on("dragend.drag", (ev) => {
  137. if (!mouseDisabled) {
  138. handler.endHandler(readlyData, ev);
  139. }
  140. });
  141. }
  142. return () => {
  143. const ndx = dragShapes.indexOf(shape);
  144. if (~ndx) {
  145. dragShapes.splice(ndx, 1);
  146. }
  147. shape.draggable(false);
  148. shape.off("dragstart.drag dragend.drag");
  149. };
  150. };
  151. export type EntityDragHandlers<R extends Attrib, T> = {
  152. readyHandler?: (attrib: R, ev: KonvaEventObject<any>) => T;
  153. moveHandler: (
  154. attrib: R,
  155. move: number[],
  156. readyData: T,
  157. ev: KonvaEventObject<any>
  158. ) => void;
  159. endHandler?: (attrib: R, readyData: T, ev: KonvaEventObject<any>) => void;
  160. };
  161. export const openEntityDrag = <T extends Attrib, S extends ShapeType, R>(
  162. entity: Entity<T, S>,
  163. handler: EntityDragHandlers<T, R>
  164. ) => {
  165. entity.enableDrag({
  166. readyHandler(ev) {
  167. return handler.readyHandler && handler.readyHandler(entity.attrib, ev);
  168. },
  169. moveHandler(move: number[], readyData: R, ev) {
  170. return handler.moveHandler(entity.attrib, move, readyData, ev);
  171. },
  172. endHandler(readlyData: R, ev) {
  173. return (
  174. handler.endHandler && handler.endHandler(entity.attrib, readlyData, ev)
  175. );
  176. },
  177. });
  178. };
  179. export const shapeTreeEq = (
  180. parent: ShapeType,
  181. eq: (shape: ShapeType) => boolean
  182. ) => {
  183. if (eq(parent)) {
  184. return parent;
  185. }
  186. if ("children" in parent) {
  187. for (const child of parent.children) {
  188. const e = shapeTreeEq(child, eq);
  189. if (e) {
  190. return child;
  191. }
  192. }
  193. }
  194. return null;
  195. };
  196. export const shapeParentsEq = (
  197. target: ShapeType,
  198. eq: (shape: ShapeType) => boolean
  199. ) => {
  200. while (target) {
  201. if (eq(target)) {
  202. return target;
  203. }
  204. target = target.parent as any;
  205. }
  206. return null;
  207. };
  208. export const shapeTreeContain = (parent: ShapeType, target: ShapeType) => {
  209. const eqShape = shapeTreeEq(parent, (shape) => shape === target);
  210. return !!eqShape;
  211. };