text.vue 3.7 KB

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