temp-group.vue 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. <template>
  2. <v-rect
  3. :config="{ ...data, opacity: 1, zIndex: undefined, strokeScaleEnable: true }"
  4. ref="shape"
  5. />
  6. </template>
  7. <script lang="ts" setup>
  8. import { defaultStyle, GroupData } from "./index.ts";
  9. import { computed, nextTick, onUnmounted, ref, watch } from "vue";
  10. import { DC, EntityShape } from "@/deconstruction.js";
  11. import { Rect, RectConfig } from "konva/lib/shapes/Rect";
  12. import { useViewerInvertTransform } from "@/core/hook/use-viewer.ts";
  13. import { useStage } from "@/core/hook/use-global-vars.ts";
  14. import { useOnComponentBoundChange } from "@/core/hook/use-component.ts";
  15. import { debounce } from "@/utils/shared.ts";
  16. const props = defineProps<{
  17. data: GroupData;
  18. addMode?: boolean;
  19. autoUpdate?: boolean;
  20. }>();
  21. const shape = ref<DC<Rect>>();
  22. const data = computed(() => ({ ...defaultStyle, ...props.data }));
  23. const stage = useStage();
  24. const getGroupShapes = () => {
  25. const $stage = stage.value?.getNode();
  26. if (!$stage) return;
  27. const shapes: EntityShape[] = [];
  28. for (const id of data.value.ids) {
  29. const $shape = $stage!.findOne(`#${id}`)!;
  30. if (!$shape || $shape.attrs.disableGroupOper) continue;
  31. shapes.push($shape as EntityShape);
  32. }
  33. return shapes;
  34. };
  35. const invMat = useViewerInvertTransform();
  36. const updateBound = () => {
  37. const shapes = $shapes.value;
  38. if (!shapes?.length) {
  39. console.error("!shapes");
  40. return;
  41. }
  42. let lx = Number.MAX_VALUE;
  43. let ly = Number.MAX_VALUE;
  44. let rx = Number.MIN_VALUE;
  45. let ry = Number.MIN_VALUE;
  46. for (const shape of shapes) {
  47. if (!shape) continue;
  48. const rect = shape.getClientRect();
  49. if (rect.x < lx) {
  50. lx = rect.x;
  51. }
  52. if (rect.y < ly) {
  53. ly = rect.y;
  54. }
  55. if (rect.x + rect.width > rx) {
  56. rx = rect.x + rect.width;
  57. }
  58. if (rect.y + rect.height > ry) {
  59. ry = rect.y + rect.height;
  60. }
  61. }
  62. const pixelStart = invMat.value.point({ x: lx, y: ly });
  63. const pixelEnd = invMat.value.point({ x: rx, y: ry });
  64. lx = pixelStart.x;
  65. ly = pixelStart.y;
  66. rx = pixelEnd.x;
  67. ry = pixelEnd.y;
  68. const config: RectConfig = {
  69. x: lx,
  70. y: ly,
  71. width: rx - lx,
  72. height: ry - ly,
  73. rotation: 0,
  74. scaleX: 1,
  75. scaleY: 1,
  76. };
  77. const $shape = shape.value?.getNode() as any;
  78. if ($shape) {
  79. for (const key in config) {
  80. $shape[key](config[key]);
  81. }
  82. nextTick(() => $shape.fire("bound-change"));
  83. }
  84. };
  85. watch(
  86. () => data.value.ids.length,
  87. () => nextTick(updateBound),
  88. { immediate: true }
  89. );
  90. const { on } = useOnComponentBoundChange();
  91. const $shapes = computed(getGroupShapes);
  92. const syncUpdateBound = debounce(updateBound, 0);
  93. watch(
  94. () => {
  95. return (
  96. data.value.ids.join(",") +
  97. (props.autoUpdate ? "1" : "0") +
  98. (data.value.listening ? "1" : "0") +
  99. (stage.value ? "1" : "0")
  100. );
  101. },
  102. (_a, _b, onCleanup) => {
  103. if (props.autoUpdate) {
  104. onCleanup(
  105. on(
  106. $shapes,
  107. () => {
  108. props.autoUpdate && syncUpdateBound();
  109. },
  110. false
  111. )
  112. );
  113. }
  114. },
  115. { immediate: true }
  116. );
  117. onUnmounted(() => console.error("des temp-group"));
  118. defineExpose({
  119. get shape() {
  120. return shape.value;
  121. },
  122. updateBound,
  123. getGroupShapes,
  124. });
  125. </script>