use-global-vars.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import { DC, EntityShape } from "../../deconstruction";
  2. import { Stage } from "konva/lib/Stage";
  3. import {
  4. computed,
  5. getCurrentInstance,
  6. nextTick,
  7. onUnmounted,
  8. reactive,
  9. ref,
  10. shallowRef,
  11. watch,
  12. WatchCallback,
  13. watchEffect,
  14. WatchOptions,
  15. WatchSource,
  16. } from "vue";
  17. import { Mode } from "../../constant/mode.ts";
  18. import { Layer } from "konva/lib/Layer";
  19. import { Pos } from "@/utils/math.ts";
  20. import { listener } from "@/utils/event.ts";
  21. import { mergeFuns } from "@/utils/shared.ts";
  22. export const installGlobalVar = <T>(
  23. create: () => { var: T; onDestroy: () => void } | T,
  24. key = Symbol("globalVar"),
  25. noRefDel = true
  26. ) => {
  27. let initialed = false;
  28. let refCount = 0;
  29. let onDestroy: (() => void) | null = null;
  30. const useGlobalVar = (): T => {
  31. const instance = getCurrentInstance() as any;
  32. const ctx = instance.appContext;
  33. if (!initialed) {
  34. let val = create() as any;
  35. if (typeof val === "object" && "var" in val && "onDestroy" in val) {
  36. onDestroy = val.onDestory;
  37. val = val.var;
  38. }
  39. ctx[key] = val;
  40. initialed = true;
  41. }
  42. return ctx[key];
  43. };
  44. return noRefDel
  45. ? () => {
  46. const instance = getCurrentInstance() as any;
  47. const ctx = instance.appContext;
  48. ++refCount;
  49. onUnmounted(() => {
  50. if (--refCount === 0 && noRefDel) {
  51. initialed = false;
  52. delete ctx[key];
  53. console.log("销毁", key);
  54. onDestroy && onDestroy();
  55. onDestroy = null;
  56. }
  57. });
  58. return useGlobalVar();
  59. }
  60. : useGlobalVar;
  61. };
  62. export const stackVar = <T>(init?: T) => {
  63. const stack = reactive([init]) as T[];
  64. const result = {
  65. get value() {
  66. return stack[stack.length - 1];
  67. },
  68. set value(val) {
  69. stack[stack.length - 1] = val;
  70. },
  71. push(data: T) {
  72. stack.push(data);
  73. },
  74. pop() {
  75. if (stack.length - 1 > 0) {
  76. stack.pop();
  77. } else {
  78. console.error("已到达栈顶");
  79. }
  80. },
  81. cycle<R>(data: T, run: () => R): R {
  82. result.push(data);
  83. const r = run();
  84. result.pop();
  85. return r;
  86. },
  87. };
  88. return result;
  89. };
  90. export const globalWatch = <T>(
  91. source: WatchSource<T>,
  92. cb: WatchCallback<T, T>,
  93. options?: WatchOptions
  94. ): (() => void) => {
  95. let stop: () => void;
  96. nextTick(() => {
  97. stop = watch(source, cb as any, options as any);
  98. });
  99. return () => {
  100. stop && stop();
  101. };
  102. };
  103. export const useStage = installGlobalVar(
  104. () => shallowRef<DC<Stage> | undefined>(),
  105. Symbol("stage")
  106. );
  107. export const useMode = installGlobalVar(() => {
  108. const stack = stackVar(new Set([Mode.viewer]))
  109. const modeStack = {
  110. ...stack,
  111. get value() {
  112. return stack.value
  113. },
  114. set value(val: Set<Mode>) {
  115. stack.value = val
  116. },
  117. push(...modes: Mode[]) {
  118. return stack.push(new Set(modes))
  119. },
  120. include(...modes: Mode[]) {
  121. return modes.every((m) => modeStack.value.has(m));
  122. },
  123. add(...modes: Mode[]) {
  124. modes.forEach((mode) => modeStack.value.add(mode));
  125. },
  126. del(...modes: Mode[]) {
  127. modes.forEach((mode) => modeStack.value.delete(mode));
  128. }
  129. }
  130. if (import.meta.env.DEV) {
  131. watchEffect(() => {
  132. console.error([...modeStack.value.values()].join(','))
  133. }, { flush: 'sync' })
  134. }
  135. return modeStack;
  136. }, Symbol("mode"));
  137. export const useCan = installGlobalVar(() => {
  138. const mode = useMode();
  139. const stage = useStage();
  140. const key = useDownKeys()
  141. const loaded = computed(() => !!stage.value?.getStage())
  142. // 鼠标是否可用
  143. const mouse = computed(() => loaded.value && !mode.include(Mode.readonly))
  144. // 可以进入拖拽模式
  145. const dragMode = computed(() => {
  146. if (!mouse.value || mode.include(Mode.viewer) || key.has(' ')) return false;
  147. return mode.include(Mode.draw) || mode.include(Mode.update)
  148. })
  149. // 是否在视图模式
  150. const viewMode = computed(() => {
  151. return mouse.value && (!mode.include(Mode.draging) || key.has(' '))
  152. })
  153. // shape是否可以对鼠标做出反应
  154. const mouseReact = computed(() => mouse.value && (mode.include(Mode.viewer) || mode.include(Mode.update)))
  155. // 可以进入编辑模式
  156. const editMode = computed(() => mouse.value && mode.include(Mode.viewer))
  157. // 可以进入绘制模式
  158. const drawMode = computed(() => mouse.value && mode.include(Mode.viewer))
  159. return reactive({
  160. viewMouseReact: mouse,
  161. viewMode,
  162. drawMode,
  163. mouseReact,
  164. editMode,
  165. dragMode,
  166. });
  167. });
  168. export const usePointerPos = installGlobalVar(() => {
  169. const stage = useStage();
  170. const pos = ref<Pos | null>(null);
  171. watchEffect((onCleanup) => {
  172. const $stage = stage.value?.getNode();
  173. if (!$stage) return;
  174. const mount = $stage.container().parentElement!;
  175. pos.value = $stage.pointerPos;
  176. onCleanup(
  177. listener(mount, "pointermove", () => {
  178. pos.value = $stage.pointerPos;
  179. })
  180. );
  181. });
  182. return pos;
  183. }, Symbol("pointerPos"));
  184. export const useDownKeys = installGlobalVar(() => {
  185. const keys = reactive(new Set<string>());
  186. const cleanup = mergeFuns(
  187. listener(window, "keydown", (ev) => {
  188. keys.add(ev.key);
  189. }),
  190. listener(window, "keyup", (ev) => {
  191. keys.delete(ev.key);
  192. })
  193. );
  194. return {
  195. var: keys,
  196. onDestroy: cleanup,
  197. };
  198. });
  199. export const useLayers = () => {
  200. const stage = useStage();
  201. return computed(() => stage.value?.getNode().children as Layer[]);
  202. };
  203. export const useTransformIngShapes = installGlobalVar(
  204. () => ref<EntityShape[]>([]),
  205. Symbol("transformIngShapes")
  206. );
  207. export const useCursor = installGlobalVar(
  208. () => stackVar('default'),
  209. Symbol('cursor')
  210. )