欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

利用vue-cropper做的关于图片裁剪、压缩、上传、预览等做的一个公共组件

程序员文章站 2022-04-08 16:07:07
...

利用vue-cropper做的关于图片裁剪、压缩、上传、预览等做的一个公共组件

<template>
  <div>
    <div class="upload-box">
      <div class="image-box"
           v-if="imageUrl"
           :style="{'background-image': 'url('+ imageUrl +')', 'height': imageHeight}"></div>
      <div class="upload">
        <h6 class="upload-des">支持 jpg、png 格式大小 5M 以内的图片</h6>
        <input type="file" @change="getFile" ref="file" id="file">
        <label for="file">点击上传</label>
      </div>
    </div>
    <!-- vueCropper 剪裁图片实现-->
    <div class="vue-cropper-box" v-if="isShowCropper">
      <div class="vue-cropper-content">
        <vueCropper ref="cropper"
                    :img="option.img"
                    :outputSize="option.outputSize"
                    :outputType="option.outputType"
                    :info="option.info"
                    :canScale="option.canScale"
                    :autoCrop="option.autoCrop"
                    :autoCropWidth="option.autoCropWidth"
                    :autoCropHeight="option.autoCropHeight"
                    :fixed="option.fixed"
                    :fixedNumber="option.fixedNumber">
        </vueCropper>
      </div>
    </div>
    <el-button v-if="isShowCropper" type="danger" @click="onCubeImg">确定裁剪图片</el-button>
  </div>
</template>
<script>
  import VueCropper from "vue-cropper"
  export default {
    name: 'Avatar',

    data () {
      return {
        //裁剪组件的基础配置option
        option: {
          img: '', //裁剪图片的地址
          info: true, //裁剪框的大小信息
          outputSize: 1, // 裁剪生成图片的质量
          outputType: 'png', //裁剪生成图片的格式
          canScale: false, // 图片是否允许滚轮缩放
          autoCrop: true, // 是否默认生成截图框
          autoCropWidth: 195, // 默认生成截图框宽度
          autoCropHeight: 10, // 默认生成截图框高度
          fixed: false, //是否开启截图框宽高固定比例
          fixedNumber: [1, 1] //截图框的宽高比例
        },
        isShowCropper: false //是否显示截图框
      }
    },

    props: {
      // 特殊配置
      cropperOption: {
        type: Object,
        default: () => {
          return null
        }
      },
      // 默认图片
      imageUrl: {
        type: String,
        default: ''
      },
      // 图片展示高度
      imageHeight: {
        type: String,
        default: '100px'
      }
    },

    components: {
      VueCropper
    },

    methods: {
      getFile (e) {
        let _this = this
        var files = e.target.files[0]
        if (!e || !window.FileReader) return // 看支持不支持FileReader
        let reader = new FileReader()
        reader.readAsDataURL(files)
        reader.onloadend = function () {
          _this.isShowCropper = true
          _this.option.img = this.result
          _this.$refs.file.value = ''
        }
      },

      // 确定裁剪图片
      onCubeImg () {
        this.$refs.cropper.getCropData((data) => {
          this.isShowCropper = false
          //console.log("压缩前的图片大小:" + data.length)
          let img = new Image(),
            _this = this
          img.src = data
          img.onload = function() {
            //let _data = _this.compress(img)
            let blob = _this.dataURItoBlob(data)

            let formData = new FormData()
            formData.append("icon", blob)
            //console.log("将blob对象转成formData对象:" + formData.get("icon"))
            _this.$emit('cropped', data, formData)
          }
        })
      },

      // 压缩图片 
      compress(img) {
        let canvas = document.createElement("canvas"),
          ctx = canvas.getContext("2d"),
          initSize = img.src.length,
          width = img.width,
          height = img.height;
        canvas.width = width
        canvas.height = height
        // 铺底色 
        ctx.fillStyle = "#fff"
        ctx.fillRect(0, 0, canvas.width, canvas.height)
        ctx.drawImage(img, 0, 0, width, height)
        //进行压缩 
        let ndata = canvas.toDataURL("image/jpeg", 0.8)
        //console.log("压缩后的图片大小:" + ndata.length)
        return ndata
      }, 
      // base64转成bolb对象 
      dataURItoBlob(base64Data) { 
        let byteString
        if (base64Data.split(",")[0].indexOf("base64") >= 0) 
          byteString = atob(base64Data.split(",")[1])
        else 
          byteString = unescape(base64Data.split(",")[1])
        let mimeString = base64Data .split(",")[0] .split(":")[1] .split(";")[0]; 
        let ia = new Uint8Array(byteString.length); 
        for (let i = 0; i < byteString.length; i++) { 
            ia[i] = byteString.charCodeAt(i); 
        } 
        return new Blob([ia], { type: mimeString })
      },

      // 初始化配置文件
      initOptions () {
        this.isShowCropper = false
        if (this.cropperOption) {
          for (let key in this.option) {
            this.option[key] = this.cropperOption[key] || this.option[key]
          }
        }
      }
    },

    created () {
      this.initOptions()
    }
  }
</script>
<style scoped lang="stylus" rel="stylesheet">
  .upload-box {
    & > div {
      display inline-block
      vertical-align middle
    }
    & .upload-des {
      margin 0
      font-weight 400
      color #909399
    }
  }

  .image-box {
    width 100px
    height 100px
    margin-right 20px
    background-size 100px auto
    background-position left center
    background-repeat no-repeat
  }

  .upload {
    & label {
      width: 80px;
      height: 24px;
      font-size: 12px;
      line-height: 24px;
      display: inline-block;
      border-radius: 4px;
      text-align: center;
      border: 1px solid #ddd;
      cursor pointer
    }
  }

  input[type='file'] {
    display: none;
  }

  .vue-cropper-box {
    width: 60%;
    height: 200px;
    margin: 15px 0px;
    .vue-cropper-content {
      height 200px
    }
  }
</style>

调用

<el-form-item label="头像" prop="icon" :rules="rules.required">
  <Avatar v-on:cropped="doCrop"
  :cropperOption="cropperOption"
  :imageUrl="guestInfo.icon"></Avatar>
</el-form-item>

注意下面这个二进制文件接收和上传的问题

resetForm(){
  this.$nextTick(() => {
    this.$refs.guest.clearValidate()
    this.guestUpInfo = new FormData()
  })
},
//完成裁剪
doCrop(icon, file){
  this.guestInfo.icon = icon
  this.guestUpInfo = file
}
//新增、编辑提交
submitGuest(){
    validCallback(this, 'guest', () => {
      this.guestUpInfo.append('name',this.guestInfo.name)
      this.guestUpInfo.append('position',this.guestInfo.position)
      this.guestUpInfo.append('description',this.guestInfo.description)
      if(this.guestInfo.seq){
        this.guestUpInfo.append('seq',this.guestInfo.seq)
      }
      if(this.editGuest){
        this.guestUpInfo.append('id',this.guestInfo.id)
      }

      publicSubmitApi('saveSpeaker', this.guestUpInfo, true).then(res => {
        if (res.status === 200) {
          this.guestShow = false
          this.fetchData()
          this.$message({
            message: this.editGuest ? '编辑成功' : '新增成功',
            type: 'success'
          })
        }
      })
    })
},