shape-mose.ts 5.5 KB

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