123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- import {
- installGlobalVar,
- useCan,
- useMode,
- useStage,
- } from "./use-global-vars.ts";
- import { DrawItem, ShapeType } from "../components";
- import { reactive, Ref, ref, watch, watchEffect } from "vue";
- import { Pos } from "../../utils/math.ts";
- import { clickListener, getOffset, listener } from "../../utils/event.ts";
- import { mergeFuns } from "../../utils/shared.ts";
- import { Mode } from "@/constant/mode.ts";
- export type InteractivePreset<T extends ShapeType = ShapeType> = {
- type: T;
- callback?: () => void;
- preset?: Partial<DrawItem<T>>;
- operate?: {
- immediate?: boolean;
- single?: boolean;
- data?: any;
- };
- };
- export const useInteractiveProps = installGlobalVar(
- () => ref<InteractivePreset | undefined>(),
- Symbol("interactiveProps")
- );
- export type Area = [Pos, Pos];
- export enum InteractiveAction {
- delete,
- }
- export type InteractiveMessage = {
- area?: Area;
- dot?: Pos;
- ndx?: number;
- action?: InteractiveAction;
- };
- export type InteractiveAreas = ReturnType<typeof useInteractiveAreas>;
- export type InteractiveDots = ReturnType<typeof useInteractiveDots>;
- export type Interactive = InteractiveAreas | InteractiveDots;
- const useInteractiveExpose = <T extends object>(
- messages: Ref<T[]>,
- init: (dom: HTMLDivElement) => () => void,
- singleDone: Ref<boolean>,
- isRunning: Ref<boolean>,
- quit: () => void,
- autoConsumed?: boolean
- ) => {
- const consumedMessages = reactive(new WeakSet<T>()) as WeakSet<T>;
- const stage = useStage();
- const interactiveProps = useInteractiveProps();
- watch(isRunning, (can, _, onCleanup) => {
- if (can) {
- const props = interactiveProps.value!;
- const cleanups = [] as Array<() => void>;
- if (props.operate?.single) {
- // 如果指定单次则消息中有信息,并且确定完成则马上退出
- cleanups.push(
- watchEffect(
- () => {
- if (messages.value.length > 0 && singleDone.value) {
- quit();
- props.callback && props.callback();
- }
- },
- { flush: "post" }
- )
- );
- }
- // 单纯添加
- if (props.operate?.immediate) {
- messages.value.push(props.operate.data as T);
- singleDone.value = true;
- } else {
- const $stage = stage.value!.getStage();
- const dom = $stage.container();
- cleanups.push(init(dom));
- cleanups.push(() => {
- quit();
- props.callback && props.callback();
- });
- }
- onCleanup(mergeFuns(cleanups));
- } else {
- messages.value = [];
- }
- });
- return {
- isRunning,
- get preset() {
- return interactiveProps.value?.preset;
- },
- get messages() {
- const items = messages.value;
- const result = items.filter((item) => !consumedMessages.has(item));
- autoConsumed && result.forEach((item) => consumedMessages.add(item));
- return result as T[];
- },
- getNdx(item: T) {
- return messages.value.indexOf(item);
- },
- get consumedMessage() {
- const items = messages.value;
- return items.filter((item) => consumedMessages.has(item)) as T[];
- },
- consume(items: T[]) {
- items.forEach((item) => consumedMessages.add(item));
- },
- singleDone,
- };
- };
- type UseInteractiveProps = {
- isRuning: Ref<boolean>;
- quit: () => void;
- beforeHandler?: (p: Pos) => Pos;
- shapeType?: ShapeType;
- autoConsumed?: boolean;
- };
- export const useInteractiveAreas = ({
- isRuning,
- autoConsumed,
- beforeHandler,
- quit,
- }: UseInteractiveProps) => {
- const mode = useMode();
- const can = useCan();
- const singleDone = ref(true);
- const messages = ref<Area[]>([]);
- const init = (dom: HTMLDivElement) => {
- let pushed = false;
- let pushNdx = -1;
- let downed = false;
- let tempArea: Area;
- let dragging = false;
- return mergeFuns(
- listener(dom, "pointerdown", (ev) => {
- if (!can.dragMode) return;
- const position = getOffset(ev, dom);
- if (ev.button === 0) {
- tempArea = [
- beforeHandler ? beforeHandler(position) : position,
- ] as unknown as Area;
- downed = true;
- singleDone.value = false;
- dragging = false;
- mode.add(Mode.draging);
- }
- }),
- listener(document.documentElement, "pointermove", (ev) => {
- if (!can.dragMode) return;
- const end = getOffset(ev, dom);
- const point = beforeHandler ? beforeHandler(end) : end;
- if (downed) {
- if (pushed) {
- messages.value[pushNdx]![1] = point;
- } else {
- tempArea[1] = point;
- pushed = true;
- pushNdx = messages.value.length;
- messages.value[pushNdx] = tempArea;
- }
- dragging = true;
- } else {
- tempArea = [point] as unknown as Area;
- }
- }),
- listener(dom, "pointerup", (ev) => {
- if (downed) {
- mode.del(Mode.draging);
- } else if (!dragging) return;
- if (can.dragMode) {
- const position = getOffset(ev, dom);
- messages.value[pushNdx]![1] = beforeHandler
- ? beforeHandler(position)
- : position;
- }
- pushNdx = -1;
- pushed = false;
- downed = false;
- dragging = false;
- singleDone.value = true;
- })
- );
- };
- return useInteractiveExpose(
- messages,
- init,
- singleDone,
- isRuning,
- quit,
- autoConsumed
- );
- };
- export const useInteractiveDots = ({
- autoConsumed,
- isRuning,
- quit,
- beforeHandler,
- }: UseInteractiveProps) => {
- if (autoConsumed === void 0) autoConsumed = false;
- const mode = useMode();
- const can = useCan();
- const singleDone = ref(true);
- const messages = ref<Pos[]>([]);
- const init = (dom: HTMLDivElement) => {
- if (!can.dragMode) return () => {};
- let moveIng = false;
- let pushed = false;
- const empty = { x: -9999, y: -9999 };
- const pointer = ref(empty);
- mode.add(Mode.draging);
- return mergeFuns(
- () => {
- mode.del(Mode.draging);
- },
- clickListener(dom, (_, ev) => {
- if (!moveIng || !can.dragMode) return;
- pointer.value = { ...empty };
- singleDone.value = true;
- moveIng = false;
- pushed = false;
- }),
- listener(dom, "pointermove", (ev) => {
- if (!can.dragMode) return;
- if (!pushed) {
- messages.value.push(pointer.value);
- singleDone.value = false;
- pushed = true;
- }
- moveIng = true;
- const position = getOffset(ev);
- const current = beforeHandler ? beforeHandler(position) : position;
- pointer.value.x = current.x;
- pointer.value.y = current.y;
- })
- );
- };
- return useInteractiveExpose(
- messages,
- init,
- singleDone,
- isRuning,
- quit,
- autoConsumed
- );
- };
|