|
@@ -0,0 +1,237 @@
|
|
|
+<template>
|
|
|
+ <template v-if="axissInfo">
|
|
|
+ <v-group v-for="info in axissInfo" :config="{ listening: false }">
|
|
|
+ <v-line :config="{ points: info.points, ...style }" />
|
|
|
+ <v-text
|
|
|
+ :config="{ ...style, strokeWidth: 0, ...textConfig, fontSize, align: 'center' }"
|
|
|
+ v-for="textConfig in info.texts"
|
|
|
+ />
|
|
|
+ </v-group>
|
|
|
+ </template>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="ts" setup>
|
|
|
+import { computed, onUnmounted } from "vue";
|
|
|
+import { components } from "../components";
|
|
|
+import { useComponentsAttach } from "../hook/use-component";
|
|
|
+import { useViewerTransform } from "../hook/use-viewer";
|
|
|
+import { useResize } from "../hook/use-event";
|
|
|
+import { Pos } from "@/utils/math";
|
|
|
+import { TextConfig } from "konva/lib/shapes/Text";
|
|
|
+import { Transform } from "konva/lib/Util";
|
|
|
+
|
|
|
+const style = {
|
|
|
+ stroke: "#000",
|
|
|
+ strokeWidth: 1,
|
|
|
+ opacity: 1,
|
|
|
+ strokeScaleEnabled: false,
|
|
|
+ shadowColor: "#fff",
|
|
|
+ shadowBlur: 3,
|
|
|
+ shadowOffset: { x: 0, y: 0 },
|
|
|
+ shadowOpacity: 1,
|
|
|
+};
|
|
|
+
|
|
|
+const props = withDefaults(
|
|
|
+ defineProps<{
|
|
|
+ splitOffset?: number;
|
|
|
+ showOffset?: number;
|
|
|
+ splitWidth?: number;
|
|
|
+ }>(),
|
|
|
+ { splitOffset: 80, showOffset: 20, splitWidth: 10 }
|
|
|
+);
|
|
|
+
|
|
|
+const size = useResize();
|
|
|
+const center = computed(() => {
|
|
|
+ if (!size.value) return;
|
|
|
+ return {
|
|
|
+ x: size.value.width / 2,
|
|
|
+ y: size.value.height / 2,
|
|
|
+ };
|
|
|
+});
|
|
|
+const rang = computed(() => {
|
|
|
+ if (!size.value) return;
|
|
|
+ return {
|
|
|
+ lt: { x: props.showOffset, y: props.showOffset },
|
|
|
+ rb: {
|
|
|
+ x: size.value.width - props.showOffset,
|
|
|
+ y: size.value.height - props.showOffset,
|
|
|
+ },
|
|
|
+ };
|
|
|
+});
|
|
|
+
|
|
|
+const compsInfo = useComponentsAttach(
|
|
|
+ (type, item) => components[type].getSnapPoints(item as any)
|
|
|
+ // ["polygon"]
|
|
|
+);
|
|
|
+onUnmounted(compsInfo.cleanup);
|
|
|
+
|
|
|
+const viewerTransform = useViewerTransform();
|
|
|
+const points = computed(() => compsInfo.attachs.flatMap((p) => p));
|
|
|
+const rectAxisSteps = computed(() => {
|
|
|
+ const axis = {
|
|
|
+ top: [] as number[],
|
|
|
+ left: [] as number[],
|
|
|
+ right: [] as number[],
|
|
|
+ bottom: [] as number[],
|
|
|
+ };
|
|
|
+ if (!center.value || !rang.value) return axis;
|
|
|
+
|
|
|
+ const pushStep = (axis: number[], cur: number, pixel: Pos) => {
|
|
|
+ if (
|
|
|
+ !(
|
|
|
+ pixel.x >= rang.value!.lt.x &&
|
|
|
+ pixel.x <= rang.value!.rb.x &&
|
|
|
+ pixel.y >= rang.value!.lt.y &&
|
|
|
+ pixel.y <= rang.value!.rb.y
|
|
|
+ )
|
|
|
+ ) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const can = (data: number) => Math.abs(data - cur) > props.splitOffset;
|
|
|
+
|
|
|
+ let i = axis.length - 1;
|
|
|
+ for (i; i >= 0; i--) {
|
|
|
+ if (axis[i] < cur) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (axis.length === 0) {
|
|
|
+ axis.push(cur);
|
|
|
+ } else if (can(axis[i]) && (i === axis.length - 1 || can(axis[i + 1]))) {
|
|
|
+ axis.splice(i + 1, 0, cur);
|
|
|
+ } else if (i === -1) {
|
|
|
+ axis[0] = cur;
|
|
|
+ } else if (i === axis.length - 1) {
|
|
|
+ axis[i] = cur;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ for (const point of points.value) {
|
|
|
+ const pixel = viewerTransform.value.point(point);
|
|
|
+ pushStep(pixel.x > center.value.x ? axis.right : axis.left, pixel.y, pixel);
|
|
|
+ pushStep(pixel.y > center.value.y ? axis.bottom : axis.top, pixel.x, pixel);
|
|
|
+ }
|
|
|
+ return axis;
|
|
|
+});
|
|
|
+
|
|
|
+const fontSize = 12;
|
|
|
+const axissInfo = computed(() => {
|
|
|
+ if (!rang.value) return;
|
|
|
+ const infos: Record<string, { points: number[]; texts: TextConfig[] }> = {
|
|
|
+ left: { points: [], texts: [] },
|
|
|
+ right: { points: [], texts: [] },
|
|
|
+ top: { points: [], texts: [] },
|
|
|
+ bottom: { points: [], texts: [] },
|
|
|
+ };
|
|
|
+
|
|
|
+ if (rectAxisSteps.value.left.length > 1) {
|
|
|
+ for (let i = 0; i < rectAxisSteps.value.left.length; i++) {
|
|
|
+ const cur = rectAxisSteps.value.left[i];
|
|
|
+ infos.left.points.push(rang.value!.lt.x, cur);
|
|
|
+ infos.left.points.push(rang.value!.lt.x + props.splitWidth, cur);
|
|
|
+ infos.left.points.push(rang.value!.lt.x, cur);
|
|
|
+
|
|
|
+ if (i === 0) continue;
|
|
|
+ const prev = rectAxisSteps.value.left[i - 1];
|
|
|
+ const lt = {
|
|
|
+ x: rang.value!.lt.x + fontSize / 2,
|
|
|
+ y: prev,
|
|
|
+ };
|
|
|
+ const width = cur - prev;
|
|
|
+ const height = fontSize;
|
|
|
+ const center = { x: 0, y: height / 2 };
|
|
|
+
|
|
|
+ const tf = new Transform()
|
|
|
+ .translate(center.x + lt.x, center.y + lt.y)
|
|
|
+ .rotate(Math.PI / 2)
|
|
|
+ .translate(-center.x, -center.y);
|
|
|
+
|
|
|
+ infos.left.texts.push({
|
|
|
+ width: width,
|
|
|
+ text: Math.floor(width * 100).toString(),
|
|
|
+ ...tf.decompose(),
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (rectAxisSteps.value.right.length > 1) {
|
|
|
+ for (let i = 0; i < rectAxisSteps.value.right.length; i++) {
|
|
|
+ const cur = rectAxisSteps.value.right[i];
|
|
|
+ infos.right.points.push(rang.value!.rb.x, cur);
|
|
|
+ infos.right.points.push(rang.value!.rb.x - props.splitWidth, cur);
|
|
|
+ infos.right.points.push(rang.value!.rb.x, cur);
|
|
|
+
|
|
|
+ if (i === 0) continue;
|
|
|
+ const prev = rectAxisSteps.value.right[i - 1];
|
|
|
+ const lt = {
|
|
|
+ x: rang.value!.rb.x - fontSize / 1.5,
|
|
|
+ y: prev,
|
|
|
+ };
|
|
|
+ const width = cur - prev;
|
|
|
+ const height = fontSize;
|
|
|
+ const center = { x: 0, y: height / 2 };
|
|
|
+
|
|
|
+ const tf = new Transform()
|
|
|
+ .translate(center.x + lt.x, center.y + lt.y)
|
|
|
+ .rotate(Math.PI / 2)
|
|
|
+ .translate(-center.x, -center.y);
|
|
|
+
|
|
|
+ infos.right.texts.push({
|
|
|
+ width: width,
|
|
|
+ text: Math.floor(width * 100).toString(),
|
|
|
+ ...tf.decompose(),
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (rectAxisSteps.value.top.length > 1) {
|
|
|
+ for (let i = 0; i < rectAxisSteps.value.top.length; i++) {
|
|
|
+ const cur = rectAxisSteps.value.top[i];
|
|
|
+ infos.top.points.push(cur, rang.value!.lt.y);
|
|
|
+ infos.top.points.push(cur, rang.value!.lt.y + props.splitWidth);
|
|
|
+ infos.top.points.push(cur, rang.value!.lt.y);
|
|
|
+
|
|
|
+ if (i === 0) continue;
|
|
|
+ const prev = rectAxisSteps.value.top[i - 1];
|
|
|
+ const lt = {
|
|
|
+ x: prev,
|
|
|
+ y: rang.value!.lt.y + fontSize / 3,
|
|
|
+ };
|
|
|
+
|
|
|
+ const width = cur - prev;
|
|
|
+ infos.top.texts.push({
|
|
|
+ width: width,
|
|
|
+ text: Math.floor(width * 100).toString(),
|
|
|
+ ...lt,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (rectAxisSteps.value.bottom.length > 1) {
|
|
|
+ for (let i = 0; i < rectAxisSteps.value.bottom.length; i++) {
|
|
|
+ const cur = rectAxisSteps.value.bottom[i];
|
|
|
+ infos.bottom.points.push(cur, rang.value!.rb.y);
|
|
|
+ infos.bottom.points.push(cur, rang.value!.rb.y - props.splitWidth);
|
|
|
+ infos.bottom.points.push(cur, rang.value!.rb.y);
|
|
|
+
|
|
|
+ if (i === 0) continue;
|
|
|
+ const prev = rectAxisSteps.value.bottom[i - 1];
|
|
|
+ const lt = {
|
|
|
+ x: prev,
|
|
|
+ y: rang.value!.rb.y - fontSize,
|
|
|
+ };
|
|
|
+
|
|
|
+ const width = cur - prev;
|
|
|
+ infos.bottom.texts.push({
|
|
|
+ width: width,
|
|
|
+ text: Math.floor(width * 100).toString(),
|
|
|
+ ...lt,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return infos;
|
|
|
+});
|
|
|
+</script>
|