利用vue-cropper剪裁圖片並上傳

劉粉絲發表於2020-02-27

因為需求所以利用vue的外掛vue-cropper進行圖片剪裁。

安裝

npm install vue-cropper
複製程式碼

使用

import VueCropper from vue-cropper
複製程式碼

效果圖

利用vue-cropper剪裁圖片並上傳

此次主要是利用Element-ui+vue-cropper完成此需求,話不多說上程式碼。

<div>
    <el-upload
          :action="''"
          list-type="picture-card"
          :file-list="MastergraphPicList"
          :limit="10"
          :on-change='changeUpload'
          :auto-upload="false"
          :show-file-list="isFlag"
          :http-request="ServiceUpLoad"
          :on-success="(response,file,fileList) => handlePostSuccess(response,file,fileList)"
          :on-remove="(response,file,fileList) => handleDelete(response,file,fileList)"
          >
          <i class="el-icon-plus"></i>
          <div slot="tips" class="el-upload__tip">支援jpg、jpge、png格式,大小不超過2m,限10張</div>
        </el-upload>
</div>
複製程式碼

上傳圖片時會觸發changeUpload此鉤子函式,由於剪裁圖片是以彈框形式喚起,因此我們的行為可以在此鉤子函式中寫。

      changeUpload(file, fileList) {
        <!--限制檔案格式、大小-->
        const isLt2M = file.size / 1024 / 1024 < 2
        if (!isLt2M) {
          this.$message.error('上傳檔案大小不能超過 2MB!')
          return false
        }
        <!--寫入回顯列表-->
        this.MastergraphPicList.push(file);
        <!--觸發子級方法,喚起剪裁框-->
        this.$children[0].isShow();
      },
複製程式碼

剪裁框模板 cropper.vue

<template>
    <div>
        <el-dialog title="圖片剪裁" :visible.sync="dialogVisible" append-to-body>
        <div class="cropper-content">
            <div class="cropper" style="text-align:center">
            <vueCropper
                ref="cropper"
                :img="option.img"
                :outputSize="option.size"
                :outputType="option.outputType"
                :info="true"
                :full="option.full"
                :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"
            ></vueCropper>
            </div>
        </div>
        <div slot="footer" class="dialog-footer">
            <el-button @click="cancel">取 消</el-button>
            <el-button type="primary" @click="finish" :loading="loading">確認</el-button>
        </div>
        </el-dialog>
    </div>
</template>

<script>
import axios from 'axios'
import { VueCropper } from 'vue-cropper'
import md5 from 'js-md5';
import { postossImage } from '../../../api/index'
    export default {
        name:'cropper',
        data() {
            return {
                // 裁剪元件的基礎配置option
                option: {
                    img: '', // 裁剪圖片的地址
                    info: true, // 裁剪框的大小資訊
                    outputSize: 0.8, // 裁剪生成圖片的質量
                    outputType: 'jpeg', // 裁剪生成圖片的格式
                    canScale: true, // 圖片是否允許滾輪縮放
                    autoCrop: true, // 是否預設生成截圖框
                    autoCropWidth: 600, // 預設生成截圖框寬度
                    autoCropHeight: 300, // 預設生成截圖框高度
                    fixedBox: false, // 固定截圖框大小 不允許改變
                    fixed: false, // 是否開啟截圖框寬高固定比例
                    fixedNumber: [7, 5], // 截圖框的寬高比例
                    full: true, // 是否輸出原圖比例的截圖
                    canMoveBox: true, // 截圖框能否拖動
                    original: true, // 上傳圖片按照原始比例渲染
                    high: true,
                    centerBox: true, // 截圖框是否被限制在圖片裡面
                    infoTrue: true // true 為展示真實輸出圖片寬高 false 展示看到的截圖框寬高
                },
                picsList: [],  //頁面顯示的陣列
                // 防止重複提交
                loading: false,
                dialogVisible:false,        // 控制彈框的顯示
                fileinfo:[],      
                RequestSource:'',           // oss直傳源地址
                baseurl:'',                 // 裁剪後的base64地址
                isVictory:false,        // 識別使用者是點否關閉
            }
        },
        components:{VueCropper},
        created() {
            this.$on('is-Show',this.isShow);
        },
        methods:{
            isShow(file){
                this.fileinfo = file;
                // 傳遞需要剪裁的地址 以彈框形式開啟
                this.$nextTick( () => {
                    this.option.img = file.url;
                    this.dialogVisible = true;
                    this.handlePostSuccess(file.url);
                })
            },
            // 關閉圖片剪裁
            cancel() {
                this.dialogVisible = false;
                // 點選取消時 去除已經回顯的圖片
                this.$parent.dislodge(this.fileinfo);
            },
            // 確認剪裁後觸發
            finish() {
                this.$refs.cropper.getCropData((data) => {
                    this.baseurl = data;
                    //將剪裁後base64的圖片轉化為file格式
                    let file = this.convertBase64UrlToBlob(data)
                    file.name = this.fileinfo.name
                    <!--let file = URL.createObjectURL(data).match(/[^blob:]+/g).join('');-->
                    this.Directpass(this.RequestSource,file).then( (res) => {
                        if (res.status === 200) {
                            this.$parent.handleSuccess({
                                uid:this.fileinfo.uid,
                                imgUrl:res.config.url.split('?')[0]
                            });
                            this.dialogVisible = false;
                            this.isVictory = true;
                            this.loading = true;
                            this.$message('上傳成功!');
                            // 防止使用者不上傳圖片 點選關閉時 不能及時刪除回顯的問題
                            setTimeout(() => {
                                this.isVictory = false;
                            },1000)
                        }
                    })
                    
                })
            },
            // 上傳服務主圖
            handlePostSuccess(file) {
                postossImage({
                    fileMd5:md5(file)
                }).then( (res) => {
                    this.RequestSource = res.data;
                })
            },
            Directpass(url,file){
                return new Promise( (resolve,reject) => {
                axios.put(url,file,{headers:{"x-oss-meta-author":"deshe","Content-Type":"application/octet-stream"}}).then( (res) => {
                    resolve(res)
                })
            })
        },
        // 將base64的圖片轉換為file檔案
        convertBase64UrlToBlob(urlData) {
            let bytes = window.atob(urlData.split(',')[1]);//去掉url的頭,並轉換為byte
            //處理異常,將ascii碼小於0的轉換為大於0
            let ab = new ArrayBuffer(bytes.length);
            let ia = new Uint8Array(ab);
            for (var i = 0; i < bytes.length; i++) {
                ia[i] = bytes.charCodeAt(i);
            }
            return new Blob([ab], { type: 'image/jpeg' });
        }
    },
    watch:{
        dialogVisible() {
            if (!this.dialogVisible && !this.isVictory) {
                this.loading = false
                // 點選關閉時 去除已經回顯的圖片
                this.$parent.dislodge(this.fileinfo);
            } else {
                this.loading = false;
            }
        }
    }
}
</script>

<style scoped lang="less">
.cropper-content {
    .cropper {
        width: auto;
        height: 300px;
    }
}
</style>
複製程式碼

總結:

在cropper.vue中主要起剪裁作用的是finish()方法以及convertBase64UrlToBlob()。 需要注意的是:剪下後的圖片返回的是一串沒有意義的字串,傳遞到伺服器無法識別,在這裡我用了 this.$refs.cropper.getCropData() 把剪下的圖片以base64的形式返回,接著把base64資料做一層處理,轉化為Blob物件,裡面儲存著剪裁後圖片的資料序列,接著把處理好的地址往伺服器丟就搞定了。

還有就是: fixed設為false將不限剪裁的寬高

以上僅個人看法。

相關文章