MarkDrawTrack.vue 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. <template>
  2. <template v-for="(track, index) in trackList" :key="index">
  3. <div
  4. v-if="isTrackMode"
  5. class="score-container"
  6. :class="[focusedTrack(track) && 'score-animation']"
  7. :style="computeTopAndLeft(track)"
  8. >
  9. <span
  10. class="tw-m-auto"
  11. :id="'a' + track.mainNumber + track.subNumber + track.offsetY"
  12. >
  13. {{ track.score }}
  14. </span>
  15. </div>
  16. </template>
  17. <template v-for="(tag, index) in specialTagList" :key="index">
  18. <div class="score-container" :style="computeTopAndLeft(tag)">
  19. <span class="tw-m-auto">
  20. {{ tag.tagName }}
  21. </span>
  22. </div>
  23. </template>
  24. </template>
  25. <script setup lang="ts">
  26. import { ModeEnum } from "@/types";
  27. import type { SpecialTag, Track } from "@/types";
  28. import { computed, defineProps, toRefs, watch } from "vue";
  29. import { store } from "./store";
  30. const props = defineProps<{
  31. trackList: Array<Track>;
  32. specialTagList: Array<SpecialTag>;
  33. sliceImage: HTMLImageElement;
  34. dx: number;
  35. dy: number;
  36. }>();
  37. const { trackList, sliceImage, dx, dy } = toRefs(props);
  38. const isTrackMode = computed(() => store.setting.mode === ModeEnum.TRACK);
  39. const computeTopAndLeft = (track: Track | SpecialTag) => {
  40. const topInsideSlice = track.offsetY - dy.value;
  41. const leftInsideSlice = track.offsetX - dx.value;
  42. return {
  43. top: (topInsideSlice / sliceImage.value.naturalHeight) * 100 + "%",
  44. left: (leftInsideSlice / sliceImage.value.naturalWidth) * 100 + "%",
  45. "font-size":
  46. (store.setting.uiSetting["score.fontSize.scale"] || 1) *
  47. store.setting.uiSetting["answer.paper.scale"] *
  48. 2.2 +
  49. "em",
  50. };
  51. };
  52. const focusedTrack = (track: Track) => {
  53. return store.focusTracks.includes(track);
  54. };
  55. watch(
  56. () => store.focusTracks.length,
  57. () => {
  58. if (store.focusTracks.length === 0) return;
  59. const minImageIndex = Math.min(
  60. ...store.focusTracks.map((t) => t.offsetIndex)
  61. );
  62. const minImageOffsetY = Math.min(
  63. ...store.focusTracks
  64. .filter((t) => t.offsetIndex === minImageIndex)
  65. .map((t) => t.offsetY)
  66. );
  67. const topTrack = store.focusTracks.find(
  68. (t) => t.offsetIndex === minImageIndex && t.offsetY === minImageOffsetY
  69. );
  70. if (topTrack) {
  71. document
  72. .querySelector(
  73. `#a${topTrack.mainNumber + topTrack.subNumber + topTrack.offsetY}`
  74. )
  75. ?.scrollIntoView({ behavior: "smooth" });
  76. }
  77. }
  78. );
  79. </script>
  80. <style scoped>
  81. .score-container {
  82. position: absolute;
  83. display: flex;
  84. place-content: center;
  85. color: red;
  86. /* to center score */
  87. width: 200px;
  88. height: 200px;
  89. margin-top: -100px;
  90. margin-left: -100px;
  91. /* to click through div */
  92. pointer-events: none;
  93. }
  94. .score-animation {
  95. animation: 2s ease-in-out 0s infinite alternate change_size;
  96. }
  97. @keyframes change_size {
  98. from {
  99. font-size: 2em;
  100. margin-top: -100px;
  101. margin-left: -100px;
  102. }
  103. to {
  104. font-size: 4em;
  105. margin-top: -80px;
  106. margin-left: -80px;
  107. }
  108. }
  109. </style>