edit-line.vue 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. <template>
  2. <v-line
  3. ref="line"
  4. :config="{
  5. id: id,
  6. strokeWidth: data.strokeWidth,
  7. opacity: opacity || 0,
  8. stroke: data.stroke,
  9. points: flatPositions(points),
  10. hitStrokeWidth: data.strokeWidth,
  11. }"
  12. />
  13. <v-circle
  14. v-if="!disablePoint"
  15. :config="{ ...pointStyle, ...center, opacity: isHover || isPointHover ? 1 : 0 }"
  16. ref="point"
  17. />
  18. </template>
  19. <script lang="ts" setup>
  20. import { copy, flatPositions } from "@/utils/shared";
  21. import { computed, ref, watch } from "vue";
  22. import { DC } from "@/deconstruction";
  23. import { Line } from "konva/lib/shapes/Line";
  24. import { useShapeDrag } from "@/core/hook/use-transformer";
  25. import { useShapeIsHover, useShapeClick } from "@/core/hook/use-mouse-status";
  26. import { useCursor } from "@/core/hook/use-global-vars";
  27. import { lineCenter, Pos } from "@/utils/math";
  28. import { useCustomSnapInfos, useGlobalSnapInfos, useSnap } from "@/core/hook/use-snap";
  29. import { ComponentSnapInfo } from "..";
  30. import { generateSnapInfos } from "../util";
  31. import { getMouseColors } from "@/utils/colors";
  32. import { themeColor } from "@/constant";
  33. import { Circle } from "konva/lib/shapes/Circle";
  34. import { SLineData } from "../sequent-line";
  35. import { useViewer } from "@/core/hook/use-viewer";
  36. type LData = Required<Pick<SLineData, "strokeWidth" | "stroke">>;
  37. const props = defineProps<{
  38. data: LData;
  39. points: Pos[];
  40. id: string;
  41. ndx: number;
  42. closed?: boolean;
  43. disablePoint?: boolean;
  44. opacity?: number;
  45. }>();
  46. const emit = defineEmits<{
  47. (e: "update:line", data: Pos[]): void;
  48. (e: "dragend"): void;
  49. (e: "dragstart"): void;
  50. (e: "addPoint", pos: Pos): void;
  51. }>();
  52. const line = ref<DC<Line>>();
  53. const offset = useShapeDrag(line);
  54. const viewer = useViewer();
  55. const [isHover] = useShapeIsHover(line);
  56. const cursor = useCursor();
  57. watch(isHover, (hover, _, onCleanup) => {
  58. if (hover) {
  59. onCleanup(cursor.push("./icons/m_move.png"));
  60. }
  61. });
  62. const points = computed(() => {
  63. return [props.points[props.ndx], props.points[(props.ndx + 1) % props.points.length]];
  64. });
  65. const infos = useCustomSnapInfos();
  66. const addedInfos = [] as ComponentSnapInfo[];
  67. const clearInfos = () => {
  68. addedInfos.forEach(infos.remove);
  69. };
  70. const dragStartHandler = () => {
  71. viewer.disabled.value = true;
  72. clearInfos();
  73. const ndx = props.ndx;
  74. const geos = [
  75. props.points.slice(Number(ndx === props.points.length - 1), ndx),
  76. props.points.slice(ndx + 2, props.points.length),
  77. ];
  78. if (ndx > 0 && ndx < props.points.length - 2) {
  79. geos.push([props.points[ndx - 1], props.points[ndx + 2]]);
  80. }
  81. geos.forEach((geo) => {
  82. const snapInfos = generateSnapInfos(geo, true, true, true);
  83. snapInfos.forEach((item) => {
  84. infos.add(item);
  85. addedInfos.push(item);
  86. });
  87. });
  88. };
  89. const snapInfos = useGlobalSnapInfos();
  90. const refSnapInfos = computed(() => {
  91. if (!props.id) {
  92. return snapInfos.value;
  93. } else {
  94. return snapInfos.value.filter((p) => !("id" in p) || p.id !== props.id);
  95. }
  96. });
  97. const snap = useSnap(refSnapInfos);
  98. let init: Pos[];
  99. watch(offset, (offset, oldOffsert) => {
  100. snap.clear();
  101. if (!oldOffsert) {
  102. emit("dragstart");
  103. init = copy(points.value);
  104. dragStartHandler();
  105. }
  106. if (offset) {
  107. const current = init.map((p) => ({
  108. x: p.x + offset.x,
  109. y: p.y + offset.y,
  110. }));
  111. const refSnapInfos = generateSnapInfos(current, true, true);
  112. const transform = snap.move(refSnapInfos);
  113. emit("update:line", transform ? current.map((p) => transform.point(p)) : current);
  114. } else {
  115. clearInfos();
  116. emit("dragend");
  117. viewer.disabled.value = false;
  118. }
  119. });
  120. const point = ref<DC<Circle>>();
  121. const [isPointHover] = useShapeIsHover(point);
  122. let addCursorPop: () => void;
  123. watch(isPointHover, (hover, _, onCleanup) => {
  124. if (hover) {
  125. let pop: (() => void) | null = cursor.push("./icons/m_add.png");
  126. addCursorPop = () => {
  127. pop && pop();
  128. pop = null;
  129. };
  130. onCleanup(addCursorPop);
  131. }
  132. });
  133. useShapeClick(point, () => {
  134. emit("addPoint", { ...center.value });
  135. addCursorPop();
  136. });
  137. const center = computed(() => lineCenter(points.value));
  138. const pointStyle = computed(() => {
  139. const color = getMouseColors(props.data.stroke || themeColor);
  140. const size = props.data.strokeWidth + 6 || 5;
  141. return {
  142. radius: size / 2,
  143. fill: "#fff",
  144. strokeWidth: size / 4,
  145. stroke: color.pub,
  146. };
  147. });
  148. defineExpose({
  149. get shape() {
  150. return line.value;
  151. },
  152. });
  153. </script>