Преглед на файлове

简化轨迹渲染的参数

Michael Wang преди 3 години
родител
ревизия
6825cdfc57

+ 12 - 7
src/features/mark/CommonMarkBody.vue

@@ -32,7 +32,8 @@
         <MarkDrawTrack
           :track-list="item.trackList"
           :special-tag-list="item.tagList"
-          :slice-image="item.sliceImage"
+          :slice-image-width="item.sliceImageWidth"
+          :slice-image-height="item.sliceImageHeight"
           :dx="item.dx"
           :dy="item.dy"
         />
@@ -240,7 +241,7 @@ async function processSliceConfig() {
       (t) => t.offsetIndex === indexInSliceUrls
     );
 
-    const sliceImage = await getImageUsingDataUrl(dataUrl);
+    const sliceImageRendered = await getImageUsingDataUrl(dataUrl);
     tempSliceImagesWithTrackList.push({
       url: dataUrl,
       indexInSliceUrls: sliceConfig.i,
@@ -255,8 +256,10 @@ async function processSliceConfig() {
           t.positionY >= accumTopHeight / theFinalHeight &&
           t.positionY < accumBottomHeight / theFinalHeight
       ),
-      originalImage: image,
-      sliceImage,
+      // originalImageWidth: image.naturalWidth,
+      // originalImageHeight: image.naturalHeight,
+      sliceImageWidth: sliceImageRendered.naturalWidth,
+      sliceImageHeight: sliceImageRendered.naturalHeight,
       dx: sliceConfig.x,
       dy: sliceConfig.y,
       accumTopHeight,
@@ -336,7 +339,7 @@ async function processSplitConfig() {
       const thisImageTagList = tagLists.filter(
         (t) => t.offsetIndex === indexInSliceUrls
       );
-      const sliceImage = await getImageUsingDataUrl(dataUrl);
+      const sliceImageRendered = await getImageUsingDataUrl(dataUrl);
       tempSliceImagesWithTrackList.push({
         url: dataUrl,
         indexInSliceUrls: store.currentTask.sliceUrls.indexOf(url) + 1,
@@ -350,8 +353,10 @@ async function processSplitConfig() {
             t.positionY >= accumTopHeight / theFinalHeight &&
             t.positionY < accumBottomHeight / theFinalHeight
         ),
-        originalImage: image,
-        sliceImage,
+        // originalImageWidth: image.naturalWidth,
+        // originalImageHeight: image.naturalHeight,
+        sliceImageWidth: sliceImageRendered.naturalWidth,
+        sliceImageHeight: sliceImageRendered.naturalHeight,
         dx: image.naturalWidth * config[0],
         dy: 0,
         accumTopHeight,

+ 7 - 6
src/features/mark/MarkDrawTrack.vue

@@ -32,20 +32,21 @@ import { store } from "./store";
 const props = defineProps<{
   trackList: Array<Track>;
   specialTagList: Array<SpecialTag>;
-  sliceImage: HTMLImageElement;
+  sliceImageWidth: number;
+  sliceImageHeight: number;
   dx: number;
   dy: number;
 }>();
-const { trackList, sliceImage, dx, dy } = toRefs(props);
+const { trackList } = toRefs(props);
 
 const isTrackMode = computed(() => store.setting.mode === ModeEnum.TRACK);
 
 const computeTopAndLeft = (track: Track | SpecialTag) => {
-  const topInsideSlice = track.offsetY - dy.value;
-  const leftInsideSlice = track.offsetX - dx.value;
+  const topInsideSlice = track.offsetY - props.dy;
+  const leftInsideSlice = track.offsetX - props.dx;
   return {
-    top: (topInsideSlice / sliceImage.value.naturalHeight) * 100 + "%",
-    left: (leftInsideSlice / sliceImage.value.naturalWidth) * 100 + "%",
+    top: (topInsideSlice / props.sliceImageHeight) * 100 + "%",
+    left: (leftInsideSlice / props.sliceImageWidth) * 100 + "%",
     "font-size":
       (store.setting.uiSetting["score.fontSize.scale"] || 1) *
         store.setting.uiSetting["answer.paper.scale"] *

+ 1 - 1
src/features/student/importInspect/ImportInspect.vue

@@ -24,7 +24,7 @@ import {
 import { store } from "@/features/mark/store";
 import MarkHeader from "./MarkHeader.vue";
 import { useRoute } from "vue-router";
-import MarkBody from "./MarkBody.vue";
+import MarkBody from "../inspect/MarkBody.vue";
 import MarkBoardInspect from "./MarkBoardInspect.vue";
 import type { Task, Setting } from "@/types";
 import { message } from "ant-design-vue";

+ 0 - 180
src/features/student/importInspect/MarkBody.vue

@@ -1,180 +0,0 @@
-<template>
-  <div class="mark-body-container tw-flex-auto tw-p-2" ref="dragContainer">
-    <a-spin
-      :spinning="rendering"
-      size="large"
-      tip="Loading..."
-      style="margin-top: 50px"
-    >
-      <div v-if="!store.currentTask" class="tw-text-center">
-        {{ store.message }}
-      </div>
-      <div v-else :style="{ width: answerPaperScale }">
-        <div
-          v-for="(item, index) in sliceImagesWithTrackList"
-          :key="index"
-          class="single-image-container"
-        >
-          <img :src="item.url" draggable="false" />
-          <MarkDrawTrack
-            :track-list="item.trackList"
-            :special-tag-list="item.tagList"
-            :original-image="item.originalImage"
-          />
-          <hr class="image-seperator" />
-        </div>
-      </div>
-    </a-spin>
-  </div>
-</template>
-
-<script setup lang="ts">
-import { computed, reactive, ref, watch } from "vue";
-import { store } from "@/features/mark/store";
-import MarkDrawTrack from "./MarkDrawTrack.vue";
-import type { SpecialTag, Track } from "@/types";
-import { useTimers } from "@/setups/useTimers";
-import { loadImage } from "@/utils/utils";
-import { dragImage } from "@/features/mark/use/draggable";
-
-interface SliceImage {
-  url: string;
-  indexInSliceUrls: number;
-  trackList: Array<Track>;
-  tagList: Array<SpecialTag>;
-  originalImage: HTMLImageElement;
-}
-
-const emit = defineEmits(["error"]);
-
-const { dragContainer } = dragImage();
-
-const { addTimeout } = useTimers();
-
-let rendering = ref(false);
-let sliceImagesWithTrackList: Array<SliceImage> = reactive([]);
-
-async function processImage() {
-  if (!store.currentTask) return;
-
-  const images = [];
-  for (const url of store.currentTask.sliceUrls) {
-    const image = await loadImage(url);
-    images.push(image);
-  }
-
-  for (const url of store.currentTask.sliceUrls) {
-    const completeUrl = url;
-
-    const indexInSliceUrls = store.currentTask.sliceUrls.indexOf(url) + 1;
-    const image = images[indexInSliceUrls - 1];
-
-    const trackLists = (store.currentTask.questionList || [])
-      .map((q) => q.trackList)
-      .reduce((acc, t) => {
-        acc = acc.concat(t);
-        return acc;
-      }, [] as Array<Track>);
-    const thisImageTrackList = trackLists.filter(
-      (t) => t.offsetIndex === indexInSliceUrls
-    );
-    const thisImageTagList = (store.currentTask.specialTagList || []).filter(
-      (t) => t.offsetIndex === indexInSliceUrls
-    );
-
-    sliceImagesWithTrackList.push({
-      url: completeUrl,
-      indexInSliceUrls,
-      trackList: thisImageTrackList,
-      tagList: thisImageTagList,
-      originalImage: image,
-    });
-  }
-}
-
-// should not render twice at the same time
-let renderLock = false;
-const renderPaperAndMark = async () => {
-  if (renderLock) {
-    console.log("上个任务还未渲染完毕,稍等一秒再尝试渲染");
-    await new Promise((res) => setTimeout(res, 1000));
-    await renderPaperAndMark();
-    return;
-  }
-  renderLock = true;
-  sliceImagesWithTrackList.splice(0);
-
-  if (!store.currentTask) {
-    renderLock = false;
-    return;
-  }
-
-  try {
-    rendering.value = true;
-    await processImage();
-  } catch (error) {
-    sliceImagesWithTrackList.splice(0);
-    console.log("render error ", error);
-    // 图片加载出错,自动加载下一个任务
-    emit("error");
-  } finally {
-    renderLock = false;
-    rendering.value = false;
-  }
-};
-
-watch(() => store.currentTask, renderPaperAndMark);
-
-const answerPaperScale = computed(() => {
-  // 放大、缩小不影响页面之前的滚动条定位
-  let percentWidth = 0;
-  let percentTop = 0;
-  const container = document.querySelector(
-    ".mark-body-container"
-  ) as HTMLDivElement;
-  if (container) {
-    const { scrollLeft, scrollTop, scrollWidth, scrollHeight } = container;
-    percentWidth = scrollLeft / scrollWidth;
-    percentTop = scrollTop / scrollHeight;
-  }
-
-  addTimeout(() => {
-    if (container) {
-      const { scrollWidth, scrollHeight } = container;
-      container.scrollTo({
-        left: scrollWidth * percentWidth,
-        top: scrollHeight * percentTop,
-      });
-    }
-  }, 10);
-  const scale = store.setting.uiSetting["answer.paper.scale"];
-  return scale * 100 + "%";
-});
-</script>
-
-<style scoped>
-.mark-body-container {
-  height: calc(100vh - 56px);
-  overflow: auto;
-  background-color: var(--app-container-bg-color);
-  background-image: linear-gradient(45deg, #e0e0e0 25%, transparent 25%),
-    linear-gradient(-45deg, #e0e0e0 25%, transparent 25%),
-    linear-gradient(45deg, transparent 75%, #e0e0e0 75%),
-    linear-gradient(-45deg, transparent 75%, #e0e0e0 75%);
-  background-size: 20px 20px;
-  background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
-  transform: inherit;
-
-  cursor: grab;
-  user-select: none;
-}
-.mark-body-container img {
-  width: 100%;
-}
-.single-image-container {
-  position: relative;
-}
-.image-seperator {
-  border: 2px solid rgba(120, 120, 120, 0.1);
-}
-</style>

+ 0 - 108
src/features/student/importInspect/MarkDrawTrack.vue

@@ -1,108 +0,0 @@
-<template>
-  <template v-for="(track, index) in trackList" :key="index">
-    <div
-      class="score-container"
-      :class="[focusedTrack(track) && 'score-animation']"
-      :style="computeTopAndLeft(track)"
-    >
-      <span
-        class="tw-m-auto"
-        :id="'a' + track.mainNumber + track.subNumber + track.offsetY"
-      >
-        {{ track.score }}
-      </span>
-    </div>
-  </template>
-  <template v-for="(tag, index) in specialTagList" :key="index">
-    <div class="score-container" :style="computeTopAndLeft(tag)">
-      <span class="tw-m-auto">
-        {{ tag.tagName }}
-      </span>
-    </div>
-  </template>
-</template>
-
-<script setup lang="ts">
-import type { SpecialTag, Track } from "@/types";
-import { store } from "@/features/mark/store";
-import { toRefs, watch } from "vue";
-
-const props = defineProps<{
-  trackList: Array<Track>;
-  specialTagList: Array<SpecialTag>;
-  originalImage: HTMLImageElement;
-}>();
-const { trackList, originalImage } = toRefs(props);
-
-const focusedTrack = (track: Track) => {
-  return store.focusTracks.includes(track);
-};
-const computeTopAndLeft = (track: Track | SpecialTag) => {
-  const topInsideSlice = track.offsetY;
-  const leftInsideSlice = track.offsetX;
-  return {
-    top: (topInsideSlice / originalImage.value.naturalHeight) * 100 + "%",
-    left: (leftInsideSlice / originalImage.value.naturalWidth) * 100 + "%",
-    "font-size": store.setting.uiSetting["answer.paper.scale"] * 2.2 + "em",
-  };
-};
-
-watch(
-  () => store.focusTracks.length,
-  () => {
-    if (store.focusTracks.length === 0) return;
-    const minImageIndex = Math.min(
-      ...store.focusTracks.map((t) => t.offsetIndex)
-    );
-    const minImageOffsetY = Math.min(
-      ...store.focusTracks
-        .filter((t) => t.offsetIndex === minImageIndex)
-        .map((t) => t.offsetY)
-    );
-    const topTrack = store.focusTracks.find(
-      (t) => t.offsetIndex === minImageIndex && t.offsetY === minImageOffsetY
-    );
-    if (topTrack) {
-      document
-        .querySelector(
-          `#a${topTrack.mainNumber + topTrack.subNumber + topTrack.offsetY}`
-        )
-        ?.scrollIntoView({ behavior: "smooth" });
-    }
-  }
-);
-</script>
-
-<style scoped>
-.score-container {
-  position: absolute;
-  display: flex;
-  place-content: center;
-  color: red;
-
-  /* to center score */
-  width: 200px;
-  height: 200px;
-  margin-top: -100px;
-  margin-left: -100px;
-
-  /* to click through div */
-  pointer-events: none;
-}
-.score-animation {
-  animation: 2s ease-in-out 0s infinite alternate change_size;
-}
-
-@keyframes change_size {
-  from {
-    font-size: 2em;
-    margin-top: -100px;
-    margin-left: -100px;
-  }
-  to {
-    font-size: 4em;
-    margin-top: -80px;
-    margin-left: -80px;
-  }
-}
-</style>

+ 7 - 8
src/features/student/inspect/MarkBody.vue

@@ -19,7 +19,8 @@
           <MarkDrawTrack
             :track-list="item.trackList"
             :special-tag-list="item.tagList"
-            :original-image="item.originalImage"
+            :original-image-height="item.originalImageHeight"
+            :original-image-width="item.originalImageWidth"
           />
           <hr class="image-seperator" />
         </div>
@@ -39,10 +40,10 @@ import { dragImage } from "@/features/mark/use/draggable";
 
 interface SliceImage {
   url: string;
-  indexInSliceUrls: number;
   trackList: Array<Track>;
   tagList: Array<SpecialTag>;
-  originalImage: HTMLImageElement;
+  originalImageWidth: number;
+  originalImageHeight: number;
 }
 
 const emit = defineEmits(["error"]);
@@ -64,8 +65,6 @@ async function processImage() {
   }
 
   for (const url of store.currentTask.sliceUrls) {
-    const completeUrl = url;
-
     const indexInSliceUrls = store.currentTask.sliceUrls.indexOf(url) + 1;
     const image = images[indexInSliceUrls - 1];
 
@@ -83,11 +82,11 @@ async function processImage() {
     );
 
     sliceImagesWithTrackList.push({
-      url: completeUrl,
-      indexInSliceUrls,
+      url,
       trackList: thisImageTrackList,
       tagList: thisImageTagList,
-      originalImage: image,
+      originalImageWidth: image.naturalWidth,
+      originalImageHeight: image.naturalHeight,
     });
   }
 }

+ 5 - 4
src/features/student/inspect/MarkDrawTrack.vue

@@ -30,9 +30,10 @@ import { toRefs, watch } from "vue";
 const props = defineProps<{
   trackList: Array<Track>;
   specialTagList: Array<SpecialTag>;
-  originalImage: HTMLImageElement;
+  originalImageWidth: number;
+  originalImageHeight: number;
 }>();
-const { trackList, originalImage } = toRefs(props);
+const { trackList } = toRefs(props);
 
 const focusedTrack = (track: Track) => {
   return store.focusTracks.includes(track);
@@ -41,8 +42,8 @@ const computeTopAndLeft = (track: Track | SpecialTag) => {
   const topInsideSlice = track.offsetY;
   const leftInsideSlice = track.offsetX;
   return {
-    top: (topInsideSlice / originalImage.value.naturalHeight) * 100 + "%",
-    left: (leftInsideSlice / originalImage.value.naturalWidth) * 100 + "%",
+    top: (topInsideSlice / props.originalImageHeight) * 100 + "%",
+    left: (leftInsideSlice / props.originalImageWidth) * 100 + "%",
     "font-size": store.setting.uiSetting["answer.paper.scale"] * 2.2 + "em",
   };
 };

+ 7 - 5
src/types/index.ts

@@ -220,11 +220,13 @@ export interface SliceImage {
   indexInSliceUrls: number;
   trackList: Array<Track>;
   tagList: Array<SpecialTag>;
-  originalImage: HTMLImageElement; // TODO: 为了兼容原图还原轨迹而添加的属性,当前CommonMarkBody用不到
-  sliceImage: HTMLImageElement;
-  dx: number;
-  dy: number;
-  accumTopHeight: number;
+  // originalImageWidth: number; // 为了兼容原图还原轨迹而添加的属性,当前CommonMarkBody用不到
+  // originalImageHeight: number; // 为了兼容原图还原轨迹而添加的属性,当前CommonMarkBody用不到
+  sliceImageWidth: number;
+  sliceImageHeight: number;
+  dx: number; // 裁切图在原图中的左上角的x偏移量
+  dy: number; // 裁切图在原图中的左上角的y偏移量
+  accumTopHeight: number; // 在多个图片从高至低排列中中累积的高度
   effectiveWidth: number;
 }