size-line.vue 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. <template>
  2. <v-group :config="{ listening: false }">
  3. <template v-for="line in lines">
  4. <v-line
  5. v-for="p in line.points"
  6. :config="{
  7. stroke: stroke,
  8. strokeWidth: strokeWidth,
  9. points: flatPositions(p),
  10. }"
  11. />
  12. <v-textPath
  13. :config="{
  14. fill: stroke,
  15. align: 'center',
  16. fontSize,
  17. textBaseline: 'middle',
  18. ...line.textConfig,
  19. }"
  20. />
  21. </template>
  22. </v-group>
  23. </template>
  24. <script lang="ts" setup>
  25. import { computed } from "vue";
  26. import { LineData } from "../line";
  27. import {
  28. getPolygonDirection,
  29. getVectorLine,
  30. lineLen,
  31. lineVector,
  32. lineVerticalVector,
  33. Pos,
  34. vector,
  35. } from "@/utils/math";
  36. import { flatPositions } from "@/utils/shared";
  37. import { useProportion } from "@/core/hook/use-proportion";
  38. import { TextPathConfig } from "konva/lib/shapes/TextPath";
  39. const props = withDefaults(
  40. defineProps<{
  41. data: Required<Pick<LineData, "points" | "strokeWidth">>;
  42. stroke?: string;
  43. strokeWidth?: number;
  44. closed?: boolean;
  45. }>(),
  46. { stroke: "#999", strokeWidth: 1 }
  47. );
  48. const fontSize = computed(() => 10 + props.strokeWidth * 2);
  49. const len = computed(() =>
  50. props.closed ? props.data.points.length : props.data.points.length - 1
  51. );
  52. const getLine = (ndx: number) =>
  53. [
  54. props.data.points[ndx % props.data.points.length],
  55. props.data.points[(ndx + 1) % props.data.points.length],
  56. ] as [Pos, Pos];
  57. const isClockwise = computed(() => getPolygonDirection(props.data.points) <= 0);
  58. const proportion = useProportion();
  59. const margin = computed(() => -(props.data.strokeWidth + 10));
  60. const lines = computed(() => {
  61. const lines: { points: Pos[][]; textConfig: TextPathConfig }[] = [];
  62. for (let i = 0; i < len.value; i++) {
  63. const line = getLine(i);
  64. const lineOffset = isClockwise.value ? margin.value : -margin.value;
  65. const vv = lineVerticalVector(line);
  66. const offset = vv.multiplyScalar(lineOffset);
  67. const p = [vector(line[0]).add(offset), vector(line[1]).add(offset)];
  68. const text = proportion.transform(lineLen(...line));
  69. const w = fontSize.value * text.length;
  70. const lw = lineLen(p[0], p[1]);
  71. if (w > lw) {
  72. continue;
  73. }
  74. const lv = lineVector(p);
  75. const line1 = getVectorLine(lv, p[0], (lw - w) / 2);
  76. const [_, start2] = getVectorLine(lv, line1[1], w);
  77. const line2 = getVectorLine(lv, start2, (lw - w) / 2);
  78. lines.push({
  79. points: [line1, line2],
  80. textConfig: {
  81. text: proportion.transform(lineLen(...line)),
  82. x: 0,
  83. y: 0,
  84. data: `M${p[0].x},${p[0].y} L${p[1].x},${p[1].y}`,
  85. },
  86. });
  87. }
  88. return lines;
  89. });
  90. </script>