Browse Source

画板拖动背景图

natasha 2 days ago
parent
commit
8a9dfa3881

+ 0 - 1
src/views/book/courseware/create/components/base/common/UploadFile.vue

@@ -384,7 +384,6 @@ export default {
               }
               this.content.file_id_list.push(file_info_list[0].file_id);
               this.$refs.upload.uploadFiles = [];
-              console.log(this.content.file_info);
               this.$forceUpdate();
             }
           });

+ 259 - 9
src/views/book/courseware/create/components/question/drawing/Drawing.vue

@@ -27,20 +27,36 @@
         />
       </div>
       <div
-        id="selectableArea"
+        class="background-img"
         :style="{
           width: data.image_width + 'px',
           height: data.image_height + 'px',
-          background: data.image_list[0]
-            ? 'url(' + data.image_list[0].file_url + ') center / contain no-repeat'
-            : '#DCDFE6',
-          position: 'relative',
           border: '1px dotted #DCDFE6',
-          textAlign: 'center',
-          lineHeight: data.image_height + 'px',
+          background: data.image_list[0] && file_url ? '' : '#DCDFE6',
         }"
       >
-        {{ data.image_list[0] ? '' : '背景图' }}
+        <div
+          v-if="data.image_list[0] && file_url"
+          class="img-set"
+          :style="{ top: `${data.imgData.top - 9}px`, left: `${data.imgData.left}px` }"
+        >
+          <div class="dot top-left" @mousedown="dragStart($event, 'nwse-resize', 'top-left')"></div>
+          <div class="horizontal-line" @mousedown="dragStart($event, 'ns-resize', 'top')"></div>
+          <div class="dot top-right" @mousedown="dragStart($event, 'nesw-resize', 'top-right')"></div>
+          <div class="vertical-line" @mousedown="dragStart($event, 'ew-resize', 'left')"></div>
+          <img
+            :src="file_url"
+            draggable="false"
+            alt="背景图"
+            :style="{ width: `${data.imgData.width}px`, height: `${data.imgData.height}px` }"
+            @mousedown="dragStart($event, 'move', 'move')"
+          />
+          <div class="vertical-line" @mousedown="dragStart($event, 'ew-resize', 'right')"></div>
+          <div class="dot bottom-left" @mousedown="dragStart($event, 'nesw-resize', 'bottom-left')"></div>
+          <div class="horizontal-line" @mousedown="dragStart($event, 'ns-resize', 'bottom')"></div>
+          <div class="dot bottom-right" @mousedown="dragStart($event, 'nwse-resize', 'bottom-right')"></div>
+        </div>
+        <p v-else :style="{ lineHeight: data.image_height + 'px', textAlign: 'center' }">背景图</p>
       </div>
     </template>
   </ModuleBase>
@@ -63,6 +79,7 @@ export default {
       labelText: '背景图',
       acceptFileType: '.jpg,.png,.jpeg',
       iconClass: 'picture',
+      file_url: '',
     };
   },
   watch: {
@@ -75,18 +92,55 @@ export default {
       immediate: true,
     },
   },
+  mounted() {
+    document.querySelector('.background-img').addEventListener('mousemove', this.mouseMove);
+    document.body.addEventListener('mouseup', this.mouseUp);
+  },
+  beforeDestroy() {
+    document.querySelector('.background-img')?.removeEventListener('mousemove', this.mouseMove);
+    document.body.removeEventListener('mouseup', this.mouseUp);
+  },
   methods: {
     updateFileList({ file_list, file_id_list, file_info_list }) {
       this.data.image_list = file_list;
       this.data.image_id_list = file_id_list;
       this.data.image_info_list = file_info_list;
       this.data.file_id_list = file_id_list;
+      if (file_list.length > 0) {
+        const img = new Image();
+        img.src = file_list[0].file_url;
+        this.file_url = file_list[0].file_url;
+        img.onload = () => {
+          const { width, height } = img;
+
+          if (width > this.data.image_width || height > this.data.image_height) {
+            const wScale = width / this.data.image_width;
+            const hScale = height / this.data.image_height;
+            const scale = wScale > hScale ? this.data.image_width / 2 / width : this.data.image_height / 2 / height;
+
+            this.data.imgData = {
+              width: width * scale,
+              height: height * scale,
+              top: 0,
+              left: 0,
+            };
+          } else {
+            this.data.imgData = {
+              width,
+              height,
+              top: 0,
+              left: 0,
+            };
+          }
+        };
+      }
     },
 
     handleData() {
       this.data.image_list.forEach((item) => {
         GetFileURLMap({ file_id_list: [item.file_id] }).then(({ url_map }) => {
-          this.$set(item, 'file_url', url_map[item.file_id]);
+          this.file_url = url_map[item.file_id];
+          // this.$set(item, 'file_url', url_map[item.file_id]);
         });
       });
       this.handleMindMap();
@@ -102,6 +156,127 @@ export default {
       });
       this.data.mind_map.node_list = node_list;
     },
+    /**
+     * 拖拽开始
+     * @param {MouseEvent} event
+     * @param {string} cursor
+     * @param {string} type
+     */
+    dragStart(event, cursor, type) {
+      const { clientX, clientY } = event;
+      this.data.drag = {
+        dragging: true,
+        startX: clientX,
+        startY: clientY,
+        type,
+      };
+
+      document.querySelector('.background-img').style.cursor = cursor;
+    },
+    /**
+     * 鼠标移动
+     * @param {MouseEvent} event
+     */
+    mouseMove(event) {
+      if (!this.data.drag.dragging) return;
+      const { clientX, clientY } = event;
+      const { startX, startY, type } = this.data.drag;
+
+      const widthDiff = clientX - startX;
+      const heightDiff = clientY - startY;
+
+      if (type === 'top-left') {
+        this.data.imgData.width = Math.min(this.data.image_width, Math.max(0, this.data.imgData.width - widthDiff));
+        this.data.imgData.height = Math.min(this.data.image_height, Math.max(0, this.data.imgData.height - heightDiff));
+        this.data.imgData.top = Math.min(
+          this.data.image_height - this.data.imgData.height,
+          Math.max(0, this.data.imgData.top + heightDiff),
+        );
+        this.data.imgData.left = Math.min(
+          this.data.image_width - this.data.imgData.width,
+          Math.max(0, this.data.imgData.left + widthDiff),
+        );
+      } else if (type === 'top-right') {
+        this.data.imgData.width = Math.min(this.data.image_width, Math.max(this.data.imgData.width + widthDiff));
+        this.data.imgData.height = Math.min(this.data.image_height, Math.max(0, this.data.imgData.height - heightDiff));
+        this.data.imgData.top = Math.min(
+          this.data.image_height - this.data.imgData.height,
+          Math.max(0, this.data.imgData.top + heightDiff),
+        );
+        this.data.imgData.left = Math.min(
+          this.data.image_width - this.data.imgData.width,
+          Math.max(0, this.data.imgData.left),
+        );
+      } else if (type === 'bottom-left') {
+        this.data.imgData.width = Math.min(this.data.image_width, Math.max(0, this.data.imgData.width - widthDiff));
+        this.data.imgData.height = Math.min(this.data.image_height, Math.max(this.data.imgData.height + heightDiff));
+        this.data.imgData.top = Math.min(
+          this.data.image_height - this.data.imgData.height,
+          Math.max(0, this.data.imgData.top),
+        );
+        this.data.imgData.left = Math.min(
+          this.data.image_width - this.data.imgData.width,
+          Math.max(0, this.data.imgData.left + widthDiff),
+        );
+      } else if (type === 'bottom-right') {
+        this.data.imgData.width = Math.min(this.data.image_width, Math.max(this.data.imgData.width + widthDiff));
+        this.data.imgData.height = Math.min(this.data.image_height, Math.max(this.data.imgData.height + heightDiff));
+        this.data.imgData.top = Math.min(
+          this.data.image_height - this.data.imgData.height,
+          Math.max(0, this.data.imgData.top),
+        );
+        this.data.imgData.left = Math.min(
+          this.data.image_width - this.data.imgData.width,
+          Math.max(0, this.data.imgData.left),
+        );
+      }
+
+      if (type === 'top') {
+        this.data.imgData.height = Math.min(this.data.image_height, Math.max(0, this.data.imgData.height - heightDiff));
+        this.data.imgData.top = Math.min(
+          this.data.image_height - this.data.imgData.height,
+          Math.max(0, this.data.imgData.top + heightDiff),
+        );
+      } else if (type === 'bottom') {
+        this.data.imgData.height = Math.min(this.data.image_height, Math.max(this.data.imgData.height + heightDiff));
+        this.data.imgData.top = Math.min(
+          this.data.image_height - this.data.imgData.height,
+          Math.max(0, this.data.imgData.top),
+        );
+      } else if (type === 'left') {
+        this.data.imgData.width = Math.min(this.data.image_width, Math.max(this.data.imgData.width - widthDiff));
+        this.data.imgData.left = Math.min(
+          this.data.image_width - this.data.imgData.width,
+          Math.max(0, this.data.imgData.left + widthDiff),
+        );
+      } else if (type === 'right') {
+        this.data.imgData.width = Math.min(this.data.image_width, Math.max(this.data.imgData.width + widthDiff));
+        this.data.imgData.left = Math.min(
+          this.data.image_width - this.data.imgData.width,
+          Math.max(0, this.data.imgData.left),
+        );
+      }
+
+      if (type === 'move') {
+        this.data.imgData.top = Math.min(
+          this.data.image_height - this.data.imgData.height,
+          Math.max(0, this.data.imgData.top + heightDiff),
+        );
+        this.data.imgData.left = Math.min(
+          this.data.image_width - this.data.imgData.width,
+          Math.max(0, this.data.imgData.left + widthDiff),
+        );
+      }
+      this.data.drag.startX = clientX;
+      this.data.drag.startY = clientY;
+    },
+    /**
+     * 鼠标抬起
+     */
+    mouseUp() {
+      this.data.drag.dragging = false;
+      document.querySelector('.background-img').style.cursor = 'auto';
+    },
   },
 };
 </script>
@@ -141,4 +316,79 @@ export default {
   align-items: center;
   padding: 20px 0;
 }
+
+.background-img {
+  height: 310px;
+  border: 1px dashed rgba(0, 0, 0, 8%);
+
+  .img-set {
+    position: relative;
+    display: inline-grid;
+    grid-template:
+      ' . . . ' 2px
+      ' . img . ' auto
+      ' . . . ' 2px
+      / 2px auto 2px;
+
+    img {
+      object-fit: cover;
+    }
+
+    .horizontal-line,
+    .vertical-line {
+      background-color: $main-color;
+    }
+
+    .horizontal-line {
+      width: 100%;
+      height: 2px;
+      cursor: ns-resize;
+    }
+
+    .vertical-line {
+      width: 2px;
+      height: 100%;
+      cursor: ew-resize;
+    }
+
+    .dot {
+      z-index: 1;
+      width: 6px;
+      height: 6px;
+      background-color: $main-color;
+
+      &.top-left {
+        top: -2px;
+        left: -2px;
+      }
+
+      &.top-right {
+        top: -2px;
+        right: 2px;
+      }
+
+      &.bottom-left {
+        bottom: 2px;
+        left: -2px;
+      }
+
+      &.bottom-right {
+        right: 2px;
+        bottom: 2px;
+      }
+
+      &.top-left,
+      &.bottom-right {
+        position: relative;
+        cursor: nwse-resize;
+      }
+
+      &.top-right,
+      &.bottom-left {
+        position: relative;
+        cursor: nesw-resize;
+      }
+    }
+  }
+}
 </style>

+ 12 - 0
src/views/book/courseware/data/drawing.js

@@ -30,6 +30,18 @@ export function getDrawingData() {
     image_width: 500, // 图片宽度px
     image_height: 500, // 图片高度px
     file_id_list: [],
+    drag: {
+      dragging: false,
+      startX: 0,
+      startY: 0,
+      type: '',
+    },
+    imgData: {
+      width: 0,
+      height: 0,
+      top: 0,
+      left: 0,
+    },
     mind_map: {
       node_list: [
       ], // 思维导图数据

+ 27 - 1
src/views/book/courseware/preview/components/drawing/DrawingPreview.vue

@@ -6,12 +6,24 @@
     <div
       class="img-box"
       :style="{
-        background: image_url ? 'url(' + image_url + ') center / contain no-repeat' : '#DCDFE6',
+        background: image_url ? '' : '#DCDFE6',
         width: data.image_width + 'px',
         height: data.image_height + 'px',
         border: '1px dotted #DCDFE6',
       }"
     >
+      <div
+        v-if="image_url"
+        class="img-set"
+        :style="{ top: `${data.imgData.top - 9}px`, left: `${data.imgData.left}px` }"
+      >
+        <img
+          :src="image_url"
+          draggable="false"
+          alt="背景图"
+          :style="{ width: `${data.imgData.width}px`, height: `${data.imgData.height}px` }"
+        />
+      </div>
       <!-- 如果是查看答案模式 v-if 下面画画的vue-esign不显示 -->
       <!-- <img :src="answer.answer_list[0].answer" alt="" /> -->
       <vue-esign
@@ -126,6 +138,20 @@ export default {
   margin: 20px auto;
 }
 
+.img-set {
+  position: relative;
+  display: inline-grid;
+  grid-template:
+    ' . . . ' 2px
+    ' . img . ' auto
+    ' . . . ' 2px
+    / 2px auto 2px;
+
+  img {
+    object-fit: cover;
+  }
+}
+
 .pen {
   display: flex;
   align-items: center;

+ 1 - 1
src/views/book/courseware/preview/components/upload_preview/UploadPreviewPreview.vue

@@ -105,7 +105,7 @@ export default {
       this.source_list = [[], [], [], []];
       this.data.file_list.forEach((item) => {
         const suffix = item.file_name.slice(item.file_name.lastIndexOf('.') + 1, item.file_name.length).toLowerCase();
-        if (suffix === 'mp3' || suffix === 'wma') {
+        if (suffix === 'mp3' || suffix === 'wma' || suffix === 'wav') {
           this.source_list[0].push(item);
         } else if (suffix === 'mp4' || suffix === 'mov') {
           this.source_list[1].push(item);