vue-cropper 移动端上传图片压缩裁剪

头像裁剪压缩上传流程:

点击头像--判断是否为IOS端--若是--A,否则--B

A:选择图片 --C

B:弹框供用户选择从相册选择或者调用相机拍照--选择图片--C

C:出现cropper裁剪框,裁剪框位于图片上方,用户可以拖动或者放大缩小裁剪框--用户手动调整后--代码执行压缩并上传

1.引入vueCropper组件

地址:https://www.npmjs.com/package/vue-cropper/v/0.4.7

npm install vue-cropper --save-dev

<script>
import { VueCropper }  from 'vue-cropper' 
export default {
  name: 'UserProfile',
  components: {
    VueCropper
  },
}
</script>

使用vue-cropper组件

<!-- 剪裁图片组件 -->
    <van-popup
      class="bg-tran"
      v-model="showCropper"
      closeable
      position="top"
      :100%' }"
    >
      <div class="flex-column-center height100">
        <vueCropper
          ref="cropper"
          :img="option.img"
          :outputSize="option.outputSize"
          :outputType="option.outputType"
          :info="option.info"
          :full="option.full"
          :autoCropWidth="option.autoCropWidth"
          :autoCropHeight="option.autoCropHeight"
          :canMove="option.canMove"
          :canMoveBox="option.canMoveBox"
          :original="option.original"
          :autoCrop="option.autoCrop"
          :fixed="option.fixed"
          :fixedNumber="option.fixedNumber"
          :centerBox="option.centerBox"
          :infoTrue="option.infoTrue"
          :fixedBox="option.fixedBox"
          :high="option.high"
          :mode="option.mode"
        ></vueCropper>
        <van-col span="24" class="font14 col-white">
          <van-col span="8" class="p-2"><span @click="cancelCropper">取消</span></van-col>
          <van-col span="8" class="p-2 text-center"><span @click="rotateImage" class="font18"><van-icon name="replay" /></span></van-col>
          <van-col span="8" class="p-2 text-right"><span @click="getCropBlob">确定</span></van-col>
        </van-col>
      </div>
    </van-popup>

vue-cropper配置:

<script>
export default {
    data() {
        return {
            option: {
                img: '',
                outputSize: 0.8,
                info: false, // 裁剪框的大小信息
                outputType: 'jpeg', // 裁剪生成图片的格式
                canScale: false, // 图片是否允许滚轮缩放
                autoCrop: true, // 是否默认生成截图框
                autoCropWidth: window.innerWidth - 100 + 'px', // 默认生成截图框宽度
                autoCropHeight: window.innerWidth - 100 + 'px', // 默认生成截图框高度
                high: true, // 是否按照设备的dpr 输出等比例图片
                fixedBox: true, // 固定截图框大小 不允许改变
                fixed: true, // 是否开启截图框宽高固定比例
                fixedNumber: [1, 1], // 截图框的宽高比例
                full: true, // 是否输出原图比例的截图
                canMoveBox: false, // 截图框能否拖动
                original: false, // 上传图片按照原始比例渲染
                centerBox: false, // 截图框是否被限制在图片里面
                infoTrue: false, // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
                mode: '100% auto' // 图片默认渲染方式
             }
        }
    }
}
</script>

2.点击头像选择文件,裁剪压缩并上传

这里需要判断是否为苹果手机;

苹果手机点击上传头像会默认弹框显示拍照还是从文件中选择,安卓手机点击上传头像则默认从文件中选择;

这里判断如果是安卓手机则显示我们自定义的弹框供用户选择是拍照还是从文件中选择

<!--点击上传头像 -->
<van-cell-group>
  <van-cell title="头像" is-link @click="checkIsIos" /><!-- 检测是否为苹果手机 -->
    <img v-if="fromWX" :src="wxImgUrl">   <!-- 默认显示微信头像(带http的绝对链接) -->
    <img v-else :src="headImgUrl">
    <input type="file" name="fileIOS" @change="changeImgIOS" accept="image/*" ref="inputFileRefIOS"  />
  </van-cell>
</van-cell-group>
<!--安卓上传头像弹框,需要注意的是调用相机时,type="file" name="file" capture="camera" 必须写正确,否则调用相机不成功 -->
<van-popup v-model="uploadModal" position="bottom">
  <div>
    <div @click="openFile">从相册中选择</div>
    <input type="file" name="file" @change="changeImg" accept="image/*" ref="inputFileRef"  />
    <div @click="openFile">拍照</div>
    <input type="file" name="file" @change="changeImg" accept="image/*" ref="inputFileRef" capture="camera"  />
    <div @click="uploadModal = false">取消</div>
  </div>
</van-popup>
methods:{
    checkIsIoS(){//点击头像调用的方法
        if(!this.isIOS){//非IOS则显示自定义弹框
            this.uploadModal = true
            this.isCamera = true
        }else{
            this.$refs.inputFIleRefIOS.click()//IOS选择文件,触发changeImgIOS方法
        }
    },

    //IOS选择文件
    changeImgIOS(){
        let file = this.$refs.inputFIleRefIOS.file[0]
        this.showCropper = true//显示裁剪框
        this.imageFileName = file.name //保存上传的文件名,方便后续用到
        this.imageToBase64(file)//图片压缩并转化为base64,否则cropper组件显示不出要裁剪的图片
    },

    //安卓选择文件,从相册中选择
    openFile(){
        this.$refs.inputFIleRef.click()
    },
    //安卓选择文件,调用相机
    openFile(){
        this.isCamera = true
        this.$refs.inputFIleRef.click()
    },
    changeImg(){
        this.uploadModal = false
        let file = null
        if(this.isCamera){
            file = this.$refs.inputFIleRefCamera.files[0]//相机拍照的
        }else{
            file = this.$refs.inputFIleRef.files[0]//相册选择的
        }
        this.showCropper = true
        this.imageFileName = file.name
        this.imageToBase64(file)
    },

    imageToBase64(file){
        this.option.img = ''//清空上一次裁剪的图片
        let reader = new FileReader()
        reader.readAsDataURL(file)
        reader.onload = () =>{
            if(file.size>(1*1024*1024)){//大于 1M 的图片压缩
                let scaleSize = 0.9//定义压缩的比例(程度)
                let image = new Image()
                image.src = reader.result;
                image.onload = () =>{

                    if(file.size>(1*1024*1024) && file.size<(2*1024*1024+1)){//1-2M
                        scaleSize = 0.9
                    }
                    if(file.size>(2*1024*1024) && file.size<(3*1024*1024+1)){//2-3M
                        scaleSize = 0.9
                    }
                    if(file.size>(3*1024*1024) && file.size<(4*1024*1024+1)){//3-4M
                        scaleSize = 0.9
                    }
                    if(file.size>(4*1024*1024)){//4M+
                        scaleSize = 0.9
                    }

                    let img64 = this.compress(image,scaleSize)
                    this.option.img = img64
                }
            }else{
                this.option.img = reader.result
            }
        }
        reader.onerror = (rrror) =>{
            console.log(error)
        }
    },

    compress(img,ratio){//图片压缩并转化为base64返回
        let canvas,ctx,img64;
        let width = img.width*0.6//改变上传的图片像素,缩放至原来的0.6
        let height = img.height*0.6
        canvas = document.createElement('canvas')
        canvas.width = width
        canvas.height = height
        ctx = canvas.getContext('2d')
        ctx.drawImage(img,0,0,width,height)
        img64 = canvas.toDataURL('image/jpeg',ratio)//参考 https://www.runoob.com/jsref/met-canvas-drawimage.html
        return img64
    },

    //绑定在cropper组件上的方法,确认裁剪并上传图片到后台
    getCropBlob(){
        let toast = Toast.loading({
            duration:0,
            forbidClick:true,
            message:'加载中'
        })
        let formData = new FormData()
        this.$refs.cropper.getCropBlob(async (data) =>{
            formData.append('custId',this.custId)
            formData.append('multiReq',data,this.imageFileName)
            let rs = await axios({
                method:'post',
                url:cfg.baseURL.XXX + '',
                data:formData,
                headers:{
                    openId:sessionStorage.getItem('openId')
                }
            })
            if(rs.data.code=='200'){
                Toast.clear()
                Toase.success('上传成功')
            }else{
                Toast.clear()
                Toase.fail('修改异常')
            }
            this.showCropper = false //隐藏裁剪遮罩
            this.isCamera = false
        })
    },

    //旋转图片
    rotateImage(){
        this.$refs.cropper.rotateImage()
    },

    //取消截图上传头像
    cancelCropper(){
        this.showCropper = false;
        this.isCamera = false
    }
},
mounted(){
    let u = navigator.userAgent
    this.isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)
},