123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469 |
- import { DC, EntityShape } from "../../deconstruction";
- import { Stage } from "konva/lib/Stage";
- import {
- computed,
- getCurrentInstance,
- nextTick,
- reactive,
- Ref,
- ref,
- shallowRef,
- watch,
- WatchCallback,
- WatchEffect,
- watchEffect,
- WatchEffectOptions,
- WatchOptions,
- WatchSource,
- } from "vue";
- import { Layer } from "konva/lib/Layer";
- import { Pos } from "@/utils/math.ts";
- import { listener } from "@/utils/event.ts";
- import { debounce, mergeFuns, onlyId } from "@/utils/shared.ts";
- import { StoreData } from "../store/store.ts";
- import { rendererMap, rendererName } from "@/constant/index.ts";
- import { Shape, ShapeConfig } from "konva/lib/Shape";
- import { ElLoading } from "element-plus";
- import { PropertyDescribes } from "../html-mount/propertys/index.ts";
- import { ShapeType } from "@/index.ts";
- import { isEditableElement } from "@/utils/dom.ts";
- let getInstance = getCurrentInstance
- export const useRendererInstance = () => {
- let instance = getInstance()!;
- while (instance.type.name !== rendererName) {
- if (instance.parent) {
- instance = instance.parent;
- } else {
- throw "未发现渲染实例";
- }
- }
- return instance;
- };
- export const installGlobalVar = <T>(
- create: () => { var: T; onDestroy: () => void } | T,
- key = Symbol("globalVar")
- ) => {
- const useGlobalVar = (): T => {
- const instance = useRendererInstance() as any;
- const { unmounteds } = rendererMap.get(instance)!;
- if (!(key in instance)) {
- let val = create() as any;
- if (typeof val === "object" && "var" in val && "onDestroy" in val) {
- val.onDestroy && unmounteds.push(val.onDestroy);
- if (import.meta.env.DEV) {
- unmounteds.push(() => {
- console.log("销毁变量", key);
- });
- }
- val = val.var;
- }
- instance[key] = val;
- }
- return instance[key];
- };
- return useGlobalVar;
- };
- export const useRunHook = installGlobalVar(() => {
- const instance = getCurrentInstance()
- return <R, T extends () => R>(hook: T): R => {
- const back = getInstance
- getInstance = () => instance
- const result = hook()
- getInstance = back
- return result
- }
- })
- export type InstanceProps = {
- id?: string;
- data?: StoreData;
- handlerResource(file: File): Promise<string>;
- };
- export const useInstanceProps = installGlobalVar(() => {
- const props = ref<InstanceProps>();
- return {
- set(val: InstanceProps) {
- props.value = val;
- },
- get() {
- return props.value!;
- },
- };
- }, Symbol("instanceId"));
- export const stackVar = <T>(init?: T) => {
- const factory = (init: T) => ({ var: init, id: onlyId() });
- const stack = reactive([]) as { var: T; id: string }[];
- if (init) {
- stack.push(factory(init));
- }
- const result = {
- get value() {
- return stack[stack.length - 1]?.var;
- },
- set value(val) {
- stack[stack.length - 1].var = val;
- },
- push(data: T) {
- stack.push(factory(data));
- const item = stack[stack.length - 1];
- const pop = (() => {
- const ndx = stack.findIndex(({ id }) => id === item.id);
- if (~ndx) {
- stack.splice(ndx, 1);
- }
- }) as (() => void) & { set: (data: T) => void };
- pop.set = (data) => {
- item.var = data;
- };
- return pop;
- },
- pop() {
- if (stack.length - 1 > 0) {
- stack.pop();
- } else {
- console.error("已到达栈顶");
- }
- },
- cycle<R>(data: T, run: () => R): R {
- result.push(data);
- const r = run();
- result.pop();
- return r;
- },
- };
- return result;
- };
- export const globalWatch = <T>(
- source: WatchSource<T>,
- cb: WatchCallback<T, T>,
- options?: WatchOptions
- ): (() => void) => {
- let stop: () => void;
- nextTick(() => {
- stop = watch(source, cb as any, options as any);
- });
- return () => {
- stop && stop();
- };
- };
- export const globalWatchEffect = (
- cb: WatchEffect,
- options?: WatchEffectOptions
- ): (() => void) => {
- let stop: () => void;
- nextTick(() => {
- stop = watchEffect(cb as any, options);
- });
- return () => {
- stop && stop();
- };
- };
- export const useStage = installGlobalVar(
- () => shallowRef<DC<Stage> | undefined>(),
- Symbol("stage")
- );
- export const usePointerPos = installGlobalVar(() => {
- const stage = useStage();
- const pos = ref(null) as Ref<Pos | null> & { replay: () => void };
- let lastClient: { clientX: number; clientY: number } | null = null;
- let replayIng = false;
- const replay = () => {
- const $stage = stage.value?.getNode();
- if (!$stage || !lastClient) return;
- replayIng = true;
- const dom = $stage.container().querySelector("canvas") as HTMLCanvasElement;
- const moveConf = {
- bubbles: true, // 事件是否能够冒泡
- cancelable: true, // 事件是否可以被取消
- isPrimary: true,
- pointerId: 1,
- };
- dom.dispatchEvent(
- new PointerEvent("pointermove", {
- ...moveConf,
- clientX: -9999999,
- clientY: -9999999,
- })
- );
- dom.dispatchEvent(
- new PointerEvent("pointermove", { ...lastClient, ...moveConf })
- );
- replayIng = false;
- };
- const stopWatch = globalWatchEffect((onCleanup) => {
- const $stage = stage.value?.getNode();
- if (!$stage) return;
- const mount = $stage.container().parentElement!;
- pos.value = $stage.pointerPos;
- onCleanup(
- listener(mount, "pointermove", (ev) => {
- pos.value = $stage.pointerPos;
- if (pos.value && !replayIng) {
- lastClient = {
- clientX: ev.clientX,
- clientY: ev.clientY,
- };
- }
- })
- );
- });
- pos.replay = replay;
- return {
- var: pos,
- onDestroy: stopWatch,
- };
- }, Symbol("pointerPos"));
- export const usePointerIntersections = installGlobalVar(() => {
- const shapes = ref<Shape<ShapeConfig>[]>([]);
- const pos = usePointerPos();
- const stage = useStage();
- const updateShapes = debounce(() => {
- if (!pos.value || !stage.value) {
- return;
- }
- shapes.value = stage.value.getNode().getAllIntersections(pos.value) || [];
- }, 300);
- const stopWatch = watch(pos, updateShapes);
- return {
- var: shapes,
- onDestroy: () => {
- stopWatch();
- shapes.value = [];
- },
- };
- });
- export const usePointerIntersection = installGlobalVar(() => {
- const shape = ref<Shape<ShapeConfig> | null>(null);
- const pos = usePointerPos();
- const stage = useStage();
- const updateShape = debounce(() => {
- if (!pos.value || !stage.value) {
- return;
- }
- shape.value = stage.value.getNode().getIntersection(pos.value);
- }, 16);
- const stopWatch = watch(pos, updateShape);
- return {
- var: shape,
- onDestroy: () => {
- stopWatch();
- shape.value = null;
- },
- };
- });
- export const useDownKeys = installGlobalVar(() => {
- const keyKeys = reactive(new Set<string>());
- const mouseKeys = reactive(new Set<string>());
- const evHandler = (ev: KeyboardEvent | MouseEvent, keys: Set<string>) => {
- ev.shiftKey ? keys.add("Shift") : keys.delete("Shift");
- ev.altKey ? keys.add("Alt") : keys.delete("Alt");
- ev.metaKey ? keys.add("Meta") : keys.delete("Meta");
- ev.ctrlKey ? keys.add("Ctrl") : keys.delete("Ctrl");
- }
- const cleanup = mergeFuns(
- listener(window, "keydown", (ev) => {
- if (!isEditableElement(ev.target as HTMLElement)) {
- keyKeys.add(ev.key);
- evHandler(ev, keyKeys)
- }
- }),
- listener(window, "keyup", (ev) => {
- keyKeys.delete(ev.key);
- evHandler(ev, keyKeys)
- }),
- listener(window, "mousemove", (ev) => {
- evHandler(ev, mouseKeys)
- })
- );
- const keys = reactive(new Set<string>());
- watchEffect(() => {
- keys.clear();
- for (const key of keyKeys.values()) {
- keys.add(key);
- }
- for (const key of mouseKeys.values()) {
- keys.add(key);
- }
- });
- return {
- var: keys,
- onDestroy: cleanup,
- };
- });
- export const useLayers = () => {
- const stage = useStage();
- return computed(() => stage.value?.getNode().children as Layer[]);
- };
- export const useTransformIngShapes = installGlobalVar(
- () => ref<EntityShape[]>([]),
- Symbol("transformIngShapes")
- );
- export const useCursor = installGlobalVar(
- () => stackVar("default"),
- Symbol("cursor")
- );
- export type MPart = { comp: any; props: any };
- export const useMountParts = installGlobalVar(() => {
- const mParts = reactive<MPart[]>([]);
- const del = (part: MPart) => {
- const ndx = mParts.indexOf(part);
- ~ndx && mParts.splice(ndx, 1);
- };
- return {
- value: mParts,
- add(part: MPart) {
- mParts.push(part);
- return () => del(part);
- },
- del,
- };
- });
- export const useForciblyShowItemIds = installGlobalVar(() => {
- const set = new Set() as Set<string> & {
- cycle: (id: string, fn: () => any) => void;
- };
- set.cycle = (id, fn) => {
- set.add(id);
- const result = fn();
- if (result instanceof Promise) {
- result.then(() => set.delete(id));
- } else {
- set.delete(id);
- }
- };
- return set;
- }, Symbol("forciblyShowItemId"));
- export const useRendererDOM = installGlobalVar(() => ref<HTMLDivElement>())
- export const useTempStatus = installGlobalVar(() => {
- const temp = ref(false);
- const enterTemp = <T>(fn: () => T): T => {
- temp.value = true
- const result = fn()
- if (result instanceof Promise) {
- return result.then(async (data) => {
- temp.value = false
- await nextTick()
- return data;
- }).catch(r => {
- temp.value = false
- throw r
- }) as T
- } else {
- temp.value = false
- return result
- }
- }
- const dom = useRendererDOM()
- watch(temp, (_a, _b, onCleanup) => {
- if (temp.value && dom.value) {
- const instance = ElLoading.service({ fullscreen: true, target: dom.value })
- onCleanup(() => instance.close())
- }
- })
-
- return { tempStatus: temp, enterTemp }
- });
- const getFilters = <T>() => {
- type Val = (d: T) => T
- const globalFilter = ref<{[key in ShapeType]?: Val[]}>({})
- const shapeFilter = ref<Record<string, Val[]>>({})
- const setShapeFilter = (id: string, descs: Val) => {
- if (shapeFilter.value[id]) {
- shapeFilter.value[id].push(descs)
- } else {
- shapeFilter.value[id] = [descs]
- }
- return () => {
- if (shapeFilter.value[id]) {
- const ndx = shapeFilter.value[id].indexOf(descs)
- shapeFilter.value[id].splice(ndx, 1)
- }
- }
- }
- const setFilter = (type: ShapeType, descs: Val) => {
- if (globalFilter.value[type]) {
- globalFilter.value[type].push(descs)
- } else {
- globalFilter.value[type] = [descs]
- }
- return () => {
- if (globalFilter.value[type]) {
- const ndx = globalFilter.value[type].indexOf(descs)
- globalFilter.value[type].splice(ndx, 1)
- }
- }
- }
- return {
- setFilter,
- setShapeFilter,
- getFilter(type: ShapeType, id: string) {
- return (menus: T) => {
- if (globalFilter.value[type]) {
- for (const filter of globalFilter.value[type]) {
- menus = filter(menus)
- }
- }
- if (shapeFilter.value[id]) {
- for (const filter of shapeFilter.value[id]) {
- menus = filter(menus)
- }
- }
- return menus
- }
- }
- }
- }
- export const useMountMenusFilter = installGlobalVar(() => {
- const menusFilter = getFilters<PropertyDescribes>()
- return {
- setMenusFilter: menusFilter.setFilter,
- setShapeMenusFilter: menusFilter.setShapeFilter,
- getFilter: menusFilter.getFilter
- }
- })
- export const useMouseMenusFilter = installGlobalVar(() => {
- type Menu = { icon?: any; label?: string; handler: () => void }
- const propsFilter = getFilters<Menu[]>()
- return {
- setMenusFilter: propsFilter.setFilter,
- setShapeMenusFilter: propsFilter.setShapeFilter,
- getFilter: propsFilter.getFilter
- }
- })
|