single-line.vue 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. <template>
  2. <EditLine
  3. :ref="(d: any) => shape = d?.shape"
  4. :data="{ ...line, ...style, lineJoin: 'miter' }"
  5. :opacity="isDrawIng ? 0.5 : 1"
  6. :points="points"
  7. :closed="false"
  8. :id="line.id"
  9. :disablePoint="!canEdit || mode.include(Mode.readonly)"
  10. :ndx="0"
  11. @dragstart="emit('dragLineStart', props.line)"
  12. @update:line="(ps) => emit('dragLine', props.line, ps)"
  13. @dragend="emit('dragLineEnd', props.line)"
  14. @add-point="addPoint"
  15. />
  16. <template v-for="polygons in joinsPolygons">
  17. <v-line v-for="config in polygons" :config="config" />
  18. </template>
  19. <SizeLine
  20. v-if="
  21. status.active ||
  22. config.showComponentSize ||
  23. isDrawIng ||
  24. dragPointIds?.includes(line.a) ||
  25. dragPointIds?.includes(line.b)
  26. "
  27. :points="points"
  28. :strokeWidth="style.strokeWidth"
  29. :stroke="style.stroke"
  30. />
  31. <template v-if="(!mode.include(Mode.readonly) && canEdit) || isDrawIng">
  32. <EditPoint
  33. v-for="(point, ndx) in points"
  34. :key="point.id"
  35. :size="line.strokeWidth"
  36. :points="points"
  37. :opacity="0.1"
  38. :drawIng="ndx === 0 && isDrawIng"
  39. :ndx="ndx"
  40. :closed="false"
  41. :id="line.id"
  42. :disable="addMode"
  43. :color="isDrawIng ? themeColor : style.stroke"
  44. @dragstart="dragstartHandler([point.id])"
  45. @update:position="(p) => emit('updatePoint', { ...point, ...p })"
  46. @dragend="dragendHandler"
  47. @delete="delPoint(point)"
  48. />
  49. </template>
  50. <PropertyUpdate
  51. :describes="describes"
  52. :data="line"
  53. :target="shape"
  54. :name="shapeName"
  55. @change="
  56. () => {
  57. isStartChange || emit('updateBefore', []);
  58. emit('updateLine', { ...line });
  59. emit('update');
  60. isStartChange = false;
  61. }
  62. "
  63. @delete="delHandler"
  64. />
  65. <Operate :target="shape" :menus="menus" />
  66. </template>
  67. <script lang="ts" setup>
  68. import { computed, ref, watchEffect } from "vue";
  69. import { getMouseStyle, getSnapInfos, LineData, shapeName } from "./index.ts";
  70. import { flatPositions, onlyId } from "@/utils/shared.ts";
  71. import EditLine from "../share/edit-line.vue";
  72. import { getVectorLine, lineCenter, lineLen, lineVector, Pos } from "@/utils/math.ts";
  73. import { Line } from "konva/lib/shapes/Line";
  74. import { DC } from "@/deconstruction.js";
  75. import { useMode } from "@/core/hook/use-status.ts";
  76. import { Mode } from "@/constant/mode.ts";
  77. import SizeLine from "../share/size-line.vue";
  78. import { useConfig } from "@/core/hook/use-config.ts";
  79. import { ComponentSnapInfo } from "../index.ts";
  80. import { useCustomSnapInfos } from "@/core/hook/use-snap.ts";
  81. import { mergeDescribes } from "@/core/html-mount/propertys/index.ts";
  82. import { PropertyUpdate, Operate } from "../../html-mount/propertys/index.ts";
  83. import {
  84. useAnimationMouseStyle,
  85. useMouseShapeStatus,
  86. } from "@/core/hook/use-mouse-status.ts";
  87. import { themeColor } from "@/constant";
  88. import { Vector2 } from "three";
  89. import { extendLinesOverlap } from "./attach-view.ts";
  90. import EditPoint from "../share/edit-point.vue";
  91. const mode = useMode();
  92. const props = defineProps<{
  93. line: LineData["lines"][number];
  94. addMode?: boolean;
  95. canEdit?: boolean;
  96. data: LineData;
  97. dragPointIds?: string[];
  98. }>();
  99. const joinsPolygons = extendLinesOverlap(props.data, props.line);
  100. const emit = defineEmits<{
  101. (e: "updatePoint", value: LineData["points"][number]): void;
  102. (e: "addPoint", value: LineData["points"][number]): void;
  103. (e: "delPoint", value: LineData["points"][number]): void;
  104. (e: "delLine"): void;
  105. (e: "updateLine", value: LineData["lines"][number]): void;
  106. (e: "updateBefore", value: string[]): void;
  107. (e: "update"): void;
  108. (e: "dragLineStart", value: LineData["lines"][number]): void;
  109. (e: "dragLine", line: LineData["lines"][number], move: Pos[]): void;
  110. (e: "dragLineEnd", value: LineData["lines"][number]): void;
  111. }>();
  112. const shape = ref<DC<Line>>();
  113. const points = computed(() => [
  114. props.data.points.find((p) => p.id === props.line.a)!,
  115. props.data.points.find((p) => p.id === props.line.b)!,
  116. ]);
  117. const lineData = computed(() => props.line);
  118. const describes = mergeDescribes(lineData, {}, ["stroke", "strokeWidth"]);
  119. const d = describes as any;
  120. d.strokeWidth.props = {
  121. ...d.strokeWidth.props,
  122. proportion: true,
  123. };
  124. d.strokeWidth.label = "粗细";
  125. d.stroke.label = "颜色";
  126. let isStartChange = false;
  127. let setLineVector: Vector2;
  128. describes.length = {
  129. type: "inputNum",
  130. label: "线段长度",
  131. "layout-type": "row",
  132. get value() {
  133. return lineLen(points.value[0], points.value[1]);
  134. },
  135. set value(val) {
  136. if (!isStartChange) {
  137. emit("updateBefore", [props.line.a, props.line.b]);
  138. setLineVector = lineVector(points.value);
  139. }
  140. isStartChange = true;
  141. const aCount = props.data.lines.filter(
  142. (line) => line.a === points.value[0].id || line.b === points.value[0].id
  143. ).length;
  144. const bCount = props.data.lines.filter(
  145. (line) => line.a === points.value[1].id || line.b === points.value[1].id
  146. ).length;
  147. if (aCount === bCount || (aCount > 1 && bCount > 1)) {
  148. // 两端伸展
  149. const center = lineCenter(points.value);
  150. const l1 = getVectorLine(setLineVector.clone().multiplyScalar(-1), center, val / 2);
  151. const l2 = getVectorLine(setLineVector, center, val / 2);
  152. emit("updatePoint", { ...points.value[0], ...l1[1] });
  153. emit("updatePoint", { ...points.value[1], ...l2[1] });
  154. } else {
  155. // 单端伸展
  156. const changeNdx = aCount > 1 ? 1 : 0;
  157. const start = points.value[aCount > 1 ? 0 : 1];
  158. const lineVec =
  159. aCount > 1 ? setLineVector : setLineVector.clone().multiplyScalar(-1);
  160. const line = getVectorLine(lineVec, start, val);
  161. emit("updatePoint", { ...points.value[changeNdx], ...line[1] });
  162. }
  163. },
  164. props: { proportion: true },
  165. };
  166. const delHandler = () => {
  167. emit("updateBefore", [props.line.a, props.line.b]);
  168. emit("delLine");
  169. emit("update");
  170. };
  171. const menus = [
  172. {
  173. label: "删除",
  174. handler: delHandler,
  175. },
  176. ];
  177. const status = useMouseShapeStatus(shape);
  178. const [mstyle] = useAnimationMouseStyle({
  179. shape,
  180. getMouseStyle,
  181. data: lineData as any,
  182. });
  183. const isDrawIng = computed(
  184. () =>
  185. props.addMode && props.data.lines.indexOf(props.line) === props.data.lines.length - 1
  186. );
  187. const style = computed(() =>
  188. isDrawIng.value ? { ...mstyle.value, stroke: themeColor } : mstyle.value
  189. );
  190. const addPoint = (pos: Pos) => {
  191. emit("updateBefore", []);
  192. emit("addPoint", { ...points.value[0], ...pos, id: onlyId() });
  193. emit("update");
  194. };
  195. const config = useConfig();
  196. const delPoint = (point: LineData["points"][number]) => {
  197. emit("updateBefore", []);
  198. emit("delPoint", point);
  199. emit("update");
  200. };
  201. const infos = useCustomSnapInfos();
  202. let snapInfos: ComponentSnapInfo[];
  203. const dragstartHandler = (eIds: string[]) => {
  204. emit("updateBefore", eIds);
  205. snapInfos = getSnapInfos({
  206. ...props.data,
  207. lines: props.data.lines.filter(
  208. (item) => !(eIds.includes(item.a) || eIds.includes(item.b))
  209. ),
  210. points: props.data.points.filter((item) => !eIds.includes(item.id)),
  211. });
  212. snapInfos.forEach((item) => {
  213. infos.add(item);
  214. });
  215. };
  216. const dragendHandler = () => {
  217. emit("update");
  218. snapInfos.forEach((item) => infos.remove(item));
  219. };
  220. // const padstart = computed(() => {
  221. // props.line.
  222. // })
  223. </script>