vu3+elementPuls,裁剪上传组件,单图和多图上传

有需要的直接cv就可以用 写的很菜 够我自己用了

裁剪组件来自gitee.com/GLUESTICK/v…

裁剪组件

<template>
  <div>
    <div @click="handleOpen" v-if="showChooseBtn === true && isModal === true">
      <slot name="openImgCutter"></slot>
      <slot name="open"></slot>
    </div>
    <button
      type="button"
      v-if="!$slots.openImgCutter && !$slots.open && isModal === true"
      class="btn btn-primary"
      @click="handleOpen"
    >
      {{ label }}
    </button>
    <transition name="fade">
      <div
        v-if="visible"
        :class="isModal === true ? 'mask vue-img-cutter' : ''"
        ref="mask"
      >
        <div
          :class="isModal === true ? 'dialogBoxModal' : 'dialogBox'"
          v-if="visible"
        >
          <transition
            name="fade"
            enter-class="fade-in-enter"
            enter-active-class="fade-in-active"
            leave-class="fade-out-enter"
            leave-active-class="fade-out-active"
          >
            <div
              ref="dialogMainModalRef"
              :class="isModal === true ? 'dialogMainModal' : 'dialogMain'"
              :style="
                'width:' + (isModal === true ? boxWidth + 32 : boxWidth) + 'px'
              "
            >
              <div class="toolMain">
                <div class="tool-title" v-if="isModal === true">
                  图片裁剪
                  <span class="closeIcon" @click="handleClose">×</span>
                </div>
                <div
                  ref="toolBox"
                  :style="'height:' + boxHeight + 'px;width:' + boxWidth + 'px'"
                  @mousemove="controlBtnMouseMove"
                  @mouseup="controlBtnMouseUp"
                  @mouseleave="controlBtnMouseUp"
                  class="toolBox"
                >
                  <!--选取图片-->
                  <div
                    class="tips"
                    v-show="!drawImg.img && showChooseBtn === true"
                  >
                    <div class="btn btn-warning btn-xs" @click="chooseImg">
                      {{ label }}
                    </div>
                  </div>
                  <!--工具栏-->
                  <div
                    v-if="tool == true"
                    v-show="
                      drawImg.img &&
                      dropImg.active !== true &&
                      controlBox.disable == true &&
                      toolBox.disable == true
                    "
                    class="dockMain"
                    :style="'background:' + toolBgc"
                    @mouseenter="dropImgOff"
                  >
                    <div class="dockBtn" v-if="rate">
                      <slot name="ratio"> Ratio: </slot>
                      {{ rate }}
                    </div>
                    <div class="dockBtn" @click="scaleReset">
                      <slot name="scaleReset"> Scale: </slot>
                      {{
                        drawImg.swidth > 0
                          ? (drawImg.width / drawImg.swidth).toFixed(2)
                          : "-"
                      }}
                    </div>
                    <div
                      v-if="originalGraph === false"
                      @click="turnImg(-90)"
                      class="dockBtn"
                    >
                      <slot name="turnLeft"> ↳ </slot>
                    </div>
                    <div
                      v-if="originalGraph === false"
                      @click="turnImg(90)"
                      class="dockBtn"
                    >
                      <slot name="turnRight"> ↲ </slot>
                    </div>
                    <div
                      v-if="originalGraph === false"
                      @click="turnReset()"
                      class="dockBtn"
                    >
                      <slot name="reset"> ↻ </slot>
                    </div>
                    <div
                      v-if="originalGraph === false"
                      class="dockBtnScrollBar"
                    >
                      <div
                        ref="dockBtnScrollControl"
                        @mousemove="scrollBarControlMove"
                        @mousedown="scrollBarControlOn"
                        @mouseleave="scrollBarControlOff"
                        @mouseup="scrollBarControlOff"
                        :style="'left:' + rotateControl.position + 'px'"
                        class="scrollBarControl"
                      ></div>
                      <div
                        v-if="rotateControl.active == true"
                        class="scrollBarText"
                        :style="'left:' + rotateControl.position + 'px'"
                      >
                        {{ rotateImg.angle.toFixed(0) + "°" }}
                      </div>
                    </div>
                    <div
                      v-if="originalGraph === false"
                      @click="flipHorizontal"
                      class="dockBtn"
                    >
                      <slot name="flipHorizontal"> ⇆ </slot>
                    </div>
                    <div
                      v-if="originalGraph === false"
                      @click="flipVertically"
                      class="dockBtn"
                    >
                      <slot name="flipVertically"> ⇅ </slot>
                    </div>
                  </div>
                  <!--裁剪区域-->
                  <div
                    v-show="drawImg.img != null"
                    ref="toolBoxControl"
                    @mousedown="toolBoxMouseDown"
                    @mouseup="toolBoxMouseUp"
                    @mousemove="toolBoxMouseMove"
                    @mouseleave="toolBoxMouseLeave"
                    class="toolBoxControl"
                    :style="{
                      pointerEvents: moveAble ? 'auto' : 'none',
                    }"
                  >
                    <div class="toolBoxControlBox">
                      <div class="controlBox">
                        <!--蚂蚁线-->
                        <div
                          class="controlBoxInnerLine controlBoxInnerLineTop"
                        ></div>
                        <div
                          class="controlBoxInnerLine controlBoxInnerLineBottom"
                        ></div>
                        <div
                          class="controlBoxInnerLine controlBoxInnerLineLeft"
                        ></div>
                        <div
                          class="controlBoxInnerLine controlBoxInnerLineRight"
                        ></div>
                        <!--工具栏提示-->
                        <div class="selectArea">
                          宽:{{ showToolBoxWidth }} 高:{{
                            showToolBoxHeight
                          }}
                          (x:{{ showToolBoxX }},y:{{ showToolBoxY }})
                        </div>
                        <!--操作杆-->
                        <div
                          data-name="leftUp"
                          v-if="sizeChange === true"
                          @mousedown="controlBtnMouseDown($event, 'leftUp')"
                          class="leftUp controlBtn"
                        ></div>
                        <div
                          data-name="leftDown"
                          v-if="sizeChange === true"
                          @mousedown="controlBtnMouseDown($event, 'leftDown')"
                          class="leftDown controlBtn"
                        ></div>
                        <div
                          data-name="rightUp"
                          v-if="sizeChange === true"
                          @mousedown="controlBtnMouseDown($event, 'rightUp')"
                          class="rightUp controlBtn"
                        ></div>
                        <div
                          data-name="rightDown"
                          v-if="sizeChange === true"
                          @mousedown="controlBtnMouseDown($event, 'rightDown')"
                          class="rightDown controlBtn"
                        ></div>

                        <div
                          data-name="topCenter"
                          v-if="
                            sizeChange === true && !rate && toolBox.width > 20
                          "
                          @mousedown="controlBtnMouseDown($event, 'topCenter')"
                          class="topCenter controlBtn"
                        ></div>
                        <div
                          data-name="downCenter"
                          v-if="
                            sizeChange === true && !rate && toolBox.width > 20
                          "
                          @mousedown="controlBtnMouseDown($event, 'downCenter')"
                          class="downCenter controlBtn"
                        ></div>
                        <div
                          data-name="leftCenter"
                          v-if="
                            sizeChange === true && !rate && toolBox.height > 20
                          "
                          @mousedown="controlBtnMouseDown($event, 'leftCenter')"
                          class="leftCenter controlBtn"
                        ></div>
                        <div
                          data-name="rightCenter"
                          v-if="
                            sizeChange === true && !rate && toolBox.height > 20
                          "
                          @mousedown="
                            controlBtnMouseDown($event, 'rightCenter')
                          "
                          class="rightCenter controlBtn"
                        ></div>
                      </div>
                      <div
                        class="toolBoxControlLine toolBoxControlLineItem-1"
                      ></div>
                      <div
                        class="toolBoxControlLine toolBoxControlLineItem-2"
                      ></div>
                      <div
                        class="toolBoxControlLine toolBoxControlLineItem-3"
                      ></div>
                      <div
                        class="toolBoxControlLine toolBoxControlLineItem-4"
                      ></div>
                    </div>
                  </div>
                  <!-- <div class="copyright">
                                        <a
                                            v-if="!DoNotDisplayCopyright"
                                            target="_blank"
                                            href="https://github.com/acccccccb/vue-img-cutter"
                                            rel="nofollow"
                                        >
                                            vue-img-cutter {{ version }}
                                        </a>
                                    </div> -->
                  <!--画布-->
                  <canvas
                    class="canvasSelectBox"
                    ref="canvasSelectBox"
                    :width="boxWidth"
                    @mousedown="dropImgOn"
                    @mouseup="dropImgOff"
                    @mousemove="dropImgMove"
                    :height="boxHeight"
                  ></canvas>
                  <canvas
                    class="canvas"
                    ref="canvas"
                    :width="boxWidth"
                    :height="boxHeight"
                  ></canvas>
                </div>
              </div>
              <div class="i-dialog-footer" style="height: 40px">
                <input
                  @change="putImgToCanv"
                  ref="inputFile"
                  type="file"
                  :accept="accept"
                  style="width: 1px; height: 1px; border: none; opacity: 0"
                />
                <span @click="chooseImg">
                  <slot name="choose">
                    <div
                      class="btn btn-primary btn-primary-plain"
                      v-if="showChooseBtn === true"
                    >
                      {{ label }}
                    </div>
                  </slot>
                </span>
                <div class="btn-group fr">
                  <span @click="handleClose">
                    <slot name="cancel">
                      <button type="button" class="btn btn-default">
                        取消
                      </button>
                    </slot>
                  </span>
                  <span @click="cropPicture(false)">
                    <slot name="confirm">
                      <button
                        type="button"
                        class="btn btn-primary"
                        style="margin-left: 15px"
                        :disabled="!drawImg.img"
                      >
                        确定
                      </button>
                    </slot>
                  </span>
                </div>
              </div>
            </div>
          </transition>
          <div style="clear: both"></div>
        </div>
      </div>
    </transition>
  </div>
</template>
<script>
import config from "../../package.json";

export default {
  name: "ImgCutter",
  props: {
    crossOrigin: {
      // 是否设置图片跨域
      type: Boolean,
      default: false,
      required: false,
    },
    crossOriginHeader: {
      // 是否设置图片跨域
      type: String,
      default: "*",
      required: false,
    },
    label: {
      // 按钮文字
      type: String,
      default: "选择图片",
      required: false,
    },
    isModal: {
      // 是否已弹窗形式展示
      type: Boolean,
      default: true,
      required: false,
    },
    lockScroll: {
      // 是否在弹窗出现时锁定body
      type: Boolean,
      default: true,
      required: false,
    },
    showChooseBtn: {
      // 是否显示选择图片按钮 如果需要兼容IE9 则将此参数改为false
      type: Boolean,
      default: true,
      required: false,
    },
    boxWidth: {
      // 裁剪窗口高度
      type: Number,
      default: 800,
      required: false,
    },
    boxHeight: {
      // 裁剪窗口高度
      type: Number,
      default: 400,
      required: false,
    },
    cutWidth: {
      // 默认裁剪宽度
      type: Number,
      default: 200,
      required: false,
    },
    cutHeight: {
      // 默认裁剪高度
      type: Number,
      default: 200,
      required: false,
    },
    rate: {
      // 按比例裁剪
      type: String,
      default: null,
      required: false,
    },
    tool: {
      // 是否显示工具栏
      type: Boolean,
      default: true,
      required: false,
    },
    toolBgc: {
      // 工具栏背景色
      type: String,
      default: "#fff",
      required: false,
    },
    imgMove: {
      // 能否拖动图片
      type: Boolean,
      default: true,
      required: false,
    },
    sizeChange: {
      // 能否调整裁剪尺寸
      type: Boolean,
      default: true,
      required: false,
    },
    originalGraph: {
      // 是否为原图裁剪
      type: Boolean,
      default: false,
      required: false,
    },
    moveAble: {
      // 能否调整裁剪区域位置
      type: Boolean,
      default: true,
      required: false,
    },
    previewMode: {
      // 裁剪过程中是否返回裁剪结果 裁剪原图卡顿时将此项设置为false
      type: Boolean,
      default: true,
      required: false,
    },
    CuttingOriginal: {
      // 是否裁剪原图
      type: Boolean,
      default: false,
      required: false,
    },
    WatermarkText: {
      // 水印文字
      type: String,
      default: "",
      required: false,
    },
    WatermarkTextFont: {
      // 水印文字样式
      type: String,
      default: "12px Sans-serif",
      required: false,
    },
    WatermarkTextColor: {
      // 水印文字颜色
      type: String,
      default: "#fff",
      required: false,
    },
    WatermarkTextX: {
      // 水印横向位置
      type: Number,
      default: 0.95,
      required: false,
    },
    WatermarkTextY: {
      // 水印纵向位置
      type: Number,
      default: 0.95,
      required: false,
    },
    smallToUpload: {
      // 选择的图片宽高均小于裁剪宽高度时候直接上传原图
      type: Boolean,
      default: false,
      required: false,
    },
    saveCutPosition: {
      // 是否保存上一次裁剪位置
      type: Boolean,
      default: false,
      required: false,
    },
    scaleAble: {
      // 是否允许缩放图片
      type: Boolean,
      default: true,
      required: false,
    },
    index: {
      // 自定义参数 返回结果时会带入此值
      default: null,
      required: false,
    },
    fileType: {
      // 文件类型
      default: "png",
      required: false,
      type: String,
    },
    toolBoxOverflow: {
      // 是否允许裁剪框超出图片
      type: Boolean,
      default: true,
      required: false,
    },
    DoNotDisplayCopyright: {
      type: Boolean,
      default: false,
      required: false,
    },
    // 裁剪后的图片质量
    quality: {
      type: Number,
      default: 1,
      required: false,
    },
    accept: {
      type: String,
      default: "image/gif, image/jpeg ,image/png",
      required: false,
    },
  },
  model: [
    "label",
    "boxWidth",
    "boxHeight",
    "rate",
    "tool",
    "DoNotDisplayCopyright",
  ],
  data() {
    let toolBoxWidth, toolBoxHeight;

    toolBoxWidth = this.boxWidth / 2;
    toolBoxHeight = this.boxHeight / 2;
    return {
      version: "",
      visible: false,
      fileName: "",
      cutImageObj: null,
      onPrintImgTimmer: null,
      toolBoxPosition: {
        x: 0,
        y: 0,
      },
      drawImg: {
        img: null, //规定要使用的图像、画布或视频
        sx: 0, //开始剪切的 x 坐标位置
        sy: 0, //开始剪切的 y 坐标位置
        swidth: 0, //被剪切图像的宽度
        sheight: 0, //被剪切图像的高度
        x: 0, //在画布上放置图像的 x 坐标位置
        y: 0, //在画布上放置图像的 y 坐标位置
        width: 0, //要使用的图像的宽度
        height: 0, //要使用的图像的高度
      },
      toolBox: {
        disable: true,
        width: toolBoxWidth,
        height: toolBoxHeight,
        x: 0,
        y: 0,
        boxMove: {
          start: {
            x: 0,
            y: 0,
          },
          moveTo: {
            x: 0,
            y: 0,
          },
        },
      },
      dropImg: {
        active: false,
        pageX: 0,
        pageY: 0,
        params: {},
      },
      // 旋转
      rotateImg: {
        angle: 0,
      },
      rotateControl: {
        active: false,
        start: {
          x: 0,
          y: 0,
        },
        position: 100,
      },
      // 缩放
      scaleImg: {
        rate: 0,
        params: {},
      },
      controlBox: {
        disable: true,
        btnName: "",
        start: {
          x: 0,
          y: 0,
          width: 0,
          height: 0,
        },
      },
      selectBox: false,
      selectBoxColor: "rgba(0,0,0,0.6)",
      isFlipHorizontal: false, //是否水平翻转
      isFlipVertically: false, // 是否垂直翻转
    };
  },
  mounted() {
    this.version = config.version;
    // 是否为弹窗模式
    if (this.isModal === false) {
      this.visible = true;
      this.$nextTick(() => {
        if (this.$refs["toolBox"]) {
          this.$refs["toolBox"].onmousewheel = this.scaleImgWheel;
          this.$refs["toolBox"].addEventListener(
            "DOMMouseScroll",
            this.scaleImgWheel
          );
        }
      });
    }
  },
  methods: {
    handleOpen(img) {
      let init = (callback) => {
        if (this.$refs["toolBox"]) {
          this.$refs["toolBox"].onmousewheel = this.scaleImgWheel;
          this.$refs["toolBox"].addEventListener(
            "DOMMouseScroll",
            this.scaleImgWheel
          );
        }

        // 判断下窗口高度
        if (this.isModal === true) {
          if (this.lockScroll === true) {
            document.body.style.overflowY = "hidden";
          }
          let dialogBoxHeight =
            this.$refs["dialogMainModalRef"].offsetHeight + 200;

          let windowHeight = window.innerHeight;

          let mask = this.$refs["mask"];

          if (dialogBoxHeight > windowHeight) {
            mask.style.overflowY = "scroll";
          } else {
            mask.style.overflowY = "hidden";
          }
        }
        if (callback && typeof callback === "function") {
          callback();
        }
      };

      // 如果传入了图片
      if (img && typeof img == "object" && img.src) {
        if (img.name) {
          let $image = new Image();

          if (this.crossOrigin === true) {
            $image.crossOrigin = this.crossOriginHeader;
          }
          $image.name = img.name;
          // $image.width = img.width;
          // $image.height = img.height;
          // $image.style.width = '1px';
          // $image.style.height = '1px';
          $image.style.position = "fixed";
          $image.style.top = -5000 + "px";
          $image.style.opacity = 0;
          $image.onerror = (e) => {
            console.error("图片加载失败");
            this.$emit("error", {
              index: this.index,
              event: e,
              msg: "图片加载失败",
            });
            this.clearCutImageObj();
          };
          $image.onload = () => {
            if ($image.complete === true) {
              this.visible = true;
              this.$nextTick(() => {
                init(() => {
                  this.importImgToCanv($image);
                });
              });
            } else {
              throw new Error("图片加载失败");
              // this.handleClose();
            }
          };
          $image.src = img.src;
          this.cutImageObj = $image;
          document.body.appendChild($image);
          this.$emit("onChooseImg", img, this.index);
        } else {
          throw new Error("传入参数必须包含:src,name");
        }
      } else {
        this.visible = true;
        this.$nextTick(() => {
          init();
        });
      }
    },
    handleClose() {
      this.clearAll();
      if (this.isModal === true) {
        if (this.lockScroll === true) {
          document.body.style.overflowY = "scroll";
        }
        this.$nextTick(() => {
          this.visible = false;
        });
      }
    },
    // 选择图片 e.stopPropagation();
    chooseImg() {
      this.$refs["inputFile"].click();
    },
    importImgToCanv(img) {
      let imgHeight = img.height;

      let imgWidth = img.width;

      let boxWidth = this.boxWidth;

      let boxHeight = this.boxHeight;

      let rate;

      let drawImg = { ...this.drawImg };

      this.fileName = img.name;
      drawImg.img = img;
      this.scaleImg.rate = imgWidth / imgHeight; // 缩放时用到此参数
      if (imgHeight < boxHeight && imgWidth < boxWidth) {
        rate = 1;
        drawImg.x = (boxWidth - imgWidth) / 2;
        drawImg.y = (boxHeight - imgHeight) / 2;
      } else {
        if (imgWidth / imgHeight <= boxWidth / boxHeight) {
          // 计算宽高比
          rate = boxHeight / imgHeight;
          drawImg.x = (boxWidth - imgWidth * rate) / 2;
        } else {
          rate = boxWidth / imgWidth;
          drawImg.y = (boxHeight - imgHeight * rate) / 2;
        }
      }
      drawImg.swidth = imgWidth;
      drawImg.sheight = imgHeight;
      drawImg.width = imgWidth * rate;
      drawImg.height = imgHeight * rate;
      drawImg.x = (boxWidth - drawImg.width) / 2;
      drawImg.y = (boxHeight - drawImg.height) / 2;
      // this.$set(this, 'drawImg', drawImg);
      this.drawImg = drawImg;
      this.printImg();
      this.putToolBox();
    },
    // 将选择的图片绘制到画布
    putImgToCanv(e) {
      let file;

      if (e.target.files) {
        file = e.target.files[0] || null;
      } else {
        // 如果是IE9及以下
        console.error("IE9及以下需要自己传入image对象");
        // this.$emit('importImage',e);
        return false;
      }
      if (file) {
        this.fileName = file.name;
        let reader = new FileReader();

        reader.readAsDataURL(file);
        reader.onload = (result) => {
          // 图片base64化
          let newUrl = result.target.result;

          let img = document.createElement("img");

          img.src = newUrl;
          let timmer = setInterval(() => {
            if (reader.readyState === 2) {
              clearInterval(timmer);
              if (
                !this.sizeChange &&
                this.smallToUpload &&
                img.width <= this.cutWidth &&
                img.height <= this.cutHeight
              ) {
                this.handleClose();
                file.name = this.changeFileName(file.name, this.fileType);
                this.$emit("cutDown", {
                  filename: this.changeFileName(file.name, this.fileType),
                  file: file,
                  index: this.index,
                });
                return;
              }
              let imgHeight = img.height;

              let imgWidth = img.width;

              let boxWidth = this.boxWidth;

              let boxHeight = this.boxHeight;

              let rate;

              let drawImg = { ...this.drawImg };

              drawImg.img = img;
              this.scaleImg.rate = imgWidth / imgHeight; // 缩放时用到此参数
              if (imgHeight < boxHeight && imgWidth < boxWidth) {
                rate = 1;
                drawImg.x = (boxWidth - imgWidth) / 2;
                drawImg.y = (boxHeight - imgHeight) / 2;
              } else {
                if (imgWidth / imgHeight <= boxWidth / boxHeight) {
                  // 计算宽高比
                  rate = boxHeight / imgHeight;
                  drawImg.x = (boxWidth - imgWidth * rate) / 2;
                } else {
                  rate = boxWidth / imgWidth;
                  drawImg.y = (boxHeight - imgHeight * rate) / 2;
                }
              }
              drawImg.swidth = imgWidth;
              drawImg.sheight = imgHeight;
              drawImg.width = imgWidth * rate;
              drawImg.height = imgHeight * rate;
              drawImg.x = (boxWidth - drawImg.width) / 2;
              drawImg.y = (boxHeight - drawImg.height) / 2;
              // this.$set(this, 'drawImg', drawImg);
              this.drawImg = drawImg;
              this.printImg();
              this.putToolBox();
            }
          }, 200);
        };
        this.$emit("onChooseImg", file, this.index);
      }
    },
    putToolBox() {
      if (
        this.toolBox.width === this.boxWidth / 2 ||
        this.toolBox.height === this.boxHeight / 2 ||
        this.saveCutPosition === false
      ) {
        this.toolBox.width =
          this.cutWidth > this.boxWidth ? this.boxWidth : this.cutWidth;
        this.toolBox.height =
          this.cutHeight > this.boxHeight ? this.boxHeight : this.cutHeight;
      }
      if (
        (this.toolBox.x === 0 && this.toolBox.y === 0) ||
        this.saveCutPosition === false
      ) {
        this.toolBox.x = this.boxWidth / 2 - this.toolBox.width / 2;
        this.toolBox.y = this.boxHeight / 2 - this.toolBox.height / 2;
      }
      this.checkToolBoxOverflow().then(() => {
        this.printImg();
      });

      this.drawControlBox(
        this.toolBox.width,
        this.toolBox.height,
        this.toolBox.x,
        this.toolBox.y
      );
    },
    // 判断裁剪框是否超出图片
    checkToolBoxOverflow() {
      return new Promise((resolve) => {
        if (!this.toolBoxOverflow) {
          // 如果裁剪框不能超出图片 则先判断图片尺寸
          // 如果图片尺寸长宽都超过裁剪框 不做处理
          // 如果图片尺寸小于裁剪框 将图片缩放至合适尺寸
          if (
            this.drawImg.width < this.toolBox.width ||
            this.drawImg.height < this.toolBox.height
          ) {
            const p = this.drawImg.width / this.drawImg.height;
            if (this.drawImg.width < this.toolBox.width) {
              this.drawImg.width = this.toolBox.width;
              this.drawImg.height = this.drawImg.width / p;
            }
            if (this.drawImg.height < this.toolBox.height) {
              this.drawImg.height = this.toolBox.height;
              this.drawImg.width = this.drawImg.height * p;
            }
            // 根据图片缩放重新调整位置
            this.drawImg.x = (this.boxWidth - this.drawImg.width) / 2;
            this.drawImg.y = (this.boxHeight - this.drawImg.height) / 2;
          }
          // 检查图片坐标是否超出
          console.log(
            "this.drawImg.y > this.toolBox.y",
            this.drawImg.y + this.drawImg.height,
            this.toolBox.y + this.toolBox.height
          );
          // 判断左右边界
          if (this.drawImg.x > this.toolBox.x) {
            this.drawImg.x = this.toolBox.x;
          }
          if (
            this.drawImg.x + this.drawImg.width <
            this.toolBox.x + this.toolBox.width
          ) {
            this.drawImg.x =
              this.toolBox.x + this.toolBox.width - this.drawImg.width;
          }
          // 判断上下边界
          if (this.drawImg.y > this.toolBox.y) {
            this.drawImg.y = this.toolBox.y;
          }
          if (
            this.drawImg.y + this.drawImg.height <
            this.toolBox.y + this.toolBox.height
          ) {
            this.drawImg.y =
              this.toolBox.y + this.toolBox.height - this.drawImg.height;
          }
        }
        resolve();
      });
    },
    isSupportFileApi() {
      if (
        window.File &&
        window.FileList &&
        window.FileReader &&
        window.Blob &&
        navigator.userAgent.indexOf("Edge") === -1 &&
        navigator.userAgent.indexOf("MSIE") === -1 &&
        navigator.userAgent.indexOf("Trident") === -1
      ) {
        return true;
      }
      return false;
    },
    dataURLtoFile(dataurl, filename) {
      //将图片转换为Base64
      let arr = dataurl.split(","),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]),
        n = bstr.length,
        u8arr = new Uint8Array(n);

      while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
      }
      if (this.isSupportFileApi()) {
        let file = new File([u8arr], filename, { type: mime });

        return file;
      }
      return "不支持File对象";
    },
    // clear both
    clearAll() {
      let _this = this;

      let c = _this.$refs["canvas"];

      let ctx = c.getContext("2d");

      ctx.clearRect(0, 0, c.width, c.height);
      let c1 = _this.$refs["canvasSelectBox"];

      let ctx1 = c1.getContext("2d");

      ctx1.clearRect(0, 0, c1.width, c1.height);
      let sx = _this.drawImg.sx;

      let sy = _this.drawImg.sy;

      this.drawImg = {
        img: null, //规定要使用的图像、画布或视频
        sx: sx, //开始剪切的 x 坐标位置
        sy: sy, //开始剪切的 y 坐标位置
        swidth: 0, //被剪切图像的宽度
        sheight: 0, //被剪切图像的高度
        x: 0, //在画布上放置图像的 x 坐标位置
        y: 0, //在画布上放置图像的 y 坐标位置
        width: 0, //要使用的图像的宽度
        height: 0, //要使用的图像的高度
      };
      this.isFlipHorizontal = false;
      this.isFlipVertically = false;
      this.$refs["inputFile"].value = "";
      this.rotateImg.angle = 0;
      this.drawImg.img = null;
      this.turnReset();
      this.clearCutImageObj();
      this.$emit("onClearAll", this.index);
    },
    clearCutImageObj() {
      if (this.cutImageObj !== null && this.cutImageObj !== undefined) {
        if (typeof this.cutImageObj.remove === "function") {
          this.cutImageObj.remove();
        } else {
          this.cutImageObj.removeNode();
        }
      }
      this.cutImageObj = null;
    },
    // draw control
    drawControlBox(width, height, x, y) {
      // 裁剪框是否能够超出图片
      if (!this.toolBoxOverflow) {
        // 如果不允许超出图片范围 则也不允许反选
        if (width < 1) {
          width = 1;
        }
        if (height < 1) {
          height = 1;
        }
        if (width > this.drawImg.width) {
          width = this.drawImg.width;
        }
        if (height > this.drawImg.height) {
          height = this.drawImg.height;
        }
        if (x < this.drawImg.x) {
          x = this.drawImg.x;
        }
        if (y < this.drawImg.y) {
          y = this.drawImg.y;
        }

        if (x > this.drawImg.x + this.drawImg.width - width) {
          x = this.drawImg.x + this.drawImg.width - width;
        }
        if (y > this.drawImg.y + this.drawImg.height - height) {
          y = this.drawImg.y + this.drawImg.height - height;
        }
      }
      // 阻止超出裁剪控件边界
      if (width > this.boxWidth) {
        width = this.boxWidth;
      }
      if (height > this.boxHeight) {
        height = this.boxHeight;
      }
      if (x < 0) {
        x = 0;
      }
      if (y < 0) {
        y = 0;
      }
      let $toolBoxControl = this.$refs["toolBoxControl"];

      let c = this.$refs["canvasSelectBox"];

      let ctx = c.getContext("2d");

      ctx.fillStyle = this.selectBoxColor;
      ctx.clearRect(0, 0, c.width, c.height);
      ctx.fillRect(0, 0, c.width, c.height);

      let toolBoxControlWidth, toolBoxControlHeight;

      if (this.rate && this.rate !== "") {
        let p = this.rate.split(":")[0] / this.rate.split(":")[1];

        if (p >= 1) {
          toolBoxControlWidth = width;
          toolBoxControlHeight = width / p;
          if (toolBoxControlHeight + y > this.drawImg.y + this.drawImg.height) {
            toolBoxControlHeight = this.drawImg.y + this.drawImg.height - y;
            toolBoxControlWidth = toolBoxControlHeight * p;
          }
        } else {
          toolBoxControlWidth = height * p;
          toolBoxControlHeight = height;
        }
      } else {
        toolBoxControlWidth = width;
        toolBoxControlHeight = height;
      }
      this.toolBox.width = Math.abs(toolBoxControlWidth);
      this.toolBox.height = Math.abs(toolBoxControlHeight);

      $toolBoxControl.style.width = Math.abs(toolBoxControlWidth) + "px";
      $toolBoxControl.style.height = Math.abs(toolBoxControlHeight) + "px";

      this.toolBox.boxMove.moveTo.x = x;
      this.toolBox.boxMove.moveTo.y = y;
      if (toolBoxControlWidth < 0) {
        x = x + toolBoxControlWidth;
      }
      if (toolBoxControlHeight < 0) {
        y = y + toolBoxControlHeight;
      }

      if (x + this.toolBox.width > this.boxWidth) {
        x = this.boxWidth - this.toolBox.width;
      }
      if (x < 0) {
        x = 0;
      }
      if (y + this.toolBox.height > this.boxHeight) {
        y = this.boxHeight - this.toolBox.height;
      }
      if (y < 0) {
        y = 0;
      }

      this.toolBoxPosition.x = x;
      this.toolBoxPosition.y = y;

      $toolBoxControl.style.left = x + "px";
      $toolBoxControl.style.top = y + "px";

      ctx.clearRect(
        x,
        y,
        Math.abs(toolBoxControlWidth),
        Math.abs(toolBoxControlHeight)
      );
      if (this.onPrintImgTimmer) {
        clearTimeout(this.onPrintImgTimmer);
      }
      this.onPrintImgTimmer = setTimeout(() => {
        this.cropPicture(true);
      }, 100);
    },

    resetToolBox() {
      if (this.toolBox.width < 0) {
        this.toolBox.boxMove.moveTo.x = this.toolBox.x - this.toolBox.width;
      }
      if (this.toolBox.height < 0) {
        this.toolBox.boxMove.moveTo.y = this.toolBox.y - this.toolBox.height;
      }
      this.toolBox.width = Math.abs(this.toolBox.width);
      this.toolBox.height = Math.abs(this.toolBox.height);
    },
    // toolBoxMouseDown
    toolBoxMouseDown(e) {
      let $toolBox = this.$refs["toolBoxControl"];

      this.toolBox.x = parseInt($toolBox.style.left.split("px")[0]);

      this.toolBox.y = parseInt($toolBox.style.top.split("px")[0]);

      this.toolBox.disable = false;
      this.dropImg.active = false;
      this.toolBox.boxMove.start = {
        x: e.pageX,
        y: e.pageY,
      };
    },
    toolBoxMouseMove(e) {
      if (this.dropImg.active) {
        this.dropImgMove(e);
      }
      if (this.toolBox.disable === false && this.moveAble === true) {
        let offsetX = e.pageX - this.toolBox.boxMove.start.x;

        let offsetY = e.pageY - this.toolBox.boxMove.start.y;

        let x = this.toolBox.x + offsetX;

        let y = this.toolBox.y + offsetY;

        this.drawControlBox(this.toolBox.width, this.toolBox.height, x, y);
      }
    },
    toolBoxMouseLeave() {
      this.toolBox.disable = true;
      if (this.onPrintImgTimmer) {
        clearTimeout(this.onPrintImgTimmer);
      }
      this.onPrintImgTimmer = setTimeout(() => {
        this.cropPicture(true);
      }, 100);
      this.resetToolBox();
    },
    toolBoxMouseUp() {
      this.toolBox.x = parseInt(this.toolBoxPosition.x);
      this.toolBox.y = parseInt(this.toolBoxPosition.y);
      this.toolBox.disable = true;
      this.dropImg.active = false;
      this.resetToolBox();
    },
    // 绘制图片
    printImg() {
      if (this.drawImg.img) {
        let canv = this.$refs["canvas"];

        let ctx = canv.getContext("2d");

        // 文字水印
        ctx.font = "18px bold 黑体";
        ctx.fillStyle = "#ff0";
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        ctx.save();
        ctx.clearRect(0, 0, canv.width, canv.height);
        ctx.translate(
          this.drawImg.x + this.drawImg.width / 2,
          this.drawImg.y + this.drawImg.height / 2
        );
        ctx.rotate((this.rotateImg.angle * Math.PI) / 180);
        ctx.translate(
          -(this.drawImg.x + this.drawImg.width / 2),
          -(this.drawImg.y + this.drawImg.height / 2)
        );
        ctx.translate(this.drawImg.x, this.drawImg.y);
        ctx.scale(
          this.isFlipHorizontal ? -1 : 1,
          this.isFlipVertically ? -1 : 1
        );
        ctx.drawImage(
          this.drawImg.img,
          this.drawImg.sx,
          this.drawImg.sy,
          this.drawImg.swidth,
          this.drawImg.sheight,
          this.isFlipHorizontal ? -this.drawImg.width : 0,
          this.isFlipVertically ? -this.drawImg.height : 0,
          this.drawImg.width,
          this.drawImg.height
        );
        ctx.translate(-this.drawImg.x, this.drawImg.y);

        ctx.restore();
        if (this.onPrintImgTimmer) {
          clearTimeout(this.onPrintImgTimmer);
        }
        this.onPrintImgTimmer = setTimeout(() => {
          this.cropPicture(true);
        }, 100);
      }
    },
    // 拖动图片
    dropImgOn(e) {
      if (this.imgMove === true) {
        this.dropImg.active = true;
        this.dropImg.params = { ...this.drawImg };
        this.dropImg.pageX = e.pageX;
        this.dropImg.pageY = e.pageY;
      }
    },
    dropImgOff() {
      this.dropImg.active = false;
      if (this.onPrintImgTimmer) {
        clearTimeout(this.onPrintImgTimmer);
      }
      this.onPrintImgTimmer = setTimeout(() => {
        this.cropPicture(true);
      }, 100);
    },
    dropImgMove(e) {
      if (this.dropImg.active && this.drawImg.img) {
        let drawImg = { ...this.drawImg };
        drawImg.x = this.dropImg.params.x - (this.dropImg.pageX - e.pageX);
        drawImg.y = this.dropImg.params.y - (this.dropImg.pageY - e.pageY);
        // 裁剪框是否能够超出图片
        if (!this.toolBoxOverflow) {
          if (drawImg.x > this.toolBox.x) {
            drawImg.x = this.toolBox.x;
          }
          if (drawImg.x < this.toolBox.x + this.toolBox.width - drawImg.width) {
            drawImg.x = this.toolBox.x + this.toolBox.width - drawImg.width;
          }

          if (drawImg.y > this.toolBox.y) {
            drawImg.y = this.toolBox.y;
          }
          if (
            drawImg.y <
            this.toolBox.y + this.toolBox.height - drawImg.height
          ) {
            drawImg.y = this.toolBox.y + this.toolBox.height - drawImg.height;
          }
        }
        // this.$set(this, 'drawImg', drawImg);
        this.drawImg = drawImg;
        this.printImg();
        e.stopPropagation();
      }
    },
    // 缩放
    scaleReset() {
      this.drawImg.width = this.drawImg.swidth;
      this.drawImg.height = this.drawImg.sheight;
      this.printImg();
    },
    scaleImgWheel(e) {
      if (this.drawImg.img && this.scaleAble === true) {
        let scale;

        // e是FF的事件。window.event是chrome/ie/opera的事件
        let ee = e || window.event;

        if (ee.wheelDelta) {
          //IE/Opera/Chrome
          scale = -(ee.wheelDelta / 40);
        } else if (ee.detail) {
          //Firefox
          scale = ee.detail;
        }

        let widthLimit = 50;
        this.drawImg.x =
          this.drawImg.width - scale * 9 > widthLimit
            ? this.drawImg.x + scale * 3
            : this.drawImg.x;
        this.drawImg.y =
          this.drawImg.width - scale * 9 > widthLimit
            ? this.drawImg.y + scale * 3
            : this.drawImg.y;
        this.drawImg.width =
          this.drawImg.width - scale * 9 > widthLimit
            ? this.drawImg.width - scale * 9
            : widthLimit;
        this.drawImg.height = this.drawImg.width / this.scaleImg.rate;

        this.checkToolBoxOverflow().then(() => {
          this.printImg();
          if (this.onPrintImgTimmer) {
            clearTimeout(this.onPrintImgTimmer);
          }
          this.onPrintImgTimmer = setTimeout(() => {
            this.cropPicture(true);
          }, 100);
        });
      }
      // let scrollTop = this.$refs['mask'].scrollTop;
      // window.scrollTo(this.$refs['mask'].scrollLeft,scrollTop);
      e.preventDefault();
      e.returnValue = false;
      return false;
    },
    // 水平翻转
    flipHorizontal() {
      if (this.drawImg.img) {
        if (this.isFlipHorizontal == false) {
          this.isFlipHorizontal = true;
        } else {
          this.isFlipHorizontal = false;
        }
        this.printImg();
      }
    },
    // 垂直翻转
    flipVertically() {
      if (this.drawImg.img) {
        if (this.isFlipVertically == false) {
          this.isFlipVertically = true;
        } else {
          this.isFlipVertically = false;
        }
        this.printImg();
      }
    },
    // 旋转
    turnImg(angle) {
      let startAngle = this.rotateImg.angle;

      let turnAngle = startAngle + angle;

      if (turnAngle >= -180 && turnAngle <= 180) {
        this.rotateImg.angle = turnAngle;
        this.rotateControl.position = (turnAngle / 180) * 100 + 100;
        this.printImg("rotate");
      }
    },
    turnReset() {
      this.rotateImg.angle = 0;
      this.rotateControl.position = 100;
      this.printImg("rotate");
    },
    // control box
    controlBtnMouseDown(e, btnName) {
      this.controlBox.disable = false;
      this.controlBox.btnName = btnName;
      this.controlBox.start.x = e.clientX;
      this.controlBox.start.y = e.clientY;
      this.controlBox.start.width = this.toolBox.width;
      this.controlBox.start.height = this.toolBox.height;
      e.stopPropagation();
    },
    controlBtnMouseUp(e) {
      this.controlBox.disable = true;
      this.dropImgOff();
      this.resetToolBox();
      this.toolBoxMouseUp();
      e.stopPropagation();
    },

    controlBtnMouseMove(e) {
      if (this.controlBox.disable === false) {
        let offsetX = e.clientX - this.controlBox.start.x;

        let offsetY = e.clientY - this.controlBox.start.y;

        let x, y;

        if (this.controlBox.btnName == "leftUp") {
          if (!this.rate) {
            x = this.toolBox.x + offsetX;
            y = this.toolBox.y + offsetY;
          } else {
            let p = this.rate.split(":")[0] / this.rate.split(":")[1];

            if (p >= 1) {
              x = this.toolBox.x + offsetX;
              y = this.toolBox.y + offsetX / p;
            } else {
              x = this.toolBox.x + offsetY * p;
              y = this.toolBox.y + offsetY;
            }
          }
          this.toolBox.width = this.controlBox.start.width - offsetX;
          this.toolBox.height = this.controlBox.start.height - offsetY;
        }
        if (this.controlBox.btnName == "rightUp") {
          if (!this.rate) {
            x = this.toolBox.x;
            y = this.toolBox.y + offsetY;
          } else {
            let p = this.rate.split(":")[0] / this.rate.split(":")[1];

            if (p >= 1) {
              x = this.toolBox.x;
              y = this.toolBox.y - offsetX / p;
            } else {
              x = this.toolBox.x;
              y = this.toolBox.y + offsetY;
            }
          }
          this.toolBox.width = this.controlBox.start.width + offsetX;
          this.toolBox.height = this.controlBox.start.height - offsetY;
        }
        if (this.controlBox.btnName == "rightDown") {
          x = this.toolBox.x;
          y = this.toolBox.y;
          this.toolBox.width = this.controlBox.start.width + offsetX;
          this.toolBox.height = this.controlBox.start.height + offsetY;
        }
        if (this.controlBox.btnName == "leftDown") {
          if (!this.rate) {
            x = this.toolBox.x + offsetX;
            y = this.toolBox.y;
          } else {
            let p = this.rate.split(":")[0] / this.rate.split(":")[1];

            if (p >= 1) {
              x = this.toolBox.x + offsetX;
              y = this.toolBox.y;
            } else {
              x = this.toolBox.x + -offsetY * p;
              y = this.toolBox.y;
            }
          }
          this.toolBox.width = this.controlBox.start.width - offsetX;
          this.toolBox.height = this.controlBox.start.height + offsetY;
        }
        if (this.controlBox.btnName == "topCenter") {
          x = this.toolBox.x;
          y = this.toolBox.y + offsetY;
          this.toolBox.width = this.controlBox.start.width;
          this.toolBox.height = this.controlBox.start.height - offsetY;
        }
        if (this.controlBox.btnName == "downCenter") {
          x = this.toolBox.x;
          y = this.toolBox.y;
          this.toolBox.width = this.controlBox.start.width;
          this.toolBox.height = this.controlBox.start.height + offsetY;
        }
        if (this.controlBox.btnName == "leftCenter") {
          x = this.toolBox.x + offsetX;
          y = this.toolBox.y;
          this.toolBox.width = this.controlBox.start.width - offsetX;
          this.toolBox.height = this.controlBox.start.height;
        }
        if (this.controlBox.btnName == "rightCenter") {
          x = this.toolBox.x;
          y = this.toolBox.y;
          this.toolBox.width = this.controlBox.start.width + offsetX;
          this.toolBox.height = this.controlBox.start.height;
        }
        this.drawControlBox(this.toolBox.width, this.toolBox.height, x, y);
      }
      e.stopPropagation();
    },
    changeFileName(fileName, type) {
      let index = fileName.lastIndexOf(".");

      return fileName.substr(0, index + 1) + (type === "jpeg" ? "jpg" : type);
    },
    cropPicture(doNotReset) {
      let _this = this;

      if (this.drawImg.img) {
        // get img
        let canvas = this.$refs["canvas"];

        // 文字水印
        if (this.WatermarkText && !doNotReset) {
          let ctx2 = canvas.getContext("2d");

          ctx2.font = this.WatermarkTextFont;
          ctx2.fillStyle = this.WatermarkTextColor;
          ctx2.textAlign = "right";
          ctx2.textBaseline = "bottom";
          ctx2.fillText(
            this.WatermarkText,
            this.toolBox.x + this.toolBox.width * this.WatermarkTextX,
            this.toolBox.y + this.toolBox.height * this.WatermarkTextY
          );
        }

        let tempImg = new Image();

        if (this.crossOrigin === true) {
          tempImg.crossOrigin = this.crossOriginHeader;
        }
        tempImg.src = canvas.toDataURL(`image/${this.fileType}`, _this.quality);

        if (!HTMLCanvasElement.prototype.toBlob) {
          Object.defineProperty(HTMLCanvasElement.prototype, "toBlob", {
            value: (callback, type, quality) => {
              if (window.atob) {
                setTimeout(() => {
                  let binStr = atob(
                    canvas.toDataURL(type, quality).split(",")[1]
                  );

                  let len = binStr.length,
                    arr = new Uint8Array(len);

                  let NewBlob;

                  for (let i = 0; i < len; i++) {
                    arr[i] = binStr.charCodeAt(i);
                  }
                  try {
                    NewBlob = new Blob([arr], {
                      type: `image/${_this.fileType}`,
                    });
                  } catch (e) {
                    window.BlobBuilder =
                      window.BlobBuilder ||
                      window.WebKitBlobBuilder ||
                      window.MozBlobBuilder ||
                      window.MSBlobBuilder;
                    if (e.name == "TypeError" && window.BlobBuilder) {
                      const BlobBuilder = window.BlobBuilder;

                      let bb = new BlobBuilder();

                      bb.append(arr.buffer);
                      NewBlob = bb.getBlob(`image/${_this.fileType}`);
                    }
                    if (e.name == "InvalidStateError") {
                      NewBlob = new Blob([arr.buffer], {
                        type: `image/${_this.fileType}`,
                      });
                    }
                  }
                  // TypeError old chrome and FF
                  callback(NewBlob);
                }, 200);
              } else {
                callback(false, {
                  type: `image/${_this.fileType}`,
                });
              }
            },
          });
        }
        canvas.toBlob((blob) => {
          if (blob) {
            let reader = new FileReader();

            reader.readAsDataURL(blob);
            reader.onload = () => {
              let timmer = setInterval(() => {
                if (reader.readyState == 2) {
                  clearInterval(timmer);
                  let newCanv = document.createElement("canvas");

                  let ctx = newCanv.getContext("2d");

                  // 原图裁剪 originalGraph
                  if (_this.originalGraph == true) {
                    let scale = _this.drawImg.width / _this.drawImg.swidth;

                    // 计算实际图像大小
                    let transWidth = _this.toolBox.width / scale;

                    let transHeight = _this.toolBox.height / scale;

                    newCanv.width = transWidth;
                    newCanv.height = transHeight;
                    // 重新计算裁剪坐标
                    let sx = (_this.toolBox.x - _this.drawImg.x) / scale;

                    let sy = (_this.toolBox.y - _this.drawImg.y) / scale;

                    let swidth = _this.drawImg.swidth;

                    let sheight = _this.drawImg.sheight;

                    // TODO 使原图裁剪支持旋转后的图像
                    // ctx.translate(sx + transWidth/2, sy + transHeight/2);
                    // ctx.rotate((_this.rotateImg.angle) * Math.PI / 180);
                    // ctx.translate(-(sx + transWidth/2), -(sy + transHeight/2));
                    ctx.translate(-sx, -sy);
                    ctx.drawImage(_this.drawImg.img, 0, 0, swidth, sheight);
                  } else {
                    newCanv.width = _this.toolBox.width;
                    newCanv.height = _this.toolBox.height;
                    let params = _this.toolBox;

                    if (_this.rate) {
                      let p =
                        _this.rate.split(":")[0] / _this.rate.split(":")[1];

                      let m = _this.rate.split(":")[0];

                      let n = _this.rate.split(":")[1];

                      if (m >= n) {
                        ctx.drawImage(
                          tempImg,
                          params.x,
                          params.y,
                          params.width,
                          params.width * p,
                          0,
                          0,
                          params.width,
                          params.width * p
                        );
                      } else {
                        ctx.drawImage(
                          tempImg,
                          params.x,
                          params.y,
                          params.width,
                          params.width / p,
                          0,
                          0,
                          params.width,
                          params.width / p
                        );
                      }
                    } else {
                      ctx.drawImage(
                        tempImg,
                        params.x,
                        params.y,
                        params.width,
                        params.height,
                        0,
                        0,
                        params.width,
                        params.height
                      );
                    }
                  }
                  newCanv.toBlob(
                    (newBlob) => {
                      let fileName = _this.changeFileName(
                        _this.fileName,
                        _this.fileType
                      );

                      if (!doNotReset) {
                        _this.handleClose();
                        _this.$emit("cutDown", {
                          index: _this.index,
                          fileName,
                          blob: newBlob,
                          file: _this.dataURLtoFile(
                            newCanv.toDataURL(
                              `image/${_this.fileType}`,
                              _this.quality
                            ),
                            fileName
                          ),
                          dataURL: newCanv.toDataURL(
                            `image/${_this.fileType}`,
                            _this.quality
                          ),
                        });
                      } else {
                        if (_this.previewMode) {
                          _this.$emit("onPrintImg", {
                            index: _this.index,
                            fileName,
                            blob: newBlob,
                            file: _this.dataURLtoFile(
                              newCanv.toDataURL(
                                `image/${_this.fileType}`,
                                _this.quality
                              ),
                              fileName
                            ),
                            dataURL: newCanv.toDataURL(
                              `image/${_this.fileType}`,
                              _this.quality
                            ),
                          });
                        }
                      }
                    },
                    `image/${_this.fileType}`,
                    _this.quality
                  );
                }
              }, 200);
            };
          } else {
            // IE9及以下
            let newCanv = document.createElement("canvas");

            newCanv.width = _this.toolBox.width;
            newCanv.height = _this.toolBox.height;
            let ctx = newCanv.getContext("2d");

            let params = _this.toolBox;

            if (_this.rate) {
              let p = _this.rate.split(":")[0] / _this.rate.split(":")[1];

              ctx.drawImage(
                tempImg,
                params.x,
                params.y,
                params.width,
                params.width * p,
                0,
                0,
                params.width,
                params.width * p
              );
            } else {
              ctx.drawImage(
                tempImg,
                params.x,
                params.y,
                params.width,
                params.height,
                0,
                0,
                params.width,
                params.height
              );
            }
            let fileName = _this.changeFileName(_this.fileName, _this.fileType);

            if (!doNotReset) {
              _this.handleClose();
              _this.$emit("cutDown", {
                fileName,
                dataURL: newCanv.toDataURL(
                  `image/${_this.fileType}`,
                  _this.quality
                ),
              });
            } else {
              _this.$emit("onPrintImg", {
                fileName,
                dataURL: newCanv.toDataURL(
                  `image/${_this.fileType}`,
                  _this.quality
                ),
              });
            }
          }
        }),
          `image/${_this.fileType}`,
          _this.quality;
      } else {
        if (!doNotReset) {
          console.warn("No picture selected");
          _this.$emit("error", {
            err: 1,
            msg: "No picture selected",
          });
        }
      }
    },
    scrollBarControlMove(e) {
      if (this.rotateControl.active) {
        let offsetX = e.pageX - this.rotateControl.start.x;

        //                let rotate = this.rotateImg.angle/360*100 + offsetX/100*100;
        let position = this.rotateControl.start.position + offsetX;

        if (position <= 0) {
          position = 0;
        }
        if (position >= 200) {
          position = 200;
        }
        this.rotateControl.position = position;
        this.rotateImg.angle = ((position - 100) / 100) * 180;
        this.printImg();
      }
    },
    scrollBarControlOn(e) {
      this.rotateControl.active = true;
      this.rotateControl.start.x = e.pageX;
      this.rotateControl.start.y = e.pageY;
      this.rotateControl.start.position = this.rotateControl.position;
    },
    scrollBarControlOff() {
      this.rotateControl.active = false;
    },
  },
  computed: {
    showToolBoxWidth() {
      let result;
      if (!this.originalGraph) {
        result = this.toolBox.width;
      } else {
        result =
          this.toolBox.width / (this.drawImg.width / this.drawImg.swidth);
      }
      return Number(result).toFixed(0);
    },
    showToolBoxHeight() {
      let result;
      if (!this.originalGraph) {
        result = this.toolBox.height;
      } else {
        result =
          this.toolBox.height / (this.drawImg.width / this.drawImg.swidth);
      }
      return Number(result).toFixed(0);
    },
    showToolBoxX() {
      let result;
      result = this.toolBoxPosition.x;
      return Number(result).toFixed(0);
    },
    showToolBoxY() {
      let result;
      result = this.toolBoxPosition.y;
      return Number(result).toFixed(0);
    },
  },
};
</script>
<style scoped>
.vue-img-cutter {
  font-size: 12px;
  line-height: 130%;
}

.fl {
  float: left;
}

.fr {
  float: right;
}

.i-dialog-footer {
  display: block;
  width: 100%;
  margin-top: 15px;
  margin-bottom: 15px;
  text-align: left;
}

.mask {
  background: rgba(0, 0, 0, 0.6);
  position: fixed;
  overflow-y: scroll;
  overflow-x: hidden;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 999;
}

.dialogBox {
  clear: both;
}

.dialogBoxModal {
  position: relative;
  padding-top: 100px;
  padding-bottom: 100px;
  clear: both;
}

.dialogMainModal {
  line-height: 125%;
  font-size: 16px;
  position: absolute;
  top: 100px;
  left: 50%;
  margin-bottom: 100px;
  transform: translateX(-50%);
  border: 1px solid rgba(0, 0, 0, 0.8);
  border-radius: 3px;
  box-sizing: border-box;
  padding: 15px 15px 0 15px;
  background: #fff;
  z-index: 1000;
  -webkit-touch-callout: none; /* iOS Safari */
  -webkit-user-select: none; /* Chrome/Safari/Opera */
  -khtml-user-select: none; /* Konqueror */
  -moz-user-select: none; /* Firefox */
  -ms-user-select: none; /* Internet Explorer/Edge */
  user-select: none; /* Non-prefixed version, currently*/
  animation: dialogShow 0.3s;
}

.dialogMain {
  line-height: 125%;
  font-size: 16px;
  box-sizing: border-box;
  background: #fff;
  z-index: 1000;
  -webkit-touch-callout: none; /* iOS Safari */
  -webkit-user-select: none; /* Chrome/Safari/Opera */
  -khtml-user-select: none; /* Konqueror */
  -moz-user-select: none; /* Firefox */
  -ms-user-select: none; /* Internet Explorer/Edge */
  user-select: none; /* Non-prefixed version, currently*/
}

@keyframes dialogShow {
  from {
    margin-top: -50px;
    opacity: 0;
  }
  to {
    margin-top: 0;
    opacity: 1;
  }
}

.toolMain {
  box-sizing: border-box;
}

.toolBox {
  border: 1px solid #dedede;
  background-image: linear-gradient(
      45deg,
      rgba(0, 0, 0, 0.25) 25%,
      transparent 0,
      transparent 75%,
      rgba(0, 0, 0, 0.25) 0
    ),
    linear-gradient(
      45deg,
      rgba(0, 0, 0, 0.25) 25%,
      transparent 0,
      transparent 75%,
      rgba(0, 0, 0, 0.25) 0
    );
  background-color: #eee;
  background-size: 30px 30px;
  background-position: 0 0, 15px 15px;
  position: relative;
}

.tool-title {
  margin-bottom: 10px;
}

.canvas {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 98;
}

.canvasSelectBox {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 99;
}

@keyframes zi-antBorder {
  0% {
    background-position: 0px 0px;
  }
  50% {
    background-position: 0px 5px;
  }
  100% {
    background-position: 0px 10px;
  }
}

.toolBoxControl {
  background: rgba(255, 255, 255, 0);
  /*background:#fff;*/
  position: absolute;
  z-index: 101;
  box-sizing: border-box;
  /*border: 1px dotted #409EFF;*/
}

.toolBoxControlBox {
  width: 100%;
  height: 100%;
  position: relative;
  background: transparent;
  z-index: 103;
  pointer-events: none;
}

.controlBoxInnerLine {
  position: absolute;
  z-index: 1;
  background-size: 10px 10px;
  background-image: linear-gradient(
    -45deg,
    rgba(64, 158, 255, 1) 25%,
    rgba(64, 158, 255, 0) 25%,
    rgba(64, 158, 255, 0) 50%,
    rgba(64, 158, 255, 1) 50%,
    rgba(64, 158, 255, 1) 75%,
    rgba(64, 158, 255, 0) 75%,
    rgba(64, 158, 255, 0)
  );
  -ms-animation: zi-antBorder 0.8s linear 0s infinite normal;
  -moz-animation: zi-antBorder 0.8s linear 0s infinite normal;
  -webkit-animation: zi-antBorder 0.8s linear 0s infinite normal;
  animation: zi-antBorder 0.8s linear 0s infinite normal;
  pointer-events: none;
}

.controlBoxInnerLineTop {
  height: 1px;
  width: 100%;
  top: -1px;
}

.controlBoxInnerLineBottom {
  height: 1px;
  width: 100%;
  bottom: -1px;
}

.controlBoxInnerLineLeft {
  height: 100%;
  width: 1px;
  left: -1px;
  top: 0;
}

.controlBoxInnerLineRight {
  height: 100%;
  width: 1px;
  right: -1px;
  top: 0;
}

.toolBoxControlLine {
  position: absolute;
  z-index: 1;
  background: transparent;
}

.toolBoxControlLineItem-1 {
  top: 33%;
  width: 100%;
  height: 1px;
  box-sizing: border-box;
  border-bottom: 1px dashed #409eff;
}

.toolBoxControlLineItem-2 {
  top: 66%;
  width: 100%;
  height: 1px;
  box-sizing: border-box;
  border-bottom: 1px dashed #409eff;
}

.toolBoxControlLineItem-3 {
  left: 33%;
  border-right: 1px dashed #409eff;
  height: 100%;
  width: 1px;
  box-sizing: border-box;
}

.toolBoxControlLineItem-4 {
  left: 66%;
  border-right: 1px dashed #409eff;
  height: 100%;
  width: 1px;
  box-sizing: border-box;
}

.controlBox {
  width: 100%;
  height: 100%;
  position: absolute;
  cursor: move;
  z-index: 1;
  pointer-events: none;
}

.controlBtn {
  border: 1px solid rgba(255, 255, 255, 0.5);
  box-sizing: border-box;
  width: 6px;
  height: 6px;
  background: #409eff;
  position: absolute;
  border-radius: 50%;
  z-index: 999;
  pointer-events: auto !important;
}

.leftUp {
  top: 0;
  left: 0;
  margin-left: -3px;
  margin-top: -3px;
  cursor: se-resize;
}

.leftDown {
  bottom: 0;
  left: 0;
  margin-left: -3px;
  margin-bottom: -3px;
  cursor: sw-resize;
}

.rightUp {
  top: 0;
  right: 0;
  margin-right: -3px;
  margin-top: -3px;
  cursor: sw-resize;
}

.rightDown {
  bottom: 0;
  right: 0;
  margin-right: -3px;
  margin-bottom: -3px;
  cursor: se-resize;
}

.topCenter {
  top: 0;
  right: 50%;
  margin-right: -3px;
  margin-top: -3px;
  cursor: s-resize;
}

.downCenter {
  bottom: 0;
  right: 50%;
  margin-right: -3px;
  margin-bottom: -3px;
  cursor: s-resize;
}

.leftCenter {
  top: 50%;
  left: 0;
  margin-left: -3px;
  margin-top: -3px;
  cursor: e-resize;
}

.rightCenter {
  top: 50%;
  right: 0;
  margin-right: -3px;
  margin-top: -3px;
  cursor: e-resize;
}

.toolBar {
  margin-top: 10px;
}

.selectArea {
  display: block;
  width: 260px;
  text-align: right;
  color: #fff;
  position: absolute;
  top: -20px;
  right: 0;
  font-size: 10px;
  user-select: none;
}

.tips {
  position: absolute;
  top: 50%;
  left: 50%;
  color: red;
  z-index: 101;
  transform: translate(-50%, -50%);
}

.btn {
  display: inline-block;
  text-align: center;
  background: #dedede;
  height: 40px;
  line-height: 40px;
  padding: 0 20px;
  box-sizing: border-box;
  border-radius: 4px;
  cursor: pointer;
  border: 1px solid;
  font-size: 14px;
  transition: background 0.3s, color 0.3s;
  -webkit-touch-callout: none; /* iOS Safari */
  -webkit-user-select: none; /* Chrome/Safari/Opera */
  -khtml-user-select: none; /* Konqueror */
  -moz-user-select: none; /* Firefox */
  -ms-user-select: none; /* Internet Explorer/Edge */
  user-select: none; /* Non-prefixed version, currently*/
}

.btn[disabled] {
  opacity: 0.6;
  color: #333;
  border-color: #dedede !important;
  background-color: #fff !important;
  cursor: default;
}

.btn[disabled]:hover {
  opacity: 0.6;
  color: #333 !important;
  border-color: #dedede !important;
  background-color: #fff !important;
}

.btn-default {
  color: #333;
  border-color: #dcdfe6;
  background-color: #fff;
  transition: background 0.3s, color 0.3s;
}

.btn-default:hover {
  color: #409eff;
  border-color: #c6e2ff;
  background-color: #ecf5ff;
}

.btn-primary {
  color: #fff;
  background-color: #409eff;
  border-color: #409eff;
  transition: background 0.3s, color 0.3s;
}

.btn-primary:hover {
  background: #66b1ff;
  border-color: #66b1ff;
  color: #fff;
}

.btn-warning {
  color: #fff;
  background-color: #e6a23c;
  border-color: #e6a23c;
}

.btn-warning:hover {
  color: #fff;
  background-color: #eeba6c;
  border-color: #e6a23c;
  transition: background 0.3s, color 0.3s;
}

.btn-primary-plain {
  color: #409eff;
  border-color: #c6e2ff;
  background-color: #ecf5ff;
  transition: background 0.3s, color 0.3s;
}

.btn-primary-plain:hover {
  background: #66b1ff;
  border-color: #66b1ff;
  color: #fff;
}

.btn-xs {
  height: 26px;
  line-height: 26px;
  padding: 0 10px;
  font-size: 12px;
}

.dialog-footer {
  float: right;
}

.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s;
}

.fade-enter,
.fade-leave-to {
  opacity: 0;
}

.fade-in-enter {
  margin-top: -50px;
  opacity: 0;
  transition: margin-top 0.2s, opacity 0.2s;
}

.fade-out-enter {
  transition: margin-top 0.2s, opacity 0.2s;
}

.fade-in-active {
  transition: margin-top 0.2s, opacity 0.2s;
}

.fade-out-active {
  margin-top: -50px;
  opacity: 0;
  transition: margin-top 0.2s, opacity 0.2s;
}

.file-input {
  height: 40px;
  line-height: 40px;
  padding: 0 10px;
  box-sizing: border-box;
  border-radius: 4px;
  border: 1px solid #dedede;
}

.file-input::placeholder {
  color: #c0c4cc;
}

.toolbar-item {
  display: inline-block;
}

.closeIcon {
  float: right;
  cursor: pointer;
  display: block;
  background: #c6e2ff;
  color: #fff;
  width: 18px;
  height: 18px;
  line-height: 18px;
  text-align: center;
  border-radius: 50%;
  margin: 0;
  padding: 0;
  transition: transform 0.3s, background 0.3s;
  /*transform: rotate(90deg);*/
}

.closeIcon:hover {
  background: #409eff;
  transform: rotate(180deg);
}

.copyright {
  font-size: 10px !important;
  clear: both !important;
  width: 100% !important;
  text-align: right !important;
  display: block !important;
  opacity: 0.5 !important;
  position: absolute !important;
  bottom: 0 !important;
  right: 0 !important;
  line-height: 100% !important;
  z-index: 100 !important;
}

.copyright a {
  color: #fff !important;
  text-decoration: none !important;
  position: relative !important;
  opacity: 1 !important;
  display: inline-block !important;
  padding: 2px !important;
  background: rgba(0, 0, 0, 0.4);
}

/*工具栏*/
.dockMain {
  position: absolute;
  z-index: 1002;
  bottom: 5px;
  left: 5px;
  /*transform: translateX(-50%);*/
  opacity: 0.5;
  transition: opacity 0.5s;
  /*width: 98%;*/
  box-sizing: border-box;
  padding: 5px 5px;
  border-radius: 5px;
}

.dockMain:hover {
  opacity: 1;
}

.dockBtn {
  font-size: 10px;
  cursor: pointer;
  display: inline-block;
  margin-right: 4px;
  color: #409eff;
  border: 1px solid #c6e2ff;
  background-color: #ecf5ff;
  padding: 1px 4px;
  border-radius: 3px;
  height: 20px;
  line-height: 16px;
  transition: background 0.2s, color 0.2s;
  -webkit-touch-callout: none; /* iOS Safari */
  -webkit-user-select: none; /* Chrome/Safari/Opera */
  -khtml-user-select: none; /* Konqueror */
  -moz-user-select: none; /* Firefox */
  -ms-user-select: none; /* Internet Explorer/Edge */
  user-select: none; /* Non-prefixed version, currently*/
}

.dockBtn:hover {
  color: #fff;
  background-color: #409eff;
  border-color: #409eff;
}

/* 旋转进度条 */
.dockBtnScrollBar {
  display: inline-block;
  margin-right: 4px;
  margin-left: 10px;
  background: #409eff;
  width: 200px;
  height: 10px;
  border-radius: 5px;
  position: relative;
  -webkit-touch-callout: none; /* iOS Safari */
  -webkit-user-select: none; /* Chrome/Safari/Opera */
  -khtml-user-select: none; /* Konqueror */
  -moz-user-select: none; /* Firefox */
  -ms-user-select: none; /* Internet Explorer/Edge */
  user-select: none; /* Non-prefixed version, currently*/
}

.scrollBarControl {
  position: absolute;
  cursor: pointer;
  background: #fff;
  border: 2px solid #409eff;
  box-sizing: border-box;
  border-radius: 50%;
  width: 20px;
  height: 20px;
  top: 50%;
  transform: translate(-50%, -50%);
  box-shadow: 0 0 3px #1f5184;
}

.scrollBarText {
  position: absolute;
  cursor: pointer;
  background: rgba(0, 0, 0, 0.7);
  color: #fff;
  top: -16px;
  height: 16px;
  line-height: 16px;
  text-align: center;
  font-size: 10px;
  white-space: nowrap;
  min-width: 50px;
  border-radius: 3px;
  transform: translate(-50%, -50%);
}
</style>

然后是接口文件

这一段要根据自己的情况来的

export const imgUrl = "换成自己的上传";
import request from '@/utils/system/request'
import { baseURL } from "@/utils/system/request";




export function updatedImg(file,biz) {
    const formData = new FormData();
    formData.append('file', file);
    formData.append('biz', biz);
    return request({
      url: baseURL + 'sys/common/uploadModeration',
      method: 'post',
      data: formData,
      headers: {
        'Content-Type': 'multipart/form-data' 
      }
    });
  }

单文件裁剪上传

<template>
  <div style="display: flex; flex-wrap: wrap" class="container">
    <div v-if="modelValue" class="image-container item">
      <img
        style="width: 146px; height: 146px"
        class="openImg"
        :src="imgUrl + modelValue"
      />
      <div class="overlay">
        <svg
          @click.stop="deleteImg"
          style="width: 30px; height: 30px; color: #fbfdff"
          data-v-3ef4f9a4=""
          xmlns="http://www.w3.org/2000/svg"
          viewBox="0 0 1024 1024"
        >
          <path
            fill="currentColor"
            d="M160 256H96a32 32 0 0 1 0-64h256V95.936a32 32 0 0 1 32-32h256a32 32 0 0 1 32 32V192h256a32 32 0 1 1 0 64h-64v672a32 32 0 0 1-32 32H192a32 32 0 0 1-32-32V256zm448-64v-64H416v64h192zM224 896h576V256H224v640zm192-128a32 32 0 0 1-32-32V416a32 32 0 0 1 64 0v320a32 32 0 0 1-32 32zm192 0a32 32 0 0 1-32-32V416a32 32 0 0 1 64 0v320a32 32 0 0 1-32 32z"
          ></path>
        </svg>
      </div>
    </div>
    <ImgCutter
      v-else
      class="item"
      :sizeChange="sizeChange"
      :cutWidth="cutWidth"
      :cutHeight="cutHeight"
      v-on:cutDown="cutDown"
    >
      <template #open>
        <div
          style="
            width: 146px;
            height: 146px;
            cursor: pointer;
            color: #a8abb2;
            background-color: #fbfdff;
            border: 1px dashed #c0ccda;
            display: flex;
            align-items: center;
            justify-content: center;
          "
        >
          <svg
            style="width: 30px; height: 30px"
            data-v-3ef4f9a4=""
            xmlns="http://www.w3.org/2000/svg"
            viewBox="0 0 1024 1024"
          >
            <path
              fill="currentColor"
              d="M480 480V128a32 32 0 0 1 64 0v352h352a32 32 0 1 1 0 64H544v352a32 32 0 1 1-64 0V544H128a32 32 0 0 1 0-64h352z"
            ></path>
          </svg>
        </div>
      </template>
    </ImgCutter>
  </div>
</template>

<script setup>
import ImgCutter from "./picturecropping.vue";

import { imgUrl, updatedImg } from "@/api/img.js";
const emit = defineEmits(["update:modelValue"]);

const props = defineProps({
  modelValue: {
    type: String,
    default: "",
  },

  biz: {
    type: String,
    default: "",
  },

  sizeChange: {
    type: Boolean,
    default: true,
  },

  cutWidth: {
    type: Number,
    default: 200,
  },
  cutHeight: {
    type: Number,
    default: 200,
  },
  max: {
    type: Number,
    default: 200,
  },
});

async function cutDown(value) {
  let data = await updatedImg(value.file, props.biz);

  emit("update:modelValue", data.result.path);
}
async function deleteImg() {
  var imgUrl = "";
  emit("update:modelValue", imgUrl);
}

// 删除图片
</script>

<style scoped lang="less">
.item {
  flex-basis: 20%;
}

.image-container {
  width: 150px;
  height: 150px;
  position: relative;
  display: inline-block;
  user-select: none;
}

.overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  opacity: 0;
  transition: opacity 0.3s ease;
  display: flex;
  justify-content: center;
  align-items: center;
  user-select: none;
}

.image-container:hover .overlay {
  opacity: 1;
}

.image-container:hover .delete-button {
  opacity: 1;
}
</style>

多文件上传

<template>
  <div style="display: flex; flex-wrap: wrap" class="container">
    <div
      v-for="(item, index) in modelValue"
      :key="item"
      class="image-container item"
    >
      <img
        style="width: 150px; height: 150px"
        class="openImg"
        :src="imgUrl + item"
      />
      <div class="overlay">
        <svg
          @click.stop="deleteImg(index)"
          style="width: 30px; height: 30px; color: #fbfdff"
          data-v-3ef4f9a4=""
          xmlns="http://www.w3.org/2000/svg"
          viewBox="0 0 1024 1024"
        >
          <path
            fill="currentColor"
            d="M160 256H96a32 32 0 0 1 0-64h256V95.936a32 32 0 0 1 32-32h256a32 32 0 0 1 32 32V192h256a32 32 0 1 1 0 64h-64v672a32 32 0 0 1-32 32H192a32 32 0 0 1-32-32V256zm448-64v-64H416v64h192zM224 896h576V256H224v640zm192-128a32 32 0 0 1-32-32V416a32 32 0 0 1 64 0v320a32 32 0 0 1-32 32zm192 0a32 32 0 0 1-32-32V416a32 32 0 0 1 64 0v320a32 32 0 0 1-32 32z"
          ></path>
        </svg>
      </div>
    </div>
    <ImgCutter
      class="item"
      v-if="max !== modelValue?.length"
      :sizeChange="sizeChange"
      :cutWidth="cutWidth"
      :cutHeight="cutHeight"
      v-on:cutDown="cutDown"
    >
      <template #open>
        <div
          style="
            width: 150px;
            height: 150px;
            cursor: pointer;
            color: #a8abb2;
            background-color: #fbfdff;
            border: 1px dashed #c0ccda;
            display: flex;
            align-items: center;
            justify-content: center;
          "
        >
          <svg
            style="width: 30px; height: 30px"
            data-v-3ef4f9a4=""
            xmlns="http://www.w3.org/2000/svg"
            viewBox="0 0 1024 1024"
          >
            <path
              fill="currentColor"
              d="M480 480V128a32 32 0 0 1 64 0v352h352a32 32 0 1 1 0 64H544v352a32 32 0 1 1-64 0V544H128a32 32 0 0 1 0-64h352z"
            ></path>
          </svg>
        </div>
      </template>
    </ImgCutter>
  </div>
</template>

<script setup>
import ImgCutter from "./picturecropping.vue";

import { imgUrl, updatedImg } from "@/api/img.js";
const emit = defineEmits(["update:modelValue"]);

const props = defineProps({
  modelValue: {
    type: Array,
    default: () => {
      [];
    },
  },

  biz: {
    type: String,
    default: "",
  },

  sizeChange: {
    type: Boolean,
    default: true,
  },

  cutWidth: {
    type: Number,
    default: 200,
  },
  cutHeight: {
    type: Number,
    default: 200,
  },
  max: {
    type: Number,
    default: 200,
  },
});

async function cutDown(value) {
  let data = await updatedImg(value.file, props.biz);

  var imgArr = [...props.modelValue];
  imgArr.push(data.result.path);

  emit("update:modelValue", imgArr);
}
async function deleteImg(index) {
  var imgArr = [...props.modelValue];
  imgArr.splice(index, 1);
  emit("update:modelValue", imgArr);
}

// 删除图片
</script>

<style scoped lang="less">
.item {
  flex-basis: 20%;
}

.image-container {
  width: 150px;
  height: 150px;
  position: relative;
  display: inline-block;
  user-select: none;
}

.overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  opacity: 0;
  transition: opacity 0.3s ease;
  display: flex;
  justify-content: center;
  align-items: center;
  user-select: none;
}

.image-container:hover .overlay {
  opacity: 1;
}

.image-container:hover .delete-button {
  opacity: 1;
}
</style>

使用方法

     <ImgCutter
     //图片地址 单图是字符串 多图的数组
        v-model=""
        
        :biz="
          'newUpload/store/logo/' +
          new Date().getFullYear() +
          '/' +
          dayjs().format('MM')
        "
        //裁剪框是否自由拖动
        :sizeChange="false"
      ></ImgCutter>