Ant Design 结合 vue-cropper 实现上传图片裁剪功能

1. 引入 vue-cropper 第三方插件

npm install vue-cropper

yarn add vue-cropper

2. 在 main.js 全局进入

import VueCropper from 'vue-cropper'
Vue.use(VueCropper)

3. 封装成组件

3.1 上传组件

<template>
  <div class="ant-upload-preview">
    <div class="avatatImg">
      <a-upload
        name="avatar"
        listType="picture-card"
        :showUploadList="false"
        :beforeUpload="beforeUpload"
        :customRequest="function () {}"
        @change="handleChange"
        accept="image/png,image/jpeg"
      >
        <img class="upload_img" v-if="imageUrl" :src="imageUrl" alt="avatar" />
        <div v-else>
          <a-icon :type="loading ? 'loading' : 'plus'" />
          <div class="ant-upload-text">上传图片</div>
        </div>
      </a-upload>
      <div class="text">
        <div>{{ avatarTextTop }}</div>
        <div>{{ avatarTextBottom }}</div>
      </div>
    </div>
    <!-- 引入裁剪组件 -->
    <CropperModal
      ref="CropperModal"
      :imgType="imgType"
      :fileName="fileName"
      :cropperMode="cropperMode"
      @cropper-no="handleCropperClose"
      @cropper-ok="handleCropperSuccess"
    ></CropperModal>
  </div>
</template>

<script>
import { message } from "ant-design-vue";
import CropperModal from "./CropperModal";
export default {
  components: { CropperModal },
  props: {
    //图片裁切配置
    options: {
      type: Object,
      default: function () {
        return {
          autoCrop: true, //是否默认生成截图框
          autoCropWidth: 1029, //默认生成截图框宽度
          autoCropHeight: 480, //默认生成截图框高度
          fixedBox: true, //是否固定截图框大小 不允许改变
          previewsCircle: false, //预览图是否是原圆形
          title: "修改图片",
        };
      },
    },
    avatarTextTop: {
      type: String,
      default: "推荐使用160*160px,JPG.PNG.JPEG格式",
    },
    avatarTextBottom: {
      type: String,
      default: "图片小于1M",
    },
    // 上传图片的大小,单位M
    imgSize: {
      type: Number,
      default: 2,
    },
    //图片存储在oss上的上级目录名
    imgType: {
      type: String,
      default: "",
    },
    // 图片地址
    imageUrl: {
      type: String,
      default: "",
    },
  },
  data() {
    return {
      fileName: "", // 文件名
      loading: false,
      isStopRun: false,
      cropperMode: 'contain'  // 图片渲染方式
    };
  },
  methods: {
    //从本地选择文件
    handleChange(info) {
      if (!info.file.status) {
        this.loading = false;
      } else {
        this.loading = true;
      }
      this.fileName = info.file.name;
      if (this.isStopRun) {
        return;
      }
      const { options } = this;
      this.getBase64(info.file.originFileObj, (imageUrl) => {
        const target = Object.assign({}, options, {
          img: imageUrl,
        });
        this.$refs.CropperModal.edit(target);
      });
    },
    // 上传之前 格式与大小校验
    beforeUpload(file) {
      this.$emit("avatarLoadingFn", true);
      const isJpgOrPng =
        file.type === "image/jpeg" || file.type === "image/png";
      if (!isJpgOrPng) {
        message.error("不能上传其他类型的图片");
      }
      
      // 获取上传图片尺寸
      var reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = () => {
        var img = new Image()
        img.src = reader.result
        img.onload = () => {
          // 1、先根据图片裁剪的尺寸
          if (this.options.autoCropWidth > this.options.autoCropHeight) {
            // 2、再根据上传图片尺寸
            this.cropperMode = this.options.autoCropWidth + 'px auto'
          } else {
            if (img.width >= img.height) {
              this.cropperMode = 'auto ' + this.options.autoCropHeight + 'px'
            } else {
              this.cropperMode = this.options.autoCropWidth + 'px auto'
            }
          }
        }
      }

      return isJpgOrPng;
    },
    //获取服务器返回的地址
    handleCropperSuccess(data) {
      //将返回的数据回显
      this.loading = false;
      this.$emit("avatarfn", data);
    },
    // 取消上传
    handleCropperClose() {
      this.loading = false;
    },
    getBase64(img, callback) {
      if (img) {
        const reader = new FileReader();
        reader.addEventListener("load", () => callback(reader.result));
        reader.readAsDataURL(img);
      }
    },
  },
};
</script>
 
<style lang="less" scoped>
.avatar-upload-wrapper {
  height: 180px;
  width: 100%;
}

.ant-upload-preview {
  background-color: #fff;
  .avatar-uploader > .ant-upload {
    width: 128px;
    height: 128px;
  }
  .ant-upload-select-picture-card i {
    font-size: 32px;
    color: #999;
  }
  .upload_img {
    width: 100%;
  }
  .ant-upload-select-picture-card .ant-upload-text {
    margin-top: 8px;
    color: #666;
  }
}
.ant-upload-picture-card-wrapper {
  width: auto;
}
.avatar-uploader > .ant-upload {
  width: 128px;
  height: 128px;
}
.avatatImg {
  // width: 100%;
  display: flex;
  align-items: center;
  .text {
    margin-left: 20px;
    div {
      line-height: 30px !important;
      font-weight: 600;
    }
  }
}
</style>

3.2 裁剪组件

<template>
  <a-modal
    :visible="visible"
    :title="options.title"
    :maskClosable="false"
    :confirmLoading="confirmLoading"
    :width="1200"
    @cancel="cancelHandel"
  >
    <a-row>
      <a-col :xs="24" :md="24" :style="{ height: '600px' }">
        <vue-cropper
          ref="cropper"
          :img="options.img"
          :info="true"
          :autoCrop="options.autoCrop"
          :autoCropWidth="options.autoCropWidth"
          :autoCropHeight="options.autoCropHeight"
          :fixedBox="options.fixedBox"
          :mode="cropperMode"
          @realTime="realTime"
        >
        </vue-cropper>
      </a-col>
      <!-- <a-col :xs="24" :md="12" :style="{ height: '250px' }">
        <div
          :class="
            options.previewsCircle
              ? 'avatar-upload-preview'
              : 'avatar-upload-preview_range'
          "
        >
          <img :src="previews.url" :style="previews.img" />
        </div>
      </a-col> -->
    </a-row>
    <template slot="footer">
      <a-button key="back" @click="cancelHandel">取消</a-button>
      <a-button
        key="submit"
        type="primary"
        :loading="confirmLoading"
        @click="okHandel"
        >保存</a-button
      >
    </template>
  </a-modal>
</template>

<script>
import axios from "axios";
import { domainName } from "@/config/index";  // 后台服务器的域名

export default {
  props: {
    //图片存储在oss上的上级目录名
    imgType: {
      type: String,
      default: "",
    },
    fileName: {
      type: String, // 文件名
      default: "",
    },
    cropperMode: {  // 图片渲染方式
      type: String,
      default: 'contain'
    }
  },
  data() {
    return {
      visible: false,
      img: null,
      confirmLoading: false,
      options: {
        img: "", //裁剪图片的地址
        autoCrop: true, //是否默认生成截图框
        autoCropWidth: 180, //默认生成截图框宽度
        autoCropHeight: 180, //默认生成截图框高度
        fixedBox: true, //是否固定截图框大小 不允许改变
        previewsCircle: false, //预览图是否是原圆形
        title: "修改图片",
      },
      previews: {},
      url: {
        upload: "/sys/common/saveToImgByStr",
      },
    };
  },
  methods: {
    edit(record) {
      const { options } = this;
      this.visible = true;
      this.options = Object.assign({}, options, record);
    },
    // 取消截图
    cancelHandel() {
      this.confirmLoading = false;
      this.visible = false;
      this.$emit("cropper-no");
    },
    // 确认截图
    okHandel() {
      const that = this;
      that.confirmLoading = true;
      // 获取截图的base64 数据
      this.$refs.cropper.getCropData((data) => {
        let file = this.dataURLtoFile(data, this.fileName);
        let formData = new window.FormData();
        formData.append("uploadFile", file);
        axios
          .post(`${domainName}/admin/files/upload/images`, formData)
          .then((res) => {
            if (res.data.code == 200) {
              this.visible = false;
              this.confirmLoading = false;
              this.$emit("cropper-ok", res.data.data);
            }
          });
      });
    },
    //移动框的事件
    realTime(data) {
      this.previews = data;
    },
    // base 64 转成二进制文件流
    dataURLtoFile(dataurl, filename) {
      var 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);
      }
      return new File([u8arr], filename, { type: mime });
    },
  },
};
</script>
 
<style lang="less" scoped>
.avatar-upload-preview_range,
.avatar-upload-preview {
  position: absolute;
  top: 50%;
  transform: translate(50%, -50%);
  width: 100%;
  height: 100%;
  border-radius: 50%;
  box-shadow: 0 0 4px #ccc;
  overflow: hidden;
  img {
    background-color: red;
    height: 100%;
  }
}
.avatar-upload-preview_range {
  border-radius: 0;
}
</style>

4. 在页面中使用

4.1 HTML

<CropperUpload
          :imageUrl="form.coverUrl"
          :options="coverOptions"
          @avatarfn="coverUrlFn"
          avatarTextTop="推荐使用174px*200px  JPG.PNG.JPEG格式 图片小于1M"
          avatarTextBottom=""
        />
// imageurl --> 图片地址
// options  --> 截图配置项
// coverUrlFn -- > 接收需要传递回后台的参数

4.2 options

export default {
	data(){
		return {
			coverOptions: {
		        autoCrop: true, //是否默认生成截图框
		        autoCropWidth: 174, //默认生成截图框宽度
		        autoCropHeight: 200, //默认生成截图框高度
		        fixedBox: true, //是否固定截图框大小 不允许改变
		        previewsCircle: false, //预览图是否是原圆形
		        title: "修改图片",
		      },
		}
	}
}

5. 效果图

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要使用vant-uploader和vue-cropper实现裁剪图片并上,你可以按照以下步骤进行操作: 1. 首先,安装vantvue-cropper插件。你可以使用npm或yarn来安装它们。 2. 在你的Vue组件中,引入vant-uploader和vue-cropper组件。 3. 在模板中,使用vant-uploader组件来实现图片功能。设置上的action属性为你的上接口地址,并设置on-success事件来处理上成功后的逻辑。 4. 在on-success事件中,获取到上成功后的图片地址,并将其递给vue-cropper组件。 5. 在vue-cropper组件中,设置裁剪框的样式和裁剪比例等属性。使用v-model指令来绑定裁剪后的图片数据。 6. 在提交按钮的点击事件中,将裁剪后的图片数据上到服务器。 下面是一个简单的示例代码: ```vue <template> <div> <van-uploader action="/upload" :on-success="handleUploadSuccess" ></van-uploader> <vue-cropper v-if="showCropper" :src="cropperSrc" :output-size="{ width: 200, height: 200 }" :output-type="'jpeg'" :fixed-box="true" :fixed-number="\[1, 1\]" v-model="croppedImage" ></vue-cropper> <button @click="handleSubmit">提交</button> </div> </template> <script> import { VanUploader } from 'vant'; import VueCropper from 'vue-cropper'; export default { components: { VanUploader, VueCropper, }, data() { return { showCropper: false, cropperSrc: '', croppedImage: '', }; }, methods: { handleUploadSuccess(response) { // 获取上成功后的图片地址 const imageUrl = response.data.imageUrl; // 显示裁剪组件 this.showCropper = true; // 设置裁剪组件的图片地址 this.cropperSrc = imageUrl; }, handleSubmit() { // 提交裁剪后的图片数据到服务器 // this.croppedImage 包含裁剪后的图片数据 }, }, }; </script> ``` 请注意,以上代码只是一个简单的示例,你需要根据你的实际需求进行适当的修改和调整。同时,你还需要在后端实现相应的上裁剪功能

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值