icon.vue 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  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. isRangInner,
  23. } from "./index.ts";
  24. import { useComponentStatus } from "@/core/hook/use-component.ts";
  25. import { PropertyUpdate, Operate } from "../../html-mount/propertys/index.ts";
  26. import { Transform } from "konva/lib/Util";
  27. import {
  28. useCustomTransformer,
  29. useGetTransformerOperType,
  30. } from "@/core/hook/use-transformer.ts";
  31. import { Group } from "konva/lib/Group";
  32. import { Rect } from "konva/lib/shapes/Rect";
  33. import { setShapeTransform } from "@/utils/shape.ts";
  34. import { useStore } from "@/core/store/index.ts";
  35. import { usePointerPos } from "@/core/hook/use-global-vars.ts";
  36. import { useViewerInvertTransform } from "@/core/hook/use-viewer.ts";
  37. import { computed, nextTick, watch } from "vue";
  38. import { useHistory } from "@/core/hook/use-history.ts";
  39. import {
  40. eqPoint,
  41. line2IncludedAngle,
  42. lineInner,
  43. lineLen,
  44. lineVector,
  45. Pos,
  46. zeroEq,
  47. } from "@/utils/math.ts";
  48. import { copy } from "@/utils/shared.ts";
  49. import { useTestPoints } from "@/core/hook/use-debugger.ts";
  50. const props = defineProps<{ data: LineIconData }>();
  51. const emit = defineEmits<{
  52. (e: "updateShape", value: LineIconData): void;
  53. (e: "addShape", value: LineIconData): void;
  54. (e: "delShape"): void;
  55. }>();
  56. const store = useStore();
  57. const getOperType = useGetTransformerOperType();
  58. const viewMat = useViewerInvertTransform();
  59. const pos = usePointerPos();
  60. const testPoints = useTestPoints();
  61. const { shape, tData, data, operateMenus, describes } = useComponentStatus({
  62. emit,
  63. props,
  64. getMouseStyle,
  65. transformType: "custom",
  66. selfData: true,
  67. customTransform(callback, shape, data) {
  68. let prevInvMat: Transform;
  69. let posOffset: Pos | null = null;
  70. return useCustomTransformer(shape, data, {
  71. getRepShape() {
  72. const group = new Group();
  73. const rect = new Rect();
  74. group.add(rect);
  75. const update = () => {
  76. const mat = getLineIconMat(getSnapLine(store, data.value)!, data.value);
  77. const width = Math.abs(data.value.endLen - data.value.startLen);
  78. const height = data.value.height;
  79. prevInvMat = mat;
  80. rect.width(width);
  81. rect.height(height);
  82. rect.offset({ x: width / 2, y: height / 2 });
  83. setShapeTransform(group, mat);
  84. };
  85. update();
  86. return { shape: group, update };
  87. },
  88. handler(data, mat) {
  89. if (pos.value && !getOperType()) {
  90. // if (!posOffset) {
  91. // const real = viewMat.value.point(pos.value);
  92. // const prevDec = prevInvMat.decompose();
  93. // posOffset = {
  94. // x: real.x - prevDec.x,
  95. // y: real.y - prevDec.y,
  96. // };
  97. // testPoints.value = [
  98. // {
  99. // x: real.x - posOffset.x,
  100. // y: real.y - posOffset.y,
  101. // },
  102. // ];
  103. // }
  104. const rpos = viewMat.value.point({
  105. x: pos.value.x,
  106. y: pos.value.y,
  107. });
  108. const m = mat.m;
  109. m[4] = rpos.x;
  110. m[5] = rpos.y;
  111. // m[4] = rpos.x - posOffset.x;
  112. // m[5] = rpos.y - posOffset.y;
  113. mat = new Transform(m);
  114. }
  115. matResponse({
  116. data,
  117. mat: mat,
  118. operType: getOperType(),
  119. store,
  120. });
  121. return true;
  122. },
  123. callback() {
  124. posOffset = null;
  125. callback();
  126. },
  127. openSnap: false,
  128. transformerConfig: {
  129. flipEnabled: true,
  130. rotateEnabled: false,
  131. enabledAnchors: ["middle-left", "middle-right"],
  132. boundBoxFunc: (oldBox, newBox) => {
  133. if (newBox.width < 5) {
  134. return oldBox;
  135. } else {
  136. return newBox;
  137. }
  138. },
  139. },
  140. });
  141. },
  142. defaultStyle,
  143. copyHandler(_, data) {
  144. const snapLine = getSnapLine(store, data)!;
  145. const line = getLineIconEndpoints(getSnapLine(store, data)!, data);
  146. const vector = lineVector(line);
  147. const move = vector.multiplyScalar(lineLen(line[0], line[1]));
  148. const mat = new Transform()
  149. .translate(move.x, move.y)
  150. .multiply(getLineIconMat(snapLine, data));
  151. return matResponse({ data, mat, store });
  152. },
  153. propertys: ["name", "fill", "stroke", "strokeWidth"],
  154. });
  155. const lineRaw = computed(() => {
  156. const line = store.getTypeItems("line")[0];
  157. return line.lines.find((item) => item.id === props.data.lineId);
  158. });
  159. const line = computed(() => getSnapLine(store, props.data));
  160. const history = useHistory();
  161. watch(
  162. () => ({
  163. line: line.value && (copy(line.value) as Pos[]),
  164. width: lineRaw.value?.strokeWidth,
  165. }),
  166. (newv, oldv) => {
  167. const line = newv.line;
  168. const oldLine = oldv?.line;
  169. history.preventTrack(() => {
  170. if (!line) {
  171. console.error("找不到line", props.data);
  172. return emit("delShape");
  173. }
  174. if (!oldLine) return;
  175. const eq0 = eqPoint(oldLine[0], line[0]);
  176. const eq1 = eqPoint(oldLine[1], line[1]);
  177. if (!eq0 || !eq1) {
  178. if (eq0 === eq1) {
  179. let newLen = lineLen(line[0], line[1]);
  180. if (zeroEq(newLen - lineLen(oldLine[0], oldLine[1]))) {
  181. shape.value?.getNode().fire("bound-change");
  182. return;
  183. }
  184. if (!isRangInner(line, data.value)) {
  185. emit("delShape");
  186. return;
  187. }
  188. } else {
  189. // 联动
  190. const startNdx = eq0 ? 0 : 1;
  191. const endNdx = eq0 ? 1 : 0;
  192. const rotate = line2IncludedAngle(
  193. [oldLine[startNdx], oldLine[endNdx]],
  194. [line[startNdx], line[endNdx]]
  195. );
  196. const mat = new Transform()
  197. .translate(line[startNdx].x, line[startNdx].y)
  198. .rotate(rotate)
  199. .translate(-line[startNdx].x, -line[startNdx].y);
  200. const endPoints = getLineIconEndpoints(oldLine, data.value).map((p) =>
  201. mat.point(p)
  202. );
  203. if (lineInner(line, endPoints[0]) && lineInner(line, endPoints[1])) {
  204. emit("updateShape", {
  205. ...data.value,
  206. startLen: lineLen(line[0], endPoints[0]),
  207. endLen: lineLen(line[0], endPoints[1]),
  208. });
  209. } else {
  210. emit("delShape");
  211. return;
  212. }
  213. }
  214. } else if (newv.width !== oldv.width && newv.width && props.data.type === "full") {
  215. emit("updateShape", { ...data.value, height: newv.width });
  216. }
  217. nextTick(() => {
  218. shape.value?.getNode().fire("bound-change");
  219. });
  220. });
  221. },
  222. { immediate: true, flush: "post" }
  223. );
  224. operateMenus.splice(0, 2);
  225. operateMenus.splice(1, 2);
  226. // if (props.data.type === "align-bottom" || props.data.type === "align-bottom-fix") {
  227. operateMenus.splice(
  228. operateMenus.length - 2,
  229. 0,
  230. // {
  231. // label: "内外翻转",
  232. // handler: () => {
  233. // emit("updateShape", {
  234. // ...data.value,
  235. // openSide: data.value.openSide === "LEFT" ? "RIGHT" : "LEFT",
  236. // });
  237. // },
  238. // },
  239. {
  240. label: "翻转",
  241. handler: () => {
  242. emit("updateShape", {
  243. ...data.value,
  244. openSide: data.value.openSide === "LEFT" ? "RIGHT" : "LEFT",
  245. startLen: data.value.endLen,
  246. endLen: data.value.startLen,
  247. });
  248. },
  249. }
  250. );
  251. // }
  252. </script>