text.vue 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. <template>
  2. <TextDom v-if="editText && $shape" :shape="$shape" @submit="quitHandler" />
  3. <TempText
  4. :data="tData"
  5. :ref="(v: any) => shape = v?.shape"
  6. :id="data.id"
  7. @dblclick="dbclickHandler"
  8. />
  9. <PropertyUpdate
  10. v-if="!editText"
  11. :describes="describes"
  12. :data="data"
  13. :target="shape"
  14. @change="emit('updateShape', { ...data })"
  15. />
  16. <Operate :target="shape" :menus="operateMenus" v-if="!editText" />
  17. </template>
  18. <script lang="ts" setup>
  19. import { TextData, getMouseStyle, defaultStyle, textNodeMap } from "./index.ts";
  20. import { PropertyUpdate, Operate } from "../../propertys";
  21. import TempText from "./temp-text.vue";
  22. import TextDom from "./text-dom.vue";
  23. import { useComponentStatus } from "@/core/hook/use-component.ts";
  24. import {
  25. cloneRepShape,
  26. useCustomTransformer,
  27. useTransformer,
  28. } from "@/core/hook/use-transformer.ts";
  29. import { Transform } from "konva/lib/Util";
  30. import { Text } from "konva/lib/shapes/Text";
  31. import { computed, ref, watch } from "vue";
  32. import { setShapeTransform } from "@/utils/shape.ts";
  33. import { zeroEq } from "@/utils/math.ts";
  34. import { MathUtils } from "three";
  35. import { KonvaEventObject } from "konva/lib/Node";
  36. const props = defineProps<{ data: TextData }>();
  37. const emit = defineEmits<{
  38. (e: "updateShape", value: TextData): void;
  39. (e: "addShape", value: TextData): void;
  40. (e: "delShape"): void;
  41. }>();
  42. const { shape, tData, data, operateMenus, describes } = useComponentStatus<
  43. Text,
  44. TextData
  45. >({
  46. emit,
  47. props,
  48. getMouseStyle,
  49. defaultStyle,
  50. transformType: "custom",
  51. customTransform(callback, shape, data) {
  52. useCustomTransformer(shape, data, {
  53. openSnap: true,
  54. getRepShape($shape) {
  55. const repShape = cloneRepShape($shape).shape;
  56. return {
  57. shape: repShape,
  58. update(data) {
  59. data.width && repShape.width(data.width);
  60. setShapeTransform(repShape, new Transform(data.mat));
  61. },
  62. };
  63. },
  64. transformerConfig: {
  65. rotateEnabled: true,
  66. enabledAnchors: ["middle-left", "middle-right"],
  67. boundBoxFunc: (oldBox, newBox) => {
  68. if (newBox.width - minWidth.value < -0.01) {
  69. return oldBox;
  70. }
  71. return newBox;
  72. },
  73. },
  74. beforeHandler(data, mat) {
  75. return { ...data, ...update(mat, data) };
  76. },
  77. handler(data, mat) {
  78. const setAttrib = update(mat, data);
  79. Object.assign(data, setAttrib);
  80. if (setAttrib.width) {
  81. return true;
  82. }
  83. },
  84. callback,
  85. });
  86. },
  87. copyHandler(tf, data) {
  88. return {
  89. ...data,
  90. mat: tf.multiply(new Transform(data.mat)).m,
  91. };
  92. },
  93. propertys: [
  94. "fill",
  95. "stroke",
  96. "strokeWidth",
  97. "dash",
  98. "opacity",
  99. "fontSize",
  100. "align",
  101. "fontStyle",
  102. // "ref",
  103. // "zIndex",
  104. ],
  105. });
  106. const minWidth = computed(() => (data.value.fontSize || 12) * 2);
  107. const getWidth = (data: TextData, scaleX: number) => {
  108. let width: number;
  109. if ("width" in data) {
  110. width = Math.max(data.width! * scaleX, minWidth.value);
  111. } else {
  112. width = Math.max(shape.value!.getNode()!.width() * scaleX, minWidth.value);
  113. }
  114. return width;
  115. };
  116. const update = (mat: Transform, data: TextData) => {
  117. const { scaleX, x, y, rotation } = mat.decompose();
  118. if (!zeroEq(scaleX - 1)) {
  119. return {
  120. width: getWidth(data, scaleX),
  121. mat: new Transform()
  122. .translate(x, y)
  123. .rotate(MathUtils.degToRad(rotation))
  124. .scale(1, 1).m,
  125. };
  126. } else {
  127. return {
  128. mat: mat.m,
  129. };
  130. }
  131. };
  132. // 字体大小变化时,更新width
  133. watch(
  134. () => data.value.fontSize,
  135. () => {
  136. data.value.width = getWidth(data.value, 1);
  137. const $shape = shape.value?.getNode();
  138. $shape && $shape.fire("bound-change");
  139. }
  140. );
  141. const transformer = useTransformer();
  142. const $shape = computed(() => shape.value?.getNode());
  143. const editText = ref(false);
  144. const dbclickHandler = (ev: KonvaEventObject<MouseEvent>) => {
  145. editText.value = true;
  146. $shape.value && $shape.value.hide();
  147. transformer.hide();
  148. };
  149. const quitHandler = (val: string) => {
  150. console.log("quit");
  151. editText.value = false;
  152. transformer.show();
  153. $shape.value && $shape.value.show();
  154. if (val !== data.value.content) {
  155. data.value.content = val;
  156. }
  157. };
  158. </script>
  159. <style scoped lang="scss"></style>