乐趣区

关于javascript:vueelementui使用vuecropper实现图片截图后上传七牛

1. 装置引入依赖包

npm i vue-cropper

在组件 vueCropper 引入依赖包

import {VueCropper} from 'vue-cropper'

2. 增加 vueCropper.vue 组件

<template>
    <div>
        <div class="cropper-content">
            <!-- 剪裁框 -->
            <div class="cropper">
                <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"
                            :autoCropWidth="option.autoCropWidth"
                            :autoCropHeight="option.autoCropHeight"
                            :fixedBox="option.fixedBox"
                            @realTime="realTime"
                            :fixed="option.fixed"
                            :fixedNumber="fixedNumber"></vueCropper>
            </div>
            <!-- 预览框 -->
            <div class="show-preview"
                 :style="{'width':'300px','height':'300px','overflow':'hidden','margin':'0 25px','display':'flex','align-items':'center'}">
                <div :style="previews.div" class="preview">
                    <img :src="previews.url" :style="previews.img">
                </div>
            </div>
        </div>
        <div class="footer-btn">
            <!-- 缩放旋转按钮 -->
            <div class="scope-btn">
                <el-button type="primary" icon="el-icon-zoom-in" @click="changeScale(1)"></el-button>
                <el-button type="primary" icon="el-icon-zoom-out" @click="changeScale(-1)"></el-button>
                <el-button type="primary" @click="rotateLeft"> 逆时针旋转 </el-button>
                <el-button type="primary" @click="rotateRight"> 顺时针旋转 </el-button>
            </div>
            <!-- 确认上传按钮 -->
            <div class="upload-btn">
                <el-button type="primary" @click="uploadImg('blob')"> 上传 </el-button>
            </div>
        </div>
    </div>
</template>

<script>
    import {VueCropper} from 'vue-cropper'
    export default {data() {
            return {previews: {}, // 预览数据
                option: {img: '', // 裁剪图片的地址  (默认:空)
                    size: 1, // 裁剪生成图片的品质  (默认:1)
                    full: false, // 是否输入原图比例的截图 选 true 生成的图片会十分大  (默认:false)
                    outputType: 'jpg', // 裁剪生成图片的格局  (默认:jpg)
                    canMove: true, // 上传图片是否能够挪动  (默认:true)
                    original: true, // 上传图片依照原始比例渲染  (默认:false)
                    canMoveBox: false, // 截图框是否拖动  (默认:true)
                    autoCrop: true, // 是否默认生成截图框  (默认:false)
                    autoCropWidth: this.imgW*(256/320), // 默认生成截图框宽度  (默认:80%)
                    autoCropHeight: this.imgH*(256/320), // 默认生成截图框高度  (默认:80%)
                    fixedBox: true, // 固定截图框大小 不容许扭转  (默认:false)
                    fixed: false, // 是否开启截图框宽高固定比例  (默认:true)
                    fixedNumber: [1, 1] // 截图框比例  (默认:[1:1])
                },
                downImg: '#'
            }
        },
        props: ['imgFile', 'fixedNumber','imgW','imgH'],
        methods: {changeScale(num) {
                // 图片缩放
                num = num || 1
                this.$refs.cropper.changeScale(num)
            },
            rotateLeft() {
                // 向左旋转
                this.$refs.cropper.rotateLeft()},
            rotateRight() {
                // 向右旋转
                this.$refs.cropper.rotateRight()},
            Update() {
                // this.file = this.imgFile
                this.option.img = this.imgFile.url
            },
            realTime(data) {
                // 实时预览
                this.previews = data
            },
            uploadImg(type) {
                // 将剪裁好的图片回传给父组件
                event.preventDefault()
                let that = this
                if (type === 'blob') {
                    this.$refs.cropper.getCropBlob(data => {const reader = new FileReader();
                        reader.readAsDataURL(data);
                        reader.onload = (e) => {that.$emit('upload', reader.result)
                        }
                    })
                } else {
                    this.$refs.cropper.getCropData(data => {that.$emit('upload', data)
                    })
                }
            }
        },
        components: {VueCropper}
    }
</script>
<style>
    .cropper-content {
        display: flex;
        display: -webkit-flex;
        justify-content: flex-end;
        -webkit-justify-content: flex-end;
    }

    .cropper-content .cropper {
        width: 350px;
        height: 300px;
    }

    .cropper-content .show-preview {
        flex: 1;
        -webkit-flex: 1;
        display: flex;
        display: -webkit-flex;
        justify-content: center;
        -webkit-justify-content: center;
        overflow: hidden;
        border: 1px solid #cccccc;
        background: #cccccc;
        margin-left: 40px;
    }

    .preview {
        overflow: hidden;
        border: 1px solid #000000;
        background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC");
    }

    .footer-btn {
        margin-top: 30px;
        display: flex;
        display: -webkit-flex;
        justify-content: flex-end;
        -webkit-justify-content: flex-end;
    }

    .footer-btn .scope-btn {
        width: 250px;
        display: flex;
        display: -webkit-flex;
        justify-content: space-between;
        -webkit-justify-content: space-between;
    }

    .footer-btn .upload-btn {
        flex: 1;
        -webkit-flex: 1;
        display: flex;
        display: -webkit-flex;
        justify-content: center;
        -webkit-justify-content: center;
    }

    .footer-btn .btn {
        outline: none;
        display: inline-block;
        line-height: 1;
        white-space: nowrap;
        cursor: pointer;
        -webkit-appearance: none;
        text-align: center;
        -webkit-box-sizing: border-box;
        box-sizing: border-box;
        outline: 0;
        margin: 0;
        -webkit-transition: 0.1s;
        transition: 0.1s;
        font-weight: 500;
        padding: 8px 15px;
        font-size: 12px;
        border-radius: 3px;
        color: #fff;
        background-color: #67c23a;
        border-color: #67c23a;
    }
</style>

3. 上传组件
单张上传要在 el-upload 加上 list-type=”picture” 属性

<el-upload
        class="avatar-uploader"
        style="width: 150px;height: 150px;"
        action="http://upload-z2.qiniup.com/"
        :auto-upload="false" :show-file-list="false"
        list-type="picture"
        :on-change="handleCrop"
        :http-request="upload">
    <img class="up-img" v-if="groupForm.logoUrl" :src="groupForm.logoUrl">
    <i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>

updated 生命周期加上以下代码

updated () {if (this.$refs.vueCropper) {this.$refs.vueCropper.Update()
    }
},

4.el-upload 的 on-change 事件函数 handleCrop 办法

handleCrop(file, files) {
    // 点击弹出剪裁框
    this.cropperModel = true
    this.file = file
    this.upPost.key = file.name
},

5. 自定义 el-upload 上传办法 upload 办法
应用原生 xhr 上传 base64 格局图片到七牛,this.upPost.key 和 than.upPost.token 别离是上传文件名和七牛 token, 七牛 token 是从后端获取的 token

upload(data) {
    let than = this
    // 截取 base64
    data = data.substring(22);
    let timestamp = Date.parse(new Date());
    let key = timestamp + this.upPost.key
    let url = "http://upload-z2.qiniup.com/putb64/" + this.fileSize(data) + '/key/' + this.baseCode64(key);
    let xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function () {if (xhr.readyState == 4) {let json = JSON.parse(xhr.responseText)
            than.groupForm.logoUrl = '七牛资源门路' + json.key
            than.$message.success('上传胜利!')
            than.cropperModel = false
        }
    }
    xhr.open("POST", url, true);
    xhr.setRequestHeader("Content-Type", "application/octet-stream");
    xhr.setRequestHeader("Authorization", "UpToken" + than.upPost.token);
    xhr.send(data);
},

6. 工具办法

// 计算文件大小函数
fileSize(str) {
    let fileSize;
    if (str.indexOf('=') > 0) {let indexOf = str.indexOf('=');
        str = str.substring(0, indexOf); // 把开端的’=‘号去掉
    }
    fileSize = parseInt(str.length - (str.length / 8) * 2);
    return fileSize;
},
// 转换 base64 函数
baseCode64(input){
    var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    var output = "";
    var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
    var i = 0;
    input = this._utf8_encode(input);
    while (i < input.length) {chr1 = input.charCodeAt(i++);
        chr2 = input.charCodeAt(i++);
        chr3 = input.charCodeAt(i++);
        enc1 = chr1 >> 2;
        enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
        enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
        enc4 = chr3 & 63;
        if (isNaN(chr2)) {enc3 = enc4 = 64;} else if (isNaN(chr3)) {enc4 = 64;}
        output = output +
            _keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
            _keyStr.charAt(enc3) + _keyStr.charAt(enc4);
    }
    return output;
},
// 字节转换
_utf8_encode(string) {string = string.replace(/\r\n/g,"\n");
    var utftext = "";
    for (var n = 0; n < string.length; n++) {var c = string.charCodeAt(n);
        if (c < 128) {utftext += String.fromCharCode(c);
        } else if((c > 127) && (c < 2048)) {utftext += String.fromCharCode((c >> 6) | 192);
            utftext += String.fromCharCode((c & 63) | 128);
        } else {utftext += String.fromCharCode((c >> 12) | 224);
            utftext += String.fromCharCode(((c >> 6) & 63) | 128);
            utftext += String.fromCharCode((c & 63) | 128);
        }
    }
    return utftext;
},
退出移动版