|
@@ -0,0 +1,228 @@
|
|
|
+import { Rect } from "konva/lib/shapes/Rect";
|
|
|
+import {
|
|
|
+ globalWatch,
|
|
|
+ globalWatchEffect,
|
|
|
+ installGlobalVar,
|
|
|
+ useMountParts,
|
|
|
+ useStage,
|
|
|
+} from "./use-global-vars";
|
|
|
+import { useFormalLayer, useHelperLayer } from "./use-layer";
|
|
|
+import { themeColor } from "@/constant/help-style";
|
|
|
+import { dragListener } from "@/utils/event";
|
|
|
+import { Layer } from "konva/lib/Layer";
|
|
|
+import { useOperMode } from "./use-status";
|
|
|
+import { computed, markRaw, ref, watch, watchEffect } from "vue";
|
|
|
+import { EntityShape } from "@/deconstruction";
|
|
|
+import { Util } from "konva/lib/Util";
|
|
|
+import { useViewerInvertTransform, useViewerTransform } from "./use-viewer";
|
|
|
+import { mergeFuns, onlyId } from "@/utils/shared";
|
|
|
+import { IRect } from "konva/lib/types";
|
|
|
+import { useMouseShapesStatus } from "./use-mouse-status";
|
|
|
+import { Pos } from "@/utils/math";
|
|
|
+import Icon from "../components/icon/temp-icon.vue";
|
|
|
+import { Group } from "konva/lib/Group";
|
|
|
+import { Component as GroupComp } from "../components/group/";
|
|
|
+import { useStore } from "../store";
|
|
|
+
|
|
|
+export const useSelection = installGlobalVar(() => {
|
|
|
+ const layer = useHelperLayer();
|
|
|
+ const formatLayer = useFormalLayer();
|
|
|
+ const box = new Rect({
|
|
|
+ stroke: themeColor,
|
|
|
+ strokeWidth: 1,
|
|
|
+ fill: "#fff",
|
|
|
+ listening: false,
|
|
|
+ opacity: 0.5,
|
|
|
+ });
|
|
|
+ const stage = useStage();
|
|
|
+ const operMode = useOperMode();
|
|
|
+ const selections = ref<EntityShape[]>();
|
|
|
+ const viewMat = useViewerTransform();
|
|
|
+ const store = useStore();
|
|
|
+
|
|
|
+ let shapeBoxs: IRect[] = [];
|
|
|
+ let shpaes: EntityShape[] = []
|
|
|
+
|
|
|
+ const init = (dom: HTMLDivElement, layer: Layer) => {
|
|
|
+ const stopListener = dragListener(dom, {
|
|
|
+ down(pos) {
|
|
|
+ layer.add(box);
|
|
|
+ box.x(pos.x);
|
|
|
+ box.y(pos.y);
|
|
|
+ box.width(0);
|
|
|
+ box.height(0);
|
|
|
+ },
|
|
|
+ move({ end }) {
|
|
|
+ box.width(end.x - box.x());
|
|
|
+ box.height(end.y - box.y());
|
|
|
+
|
|
|
+ const boxRect = box.getClientRect();
|
|
|
+ selections.value = [];
|
|
|
+ for (let i = 0; i < shapeBoxs.length; i++) {
|
|
|
+ if (Util.haveIntersection(boxRect, shapeBoxs[i])) {
|
|
|
+ selections.value!.push(shpaes[i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ up() {
|
|
|
+ selections.value = undefined;
|
|
|
+ box.remove();
|
|
|
+ },
|
|
|
+ });
|
|
|
+ return () => {
|
|
|
+ stopListener();
|
|
|
+ box.remove();
|
|
|
+ };
|
|
|
+ };
|
|
|
+
|
|
|
+ const stopWatch = mergeFuns(
|
|
|
+ globalWatchEffect((onCleanup) => {
|
|
|
+ const dom = stage.value?.getNode().container();
|
|
|
+ if (dom && operMode.value.mulSelection && layer.value) {
|
|
|
+ onCleanup(init(dom, layer.value));
|
|
|
+ }
|
|
|
+ }),
|
|
|
+ globalWatch(
|
|
|
+ () => [viewMat.value, operMode.value.mulSelection],
|
|
|
+ () => {
|
|
|
+ if (operMode.value.mulSelection) {
|
|
|
+ shpaes = formatLayer.value!.children.filter((shape) => store.getItemById(shape.id()))
|
|
|
+ shapeBoxs = shpaes.map((shape) => shape.getClientRect());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ );
|
|
|
+
|
|
|
+ return {
|
|
|
+ onDestroy: stopWatch,
|
|
|
+ var: selections,
|
|
|
+ };
|
|
|
+});
|
|
|
+
|
|
|
+export const useSelectionShowIcons = installGlobalVar(() => {
|
|
|
+ const mParts = useMountParts();
|
|
|
+ const iconProps = {
|
|
|
+ width: 20,
|
|
|
+ height: 20,
|
|
|
+ zIndex: 99999,
|
|
|
+ url: "/icons/state_s.svg",
|
|
|
+ fill: themeColor,
|
|
|
+ stroke: "#fff",
|
|
|
+ };
|
|
|
+
|
|
|
+ const status = useMouseShapesStatus();
|
|
|
+ const formatLayer = useFormalLayer();
|
|
|
+ const mouseSelects = computed(() => {
|
|
|
+ const selectShapes = status.selects.filter((shape) =>
|
|
|
+ formatLayer.value?.children.includes(shape)
|
|
|
+ );
|
|
|
+ return selectShapes;
|
|
|
+ });
|
|
|
+
|
|
|
+ watchEffect(() => {
|
|
|
+ if (mouseSelects.value.length !== status.selects.length) {
|
|
|
+ status.selects = mouseSelects.value;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ const rectSelects = useSelection();
|
|
|
+ let initSelections: EntityShape[] = [];
|
|
|
+ watch(
|
|
|
+ () => rectSelects.value && [...rectSelects.value],
|
|
|
+ (rectSelects, oldRectSelects) => {
|
|
|
+ if (!oldRectSelects) {
|
|
|
+ initSelections = [...mouseSelects.value];
|
|
|
+ } else if (!rectSelects) {
|
|
|
+ initSelections = [];
|
|
|
+ } else {
|
|
|
+ status.selects = Array.from(
|
|
|
+ new Set([...initSelections, ...rectSelects])
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ const selectionCenters = ref<Pos[]>([]);
|
|
|
+ const invMat = useViewerInvertTransform()
|
|
|
+ watch(
|
|
|
+ () => [invMat.value, status.selects],
|
|
|
+ (_a, _b, onCleanup) => {
|
|
|
+ selectionCenters.value = [];
|
|
|
+ onCleanup(mergeFuns(status.selects.map((shape, ndx) => {
|
|
|
+ const set = () => {
|
|
|
+ const rect = shape.getClientRect();
|
|
|
+ selectionCenters.value[ndx] = invMat.value.point({
|
|
|
+ x: rect.x + rect.width / 2,
|
|
|
+ y: rect.y + rect.height / 2,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ set()
|
|
|
+ shape.on('bound-change', set)
|
|
|
+ return () => shape.off('bound-change', set)
|
|
|
+ })))
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ watchEffect((onCleanup) => {
|
|
|
+ onCleanup(
|
|
|
+ mergeFuns(
|
|
|
+ selectionCenters.value.map((center) =>
|
|
|
+ mParts.add({
|
|
|
+ comp: markRaw(Icon),
|
|
|
+ props: {
|
|
|
+ data: { ...iconProps, mat: [1, 0, 0, 1, center.x, center.y] },
|
|
|
+ },
|
|
|
+ })
|
|
|
+ )
|
|
|
+ )
|
|
|
+ );
|
|
|
+ });
|
|
|
+
|
|
|
+ return computed({
|
|
|
+ get: () => status.selects,
|
|
|
+ set: (val: EntityShape[]) => (status.selects = val),
|
|
|
+ });
|
|
|
+});
|
|
|
+
|
|
|
+export const useSelectionRevise = () => {
|
|
|
+ const mParts = useMountParts();
|
|
|
+ const selects = useSelectionShowIcons();
|
|
|
+
|
|
|
+ const ids = computed(() => selects.value.map((item) => item.id()));
|
|
|
+ const groupConfig = {
|
|
|
+ id: onlyId(),
|
|
|
+ createTime: Date.now(),
|
|
|
+ zIndex: 9999,
|
|
|
+ lock: false,
|
|
|
+ opacity: 1,
|
|
|
+ ref: false,
|
|
|
+ listening: false,
|
|
|
+ };
|
|
|
+ const status = useMouseShapesStatus();
|
|
|
+ const operMode = useOperMode();
|
|
|
+ const layer = useFormalLayer();
|
|
|
+ watch(
|
|
|
+ () => [!!ids.value.length, operMode.value.mulSelection],
|
|
|
+ () => {
|
|
|
+ const groupShape = layer.value?.findOne<Group>(`#${groupConfig.id}`);
|
|
|
+ if (!groupShape) return;
|
|
|
+ if (ids.value.length && !operMode.value.mulSelection) {
|
|
|
+ status.actives = [groupShape];
|
|
|
+ } else if (status.actives.includes(groupShape)) {
|
|
|
+ status.actives = [];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+
|
|
|
+ watchEffect((onCleanup) => {
|
|
|
+ if (ids.value.length) {
|
|
|
+ onCleanup(
|
|
|
+ mParts.add({
|
|
|
+ comp: markRaw(GroupComp),
|
|
|
+ props: { data: { ...groupConfig, ids: ids.value } },
|
|
|
+ })
|
|
|
+ );
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|