1

web技术分享| 图片上传与图片裁剪结合 vue3

 1 year ago
source link: https://blog.51cto.com/u_15232255/5919535
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

web技术分享| 图片上传与图片裁剪结合 vue3

精选 原创

anyRTC 2022-12-07 15:04:55 ©著作权

文章标签 图片上传 裁剪图片 web vue 前端 文章分类 其它 前端开发 阅读数168

  • 上传的图片限制长宽相同;

  • 只能上传图片;

  • 图片大小限制 500k

  • 当前项目仅需要上传的图片信息

项目组件使用

裁剪:vue-cropper

import "vue-cropper/dist/index.css";
import { VueCropper } from "vue-cropper";

上传: Upload 上传(element plus 组件)

实现理论:

  1. 通过组件 Upload 进行图片上传前校验方法判断是否需要裁剪、是否是图片、是否超过大小限制;
  2. 裁剪后的图片限制大小;
  3. 上传的图片信息暴漏出组件;

img-url 默认预览的图片
@img-upload 上传所需的最终图片

裁剪时弹窗显示

<!-- 上传 -->
  <el-upload
    class="uploader_cutsom"
    action=""
    :show-file-list="false"
    :accept="imgUpload.imgBmp"
    :http-request="updataImg"
    :before-upload="beforUpload"
  >
    <div v-if="imgUpload.imageUrl" class="w-full h-full relative">
      <img class="w-full h-full" :src="imgUpload.imageUrl" />
      <!-- 删除 -->
      <div
        @click="clearImg"
        class="absolute top-0 right-0 w-8 h-8 rounded flex items-center justify-center bg-black bg-opacity-30 text-white text-base z-20 cursor-pointer"
      >
        <i class="iconfont icon-shanchu"></i>
      </div>
    </div>

    <div v-else class="uploader_cutsom_default">
      <p>从你的计算机中</p>
      <p class="uploader_cutsom_default_button">选择文件</p>
    </div>
  </el-upload>
  <!-- 裁剪 -->
  <el-dialog
    v-model="imgUpload.dialogCropping"
    custom-class="dialog_custom"
    title="裁剪图片"
    append-to-body
    :close-on-press-escape="false"
    :show-close="false"
    :close-on-click-modal="false"
  >
    <div>
      <div class="h-96 w-full">
        <vueCropper
          ref="cropper"
          class="vue_cropper_custom"
          :img="imgCropping.imageUrl"
          :outputType="imgCropping.outputType"
          :autoCrop="imgCropping.autoCrop"
          :autoCropWidth="imgCropping.autoCropWidth"
          :autoCropHeight="imgCropping.autoCropHeight"
          :fixed="imgCropping.fixed"
          :fixedNumber="imgCropping.fixedNumber"
          :centerBox="imgCropping.centerBox"
          :full="imgCropping.full"
        ></vueCropper>
      </div>
    </div>
    <template #footer>
      <el-button v-focus @click="handleCropping($refs.cropper, false)">
        取消
      </el-button>
      <el-button
        v-focus
        type="primary"
        @click="handleCropping($refs.cropper, true)"
      >
        确定
      </el-button>
    </template>
  </el-dialog>
/** 图片地址传递 */
const prop = defineProps({
  imgUrl: {
    type: String,
    default: "",
  },
});
/** 事件通知 */
const emit = defineEmits(["img-upload"]);

/** 图片上传 */
const imgUpload = reactive({
  // 是否展示裁剪
  dialogCropping: false,
  isCropping: false, // 判断是否已经截图
  // 图片
  imageUrl: "",
  // 图片格式
  imgBmp: "image/*",
  // 图片名称
  imgName: "",
});
// 图片上传前校验
const beforUpload: UploadProps["beforeUpload"] = async (rawFile) => {
  // 判断是否需要进行裁剪
  imgUpload.isCropping = (await imgSize(rawFile)) as boolean;
  // 图片名称
  imgUpload.imgName = rawFile.name;
  if (imgUpload.isCropping) {
    if (rawFile.type.indexOf("image/") === -1) {
      ElMessage.error("请上传正确的图片格式");
      return false;
    } else if (rawFile.size / 1024 / 1024 > 0.5) {
      ElMessage.error("图片大小不能超过 500k");
      return false;
    }
  } else {
    // 进入裁剪
    imgCropping.imageUrl = URL.createObjectURL(rawFile);
    imgUpload.dialogCropping = true;
    return false;
  }
};
// 上传
const updataImg = async (data: any) => {
  // 如果未截图,打开裁剪
  if (imgUpload.isCropping) {
    // 图片预览
    imgUpload.imageUrl = URL.createObjectURL(data.file);
    imgUpload.dialogCropping = false;
    imgUpload.isCropping = false;
    // 图片信息
    emit("img-upload", data.file);
  }
};

/** 图片裁剪 */
const imgCropping = reactive({
  imageUrl: "",
  // 裁剪生成图片的格式
  outputType: "png",
  // 是否默认生成截图框
  autoCrop: true,
  // 上传图片按照原始比例渲染
  //   original: true,
  // 是否输出原图比例的截图
  full: false,
  // 默认生成截图框宽度
  autoCropWidth: 160,
  // 默认生成截图框高度
  autoCropHeight: 160,
  // 是否开启截图框宽高固定比例
  fixed: true,
  // 截图框的宽高比例
  fixedNumber: [1, 1],
  // 截图框是否被限制在图片里面
  centerBox: true,
});

// 生成裁剪图片
const handleCropping = (roleRefs: any, type: boolean) => {
  if (type) {
    roleRefs.getCropBlob((data: any) => {
      // 判断裁剪图片大小
      if (data.size / 1024 / 1024 > 0.5) {
        ElMessage.error("裁剪图片大小不能超过 500k");
      } else {
        // 图片预览
        imgUpload.imageUrl = URL.createObjectURL(data);
        imgUpload.dialogCropping = false; 
        // 图片信息
        emit("img-upload", blobToFile(data, imgUpload.imgName));
      }
    });
  } else {
    imgUpload.imageUrl = "";
    imgUpload.dialogCropping = false;
  }
};

// 清除图片
const clearImg = () => {
  imgUpload.imageUrl = "";
  emit("img-upload");
};

onMounted(() => {
  // 图片地址传递
  imgUpload.imageUrl = prop.imgUrl;
});

图片相关方法封装

/** 查询图片大小 */
export const imgSize = (file: any) => {
  return new Promise((resolve, reject) => {
    let reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => {
      // 让页面中的img标签的src指向读取的路径
      let img = new Image();
      img.src = reader.result as string;
      if (img.complete) {
        // 如果存在浏览器缓存中
        if (img.width / img.height !== 1) {
          resolve(false);
        } else {
          resolve(true);
        }
      } else {
        img.onload = () => {
          if (img.width / img.height !== 1) {
            resolve(false);
          } else {
            resolve(true);
          }
        };
      }
    };
  });
};

/**
 * 文件格式转换
 * @description blobToFile
 * @param {Blob} blob
 * @param {String} fileName
 */
export const blobToFile = (blob: any, fileName: string) => {
  return new window.File([blob], fileName, {
    type: blob.type,
  });
};
  • 打赏
  • 1
  • 收藏
  • 评论
  • 分享
  • 举报

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK