icon.vue 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <template>
  2. <TempIcon :data="tData" :ref="(e: any) => shape = e?.shape" />
  3. <PropertyUpdate
  4. :describes="describes"
  5. :data="data"
  6. :target="shape"
  7. @delete="emit('delShape')"
  8. @change="emit('updateShape', { ...data })"
  9. />
  10. <Operate :target="shape" :menus="operateMenus" />
  11. </template>
  12. <script lang="ts" setup>
  13. import TempIcon from "./temp-icon.vue";
  14. import {
  15. LineIconData,
  16. getMouseStyle,
  17. defaultStyle,
  18. matResponse,
  19. getLineIconMat,
  20. getSnapLine,
  21. getLineIconEndpoints,
  22. } from "./index.ts";
  23. import { useComponentStatus } from "@/core/hook/use-component.ts";
  24. import { PropertyUpdate, Operate } from "../../html-mount/propertys/index.ts";
  25. import { Transform } from "konva/lib/Util";
  26. import {
  27. useCustomTransformer,
  28. useGetTransformerOperType,
  29. } from "@/core/hook/use-transformer.ts";
  30. import { Group } from "konva/lib/Group";
  31. import { Rect } from "konva/lib/shapes/Rect";
  32. import { setShapeTransform } from "@/utils/shape.ts";
  33. import { useStore } from "@/core/store/index.ts";
  34. import { usePointerPos } from "@/core/hook/use-global-vars.ts";
  35. import { useViewerInvertTransform } from "@/core/hook/use-viewer.ts";
  36. import { computed, nextTick, watch } from "vue";
  37. import { useHistory } from "@/core/hook/use-history.ts";
  38. import {
  39. eqPoint,
  40. line2IncludedAngle,
  41. lineInner,
  42. lineLen,
  43. lineVector,
  44. Pos,
  45. } from "@/utils/math.ts";
  46. import { copy } from "@/utils/shared.ts";
  47. const props = defineProps<{ data: LineIconData }>();
  48. const emit = defineEmits<{
  49. (e: "updateShape", value: LineIconData): void;
  50. (e: "addShape", value: LineIconData): void;
  51. (e: "delShape"): void;
  52. }>();
  53. const store = useStore();
  54. const getOperType = useGetTransformerOperType();
  55. const viewMat = useViewerInvertTransform();
  56. const pos = usePointerPos();
  57. const { shape, tData, data, operateMenus, describes } = useComponentStatus({
  58. emit,
  59. props,
  60. getMouseStyle,
  61. transformType: "custom",
  62. selfData: true,
  63. customTransform(callback, shape, data) {
  64. let prevInvMat: Transform;
  65. return useCustomTransformer(shape, data, {
  66. getRepShape() {
  67. const group = new Group();
  68. const rect = new Rect();
  69. group.add(rect);
  70. const update = () => {
  71. const mat = getLineIconMat(getSnapLine(store, data.value)!, data.value);
  72. const width = Math.abs(data.value.endLen - data.value.startLen);
  73. const height = data.value.height;
  74. prevInvMat = mat;
  75. rect.width(width);
  76. rect.height(height);
  77. rect.offset({ x: width / 2, y: height / 2 });
  78. setShapeTransform(group, mat);
  79. };
  80. update();
  81. return { shape: group, update };
  82. },
  83. handler(data, mat) {
  84. if (pos.value && !getOperType()) {
  85. const rpos = viewMat.value.point(pos.value);
  86. const m = mat.m;
  87. m[4] = rpos.x;
  88. m[5] = rpos.y;
  89. mat = new Transform(m);
  90. }
  91. matResponse({
  92. data,
  93. mat: mat,
  94. operType: getOperType(),
  95. store,
  96. });
  97. return true;
  98. },
  99. callback,
  100. openSnap: false,
  101. transformerConfig: {
  102. flipEnabled: true,
  103. rotateEnabled: false,
  104. enabledAnchors: ["middle-left", "middle-right"],
  105. boundBoxFunc: (oldBox, newBox) => {
  106. if (newBox.width < 5) {
  107. return oldBox;
  108. } else {
  109. return newBox;
  110. }
  111. },
  112. },
  113. });
  114. },
  115. defaultStyle,
  116. copyHandler(_, data) {
  117. const snapLine = getSnapLine(store, data)!;
  118. const line = getLineIconEndpoints(getSnapLine(store, data)!, data);
  119. const vector = lineVector(line);
  120. const move = vector.multiplyScalar(lineLen(line[0], line[1]));
  121. const mat = new Transform()
  122. .translate(move.x, move.y)
  123. .multiply(getLineIconMat(snapLine, data));
  124. return matResponse({ data, mat, store });
  125. },
  126. propertys: ["name", "fill", "stroke", "strokeWidth"],
  127. });
  128. const line = computed(() => getSnapLine(store, props.data));
  129. const history = useHistory();
  130. watch(
  131. () => line.value && (copy(line.value) as Pos[]),
  132. (line, oldLine) => {
  133. history.preventTrack(() => {
  134. if (!line) {
  135. console.error("找不到line", props.data);
  136. return emit("delShape");
  137. }
  138. if (!oldLine) return;
  139. const eq0 = eqPoint(oldLine[0], line[0]);
  140. const eq1 = eqPoint(oldLine[1], line[1]);
  141. if (eq0 !== eq1) {
  142. // 联动
  143. const startNdx = eq0 ? 0 : 1;
  144. const endNdx = eq0 ? 1 : 0;
  145. const rotate = line2IncludedAngle(
  146. [oldLine[startNdx], oldLine[endNdx]],
  147. [line[startNdx], line[endNdx]]
  148. );
  149. const mat = new Transform()
  150. .translate(line[startNdx].x, line[startNdx].y)
  151. .rotate(rotate)
  152. .translate(-line[startNdx].x, -line[startNdx].y);
  153. const endPoints = getLineIconEndpoints(oldLine, data.value).map((p) =>
  154. mat.point(p)
  155. );
  156. if (lineInner(line, endPoints[0]) && lineInner(line, endPoints[1])) {
  157. emit("updateShape", {
  158. ...data.value,
  159. startLen: lineLen(line[0], endPoints[0]),
  160. endLen: lineLen(line[0], endPoints[1]),
  161. });
  162. } else {
  163. emit("delShape");
  164. return;
  165. }
  166. }
  167. nextTick(() => {
  168. shape.value?.getNode().fire("bound-change");
  169. });
  170. });
  171. },
  172. { immediate: true, flush: "post" }
  173. );
  174. if (props.data.type === "align-bottom") {
  175. operateMenus.splice(
  176. operateMenus.length - 1,
  177. 0,
  178. {
  179. label: "内外翻转",
  180. handler: () => {
  181. emit("updateShape", {
  182. ...data.value,
  183. openSide: data.value.openSide === "LEFT" ? "RIGHT" : "LEFT",
  184. });
  185. },
  186. },
  187. {
  188. label: "左右翻转",
  189. handler: () => {
  190. emit("updateShape", {
  191. ...data.value,
  192. openSide: data.value.openSide === "LEFT" ? "RIGHT" : "LEFT",
  193. startLen: data.value.endLen,
  194. endLen: data.value.startLen,
  195. });
  196. },
  197. }
  198. );
  199. }
  200. </script>