|
@@ -1,223 +1,240 @@
|
|
|
import { computed, reactive, ref, Ref, watch, watchEffect } from "vue";
|
|
|
import { DC, EntityShape } from "../../deconstruction";
|
|
|
import { Shape } from "konva/lib/Shape";
|
|
|
-import { globalWatch, installGlobalVar, useMode, useStage, useTransformIngShapes } from "./use-global-vars.ts";
|
|
|
+import {
|
|
|
+ globalWatch,
|
|
|
+ installGlobalVar,
|
|
|
+ useMode,
|
|
|
+ useStage,
|
|
|
+ useTransformIngShapes,
|
|
|
+} from "./use-global-vars.ts";
|
|
|
import { Stage } from "konva/lib/Stage";
|
|
|
import { listener } from "../../utils/event.ts";
|
|
|
import { inRevise, mergeFuns } from "../../utils/shared.ts";
|
|
|
import { ComponentValue, DrawItem, ShapeType } from "../components";
|
|
|
import { shapeTreeContain } from "../../utils/shape.ts";
|
|
|
-import { usePointerIsTransformerInner, useTransformer } from "./use-transformer.ts";
|
|
|
+import {
|
|
|
+ usePointerIsTransformerInner,
|
|
|
+ useTransformer,
|
|
|
+} from "./use-transformer.ts";
|
|
|
import { Mode } from "@/constant/mode.ts";
|
|
|
import { useAniamtion } from "./use-animation.ts";
|
|
|
|
|
|
-export const useMouseShapesStatus = installGlobalVar(
|
|
|
- () => {
|
|
|
- const mode = useMode()
|
|
|
- const stage = useStage()
|
|
|
- const listeners = ref([]) as Ref<EntityShape[]>
|
|
|
- const hovers = ref([]) as Ref<EntityShape[]>
|
|
|
- const press = ref([]) as Ref<EntityShape[]>
|
|
|
- const selects = ref([]) as Ref<EntityShape[]>
|
|
|
- const actives = ref([]) as Ref<EntityShape[]>
|
|
|
- const transformer = useTransformer()
|
|
|
- const pointerIsTransformerInner = usePointerIsTransformerInner()
|
|
|
-
|
|
|
-
|
|
|
- const init = (stage: Stage) => {
|
|
|
- console.log('init?')
|
|
|
- let downTime: number
|
|
|
- let downTarget: EntityShape | null
|
|
|
- const inner = new WeakMap<EntityShape, boolean>()
|
|
|
-
|
|
|
-
|
|
|
- stage.on('pointerenter.mouse-status', async (ev) => {
|
|
|
- const target = shapeTreeContain(listeners.value, ev.target)
|
|
|
- if (!target) return;
|
|
|
- inner.set(target, true)
|
|
|
- if (hovers.value.includes(target)) return;
|
|
|
-
|
|
|
- hovers.value.push(target)
|
|
|
- const targetLeave = () => {
|
|
|
- target.off('pointerleave.mouse-status')
|
|
|
- stage.off('pointermove.mouse-status')
|
|
|
- stopIncludeWatch! && stopIncludeWatch()
|
|
|
- const ndx = hovers.value.indexOf(target)
|
|
|
- if (~ndx) {
|
|
|
- hovers.value.splice(ndx, 1)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- let stopIncludeWatch: () => void
|
|
|
- target.on('pointerleave.mouse-status', ev => {
|
|
|
- const target = shapeTreeContain(listeners.value, ev.target)
|
|
|
- if (!target) return;
|
|
|
- inner.set(target, false)
|
|
|
-
|
|
|
- // TODO: 有可能在transformer上,需要额外检测
|
|
|
- stopIncludeWatch! && stopIncludeWatch()
|
|
|
- stopIncludeWatch = watch(transformer.queueShapes, (queueShapes, _, onCleanup) => {
|
|
|
- if (inner.get(target)) return;
|
|
|
-
|
|
|
- if (!queueShapes.includes(target) || !pointerIsTransformerInner()) {
|
|
|
- targetLeave()
|
|
|
- } else {
|
|
|
- stage.on('pointermove.mouse-status', () => {
|
|
|
- if (!inner.get(target) && !pointerIsTransformerInner()) {
|
|
|
- targetLeave()
|
|
|
- }
|
|
|
- })
|
|
|
- onCleanup(() => stage.off('pointermove.mouse-status'))
|
|
|
- }
|
|
|
- }, {immediate: true})
|
|
|
- })
|
|
|
- })
|
|
|
- stage.on('pointerdown.mouse-status', (ev) => {
|
|
|
- downTime = Date.now()
|
|
|
- const target = shapeTreeContain(listeners.value, ev.target)
|
|
|
- if (target && !press.value.includes(target)) {
|
|
|
- press.value.push(target)
|
|
|
- }
|
|
|
- downTarget = target
|
|
|
- })
|
|
|
-
|
|
|
- return mergeFuns(
|
|
|
- listener(stage.container(), 'pointerup', () => {
|
|
|
- press.value = []
|
|
|
- if ( Date.now() - downTime >= 300) return;
|
|
|
- if (downTarget) {
|
|
|
- const ndx = selects.value.indexOf(downTarget)
|
|
|
- if (~ndx) {
|
|
|
- selects.value.splice(ndx, 1)
|
|
|
- } else {
|
|
|
- selects.value.push(downTarget)
|
|
|
- }
|
|
|
- actives.value = [downTarget]
|
|
|
- } else{
|
|
|
- actives.value = []
|
|
|
- }
|
|
|
- }),
|
|
|
- () => {
|
|
|
- listeners.value.forEach((shape) => {
|
|
|
- shape.off('pointerleave.mouse-status')
|
|
|
- })
|
|
|
- stage.off('pointerenter.mouse-status pointerdown.mouse-status pointereup.mouse-status pointermove.mouse-status')
|
|
|
- hovers.value = []
|
|
|
- actives.value = []
|
|
|
- press.value = []
|
|
|
- selects.value = []
|
|
|
- }
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- let cleanup: () => void
|
|
|
- const stopStatusWatch = globalWatch(
|
|
|
- () => [stage.value?.getStage(), [Mode.update, Mode.viewer].includes(mode.value)] as const,
|
|
|
- (current, prev) => {
|
|
|
- if (inRevise(prev, current)) {
|
|
|
- cleanup! && cleanup()
|
|
|
- if (current[0] && current[1]) {
|
|
|
- cleanup = init(current[0])
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
- { immediate: true }
|
|
|
- )
|
|
|
-
|
|
|
- return {
|
|
|
- var: reactive({hovers, actives, selects, press, listeners}),
|
|
|
- onDestroy: () => {
|
|
|
- stopStatusWatch()
|
|
|
- cleanup && cleanup()
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
- Symbol('mouseStatus')
|
|
|
-)
|
|
|
-
|
|
|
-
|
|
|
-export const useMouseShapeStatus = (shape: Ref<DC<EntityShape> | undefined>) => {
|
|
|
- const status = useMouseShapesStatus()
|
|
|
- watch(
|
|
|
- () => shape.value?.getStage(),
|
|
|
- (shape, _, onCleanup) => {
|
|
|
- if (shape) {
|
|
|
- if (status.listeners.includes(shape)) return;
|
|
|
- status.listeners.push(shape)
|
|
|
- onCleanup(() => {
|
|
|
- for (const key in status) {
|
|
|
- const k = key as keyof typeof status
|
|
|
- const ndx = status[k].indexOf(shape)
|
|
|
- ~ndx && status[k].splice(ndx, 1)
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
- })
|
|
|
-
|
|
|
- return computed(() => {
|
|
|
- const $shape = shape.value?.getStage() as Shape
|
|
|
- return {
|
|
|
- hover: status.hovers.includes($shape),
|
|
|
- active: status.actives.includes($shape),
|
|
|
- press: status.press.includes($shape),
|
|
|
- select: status.selects.includes($shape),
|
|
|
- }
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
+export const useMouseShapesStatus = installGlobalVar(() => {
|
|
|
+ const mode = useMode();
|
|
|
+ const stage = useStage();
|
|
|
+ const listeners = ref([]) as Ref<EntityShape[]>;
|
|
|
+ const hovers = ref([]) as Ref<EntityShape[]>;
|
|
|
+ const press = ref([]) as Ref<EntityShape[]>;
|
|
|
+ const selects = ref([]) as Ref<EntityShape[]>;
|
|
|
+ const actives = ref([]) as Ref<EntityShape[]>;
|
|
|
+ const transformer = useTransformer();
|
|
|
+ const pointerIsTransformerInner = usePointerIsTransformerInner();
|
|
|
+
|
|
|
+ const init = (stage: Stage) => {
|
|
|
+ console.log("init?");
|
|
|
+ let downTime: number;
|
|
|
+ let downTarget: EntityShape | null;
|
|
|
+ const inner = new WeakMap<EntityShape, boolean>();
|
|
|
+
|
|
|
+ stage.on("pointerenter.mouse-status", async (ev) => {
|
|
|
+ const target = shapeTreeContain(listeners.value, ev.target);
|
|
|
+ if (!target) return;
|
|
|
+ inner.set(target, true);
|
|
|
+ if (hovers.value.includes(target)) return;
|
|
|
+
|
|
|
+ hovers.value.push(target);
|
|
|
+ const targetLeave = () => {
|
|
|
+ target.off("pointerleave.mouse-status");
|
|
|
+ stage.off("pointermove.mouse-status");
|
|
|
+ stopIncludeWatch! && stopIncludeWatch();
|
|
|
+ const ndx = hovers.value.indexOf(target);
|
|
|
+ if (~ndx) {
|
|
|
+ hovers.value.splice(ndx, 1);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ let stopIncludeWatch: () => void;
|
|
|
+ target.on("pointerleave.mouse-status", (ev) => {
|
|
|
+ const target = shapeTreeContain(listeners.value, ev.target);
|
|
|
+ if (!target) return;
|
|
|
+ inner.set(target, false);
|
|
|
+
|
|
|
+ // TODO: 有可能在transformer上,需要额外检测
|
|
|
+ stopIncludeWatch! && stopIncludeWatch();
|
|
|
+ stopIncludeWatch = watch(
|
|
|
+ transformer.queueShapes,
|
|
|
+ (queueShapes, _, onCleanup) => {
|
|
|
+ if (inner.get(target)) return;
|
|
|
+
|
|
|
+ if (!queueShapes.includes(target) || !pointerIsTransformerInner()) {
|
|
|
+ targetLeave();
|
|
|
+ } else {
|
|
|
+ stage.on("pointermove.mouse-status", () => {
|
|
|
+ if (!inner.get(target) && !pointerIsTransformerInner()) {
|
|
|
+ targetLeave();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ onCleanup(() => stage.off("pointermove.mouse-status"));
|
|
|
+ }
|
|
|
+ },
|
|
|
+ { immediate: true }
|
|
|
+ );
|
|
|
+ });
|
|
|
+ });
|
|
|
+ stage.on("pointerdown.mouse-status", (ev) => {
|
|
|
+ downTime = Date.now();
|
|
|
+ const target = shapeTreeContain(listeners.value, ev.target);
|
|
|
+ if (target && !press.value.includes(target)) {
|
|
|
+ press.value.push(target);
|
|
|
+ }
|
|
|
+ downTarget = target;
|
|
|
+ });
|
|
|
+
|
|
|
+ return mergeFuns(
|
|
|
+ listener(stage.container(), "pointerup", () => {
|
|
|
+ press.value = [];
|
|
|
+ if (Date.now() - downTime >= 300) return;
|
|
|
+ if (downTarget) {
|
|
|
+ const ndx = selects.value.indexOf(downTarget);
|
|
|
+ if (~ndx) {
|
|
|
+ selects.value.splice(ndx, 1);
|
|
|
+ } else {
|
|
|
+ selects.value.push(downTarget);
|
|
|
+ }
|
|
|
+ actives.value = [downTarget];
|
|
|
+ } else {
|
|
|
+ actives.value = [];
|
|
|
+ }
|
|
|
+ }),
|
|
|
+ () => {
|
|
|
+ listeners.value.forEach((shape) => {
|
|
|
+ shape.off("pointerleave.mouse-status");
|
|
|
+ });
|
|
|
+ stage.off(
|
|
|
+ "pointerenter.mouse-status pointerdown.mouse-status pointereup.mouse-status pointermove.mouse-status"
|
|
|
+ );
|
|
|
+ hovers.value = [];
|
|
|
+ actives.value = [];
|
|
|
+ press.value = [];
|
|
|
+ selects.value = [];
|
|
|
+ }
|
|
|
+ );
|
|
|
+ };
|
|
|
+
|
|
|
+ let cleanup: () => void;
|
|
|
+ const stopStatusWatch = globalWatch(
|
|
|
+ () =>
|
|
|
+ [
|
|
|
+ stage.value?.getStage(),
|
|
|
+ [Mode.update, Mode.viewer].includes(mode.value),
|
|
|
+ ] as const,
|
|
|
+ (current, prev) => {
|
|
|
+ if (inRevise(prev, current)) {
|
|
|
+ cleanup! && cleanup();
|
|
|
+ if (current[0] && current[1]) {
|
|
|
+ cleanup = init(current[0]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ { immediate: true }
|
|
|
+ );
|
|
|
+
|
|
|
+ return {
|
|
|
+ var: reactive({ hovers, actives, selects, press, listeners }),
|
|
|
+ onDestroy: () => {
|
|
|
+ stopStatusWatch();
|
|
|
+ cleanup && cleanup();
|
|
|
+ },
|
|
|
+ };
|
|
|
+}, Symbol("mouseStatus"));
|
|
|
+
|
|
|
+export const useMouseShapeStatus = (
|
|
|
+ shape: Ref<DC<EntityShape> | undefined>
|
|
|
+) => {
|
|
|
+ const status = useMouseShapesStatus();
|
|
|
+ watch(
|
|
|
+ () => shape.value?.getStage(),
|
|
|
+ (shape, _, onCleanup) => {
|
|
|
+ if (shape) {
|
|
|
+ if (status.listeners.includes(shape)) return;
|
|
|
+ status.listeners.push(shape);
|
|
|
+ onCleanup(() => {
|
|
|
+ for (const key in status) {
|
|
|
+ const k = key as keyof typeof status;
|
|
|
+ const ndx = status[k].indexOf(shape);
|
|
|
+ ~ndx && status[k].splice(ndx, 1);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ return computed(() => {
|
|
|
+ const $shape = shape.value?.getStage() as Shape;
|
|
|
+ return {
|
|
|
+ hover: status.hovers.includes($shape),
|
|
|
+ active: status.actives.includes($shape),
|
|
|
+ press: status.press.includes($shape),
|
|
|
+ select: status.selects.includes($shape),
|
|
|
+ };
|
|
|
+ });
|
|
|
+};
|
|
|
|
|
|
type MouseStyleProps<T extends ShapeType> = {
|
|
|
- shape?: Ref<DC<EntityShape> | undefined>
|
|
|
- // getMouseStyle: ComponentValue<T, 'getMouseStyle'>
|
|
|
- data: Ref<DrawItem<T>>
|
|
|
- getMouseStyle: any
|
|
|
-}
|
|
|
-type ValueOf<T> = T[keyof T]
|
|
|
+ shape?: Ref<DC<EntityShape> | undefined>;
|
|
|
+ getMouseStyle: ComponentValue<T, "getMouseStyle">;
|
|
|
+ data: Ref<DrawItem<T>>;
|
|
|
+};
|
|
|
export const useMouseStyle = <T extends ShapeType>(
|
|
|
- props: MouseStyleProps<T>,
|
|
|
+ props: MouseStyleProps<T>
|
|
|
) => {
|
|
|
- const shape = props.shape || ref()
|
|
|
- const status = useMouseShapeStatus(shape)
|
|
|
- const transformIngShapes = useTransformIngShapes()
|
|
|
- const mouseStyle = computed(() => {
|
|
|
- return props.getMouseStyle(props.data.value)
|
|
|
- })
|
|
|
- const getStyle = () => {
|
|
|
- const styleMap = new Map([[mouseStyle.value.default as any, true]])
|
|
|
- if ('hover' in mouseStyle.value) {
|
|
|
- styleMap.set(mouseStyle.value.hover, status.value.hover)
|
|
|
- }
|
|
|
- if ('press' in mouseStyle.value) {
|
|
|
- styleMap.set(mouseStyle.value.press, status.value.press)
|
|
|
- }
|
|
|
- if ('focus' in mouseStyle.value) {
|
|
|
- styleMap.set(mouseStyle.value.focus, status.value.active)
|
|
|
- }
|
|
|
- if ( 'drag' in mouseStyle.value && transformIngShapes.value.includes(shape.value?.getNode()!)) {
|
|
|
- styleMap.set(mouseStyle.value.drag, true)
|
|
|
- }
|
|
|
- // if ('select' in props.style) {
|
|
|
- // styleMap.set(props.style.select, status.value.select)
|
|
|
- // }
|
|
|
-
|
|
|
- const finalStyle = {} as ValueOf<ComponentValue<T, 'style'>>
|
|
|
- for (const [style, use] of styleMap.entries()) {
|
|
|
- use && Object.assign(finalStyle as any, style)
|
|
|
- }
|
|
|
- return finalStyle
|
|
|
- }
|
|
|
- const style = ref<ValueOf<ComponentValue<T, 'style'>>>()
|
|
|
- watchEffect(() => {
|
|
|
- const newStyle = getStyle()
|
|
|
- if (inRevise(newStyle, style.value)) {
|
|
|
- style.value = newStyle
|
|
|
- }
|
|
|
- })
|
|
|
- return { currentStyle: style, status, shape }
|
|
|
-}
|
|
|
-
|
|
|
-export const useAnimationMouseStyle = <T extends ShapeType>(props: MouseStyleProps<T>) => {
|
|
|
- const { currentStyle } = useMouseStyle(props);
|
|
|
- return useAniamtion(currentStyle as any);
|
|
|
- // return [currentStyle]
|
|
|
-}
|
|
|
+ const shape = props.shape || ref();
|
|
|
+ const status = useMouseShapeStatus(shape);
|
|
|
+ const transformIngShapes = useTransformIngShapes();
|
|
|
+ const mouseStyle = computed(() => {
|
|
|
+ return props.getMouseStyle(props.data.value as any) as any;
|
|
|
+ });
|
|
|
+ const getStyle = () => {
|
|
|
+ const styleMap = new Map([[mouseStyle.value.default, true]]);
|
|
|
+ if ("hover" in mouseStyle.value) {
|
|
|
+ styleMap.set(mouseStyle.value.hover, status.value.hover);
|
|
|
+ }
|
|
|
+ if ("press" in mouseStyle.value) {
|
|
|
+ styleMap.set(mouseStyle.value.press, status.value.press);
|
|
|
+ }
|
|
|
+ if ("focus" in mouseStyle.value) {
|
|
|
+ styleMap.set(mouseStyle.value.focus, status.value.active);
|
|
|
+ }
|
|
|
+ if (
|
|
|
+ "drag" in mouseStyle.value &&
|
|
|
+ transformIngShapes.value.includes(shape.value?.getNode()!)
|
|
|
+ ) {
|
|
|
+ styleMap.set(mouseStyle.value.drag, true);
|
|
|
+ }
|
|
|
+ // if ('select' in props.style) {
|
|
|
+ // styleMap.set(props.style.select, status.value.select)
|
|
|
+ // }
|
|
|
+
|
|
|
+ const finalStyle = {};
|
|
|
+ for (const [style, use] of styleMap.entries()) {
|
|
|
+ use && Object.assign(finalStyle as any, style);
|
|
|
+ }
|
|
|
+ return finalStyle;
|
|
|
+ };
|
|
|
+ const style = ref();
|
|
|
+ watchEffect(() => {
|
|
|
+ const newStyle = getStyle();
|
|
|
+ if (inRevise(newStyle, style.value)) {
|
|
|
+ style.value = newStyle;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return { currentStyle: style, status, shape };
|
|
|
+};
|
|
|
+
|
|
|
+export const useAnimationMouseStyle = <T extends ShapeType>(
|
|
|
+ props: MouseStyleProps<T>
|
|
|
+) => {
|
|
|
+ const { currentStyle } = useMouseStyle(props);
|
|
|
+ return useAniamtion(currentStyle as any);
|
|
|
+};
|