美文网首页Vue
vue中使用cropperjs实现上传头像并裁剪

vue中使用cropperjs实现上传头像并裁剪

作者: 陶菇凉 | 来源:发表于2021-03-26 14:40 被阅读0次

效果图:


image.png

1.首先安装 npm install cropperjs

2.封装组件

<template>
    <div class="Upload">
        <el-dialog v-el-drag-dialog title="更换头像" :visible.sync="dialogVisible" :append-to-body="false">
            <el-row :gutter="10">
                <el-col :span="14">
                    <div class="container">
                        <div>
                            <img id="image" :src="url" alt="Picture" />
                        </div>
                        <div style="text-align: center;">
                            <el-button @click="handleAdd" style="margin:10px 0 auto;">上传图片</el-button>
                            <form enctype="multipart/form-data" class="handle_add" name="fileinfo" @click="handleAdd">
                                <input type="file" id="change" ref="inputFile" :accept="accept" @change="change">
                                <label for="change"></label>
                            </form>
                        </div>
                    </div>
                </el-col>
                <el-col :span="10">
                    <div class="yulan">
                        <div>
                            预览
                        </div>
                        <div style="text-align: center;" class="small">
                        </div>
                    </div>
                </el-col>
            </el-row>
            <div slot="footer" class="dialog-footer">
                <el-button @click="dialogClose">取 消</el-button>
                <el-button type="primary" @click="crop">确 定</el-button>
            </div>
        </el-dialog>
    </div>
</template>

<script>
import Cropper from 'cropperjs'
export default {
    props: {
        accept: {
            type: String,
            default: 'image/jpeg,image/jpg,image/png,,image/gif'
        },
        fileSize: {
            type: Number,
            default: 1
        }
    },
    data() {
        return {
            headerImage: '',
            picValue: '',
            cropper: '',
            croppable: false,
            url: require("../../../public/img/headmr.jpg"),
            dialogVisible: false,
            fileData: "",
        }
    },
    methods: {
        dialogChange(data) {
            this.dialogVisible = true;
            //      // 初始化这个裁剪框
            const self = this
            this.$nextTick(function() {
                //不能多事初始化裁剪框
                if(this.cropper) {
                    return false
                }
                const image = document.getElementById('image')
                this.cropper = new Cropper(image, {
                    aspectRatio: 1,
                    viewMode: 1,
                    background: false,
                    zoomable: false,
                    preview: ".small",
                    ready: function() {
                        self.croppable = true
                    },
                    crop: function() {
                        //              self.crop()
                    }
                })
            })
        },
        dialogClose() {
            this.dialogVisible = false
        },
        limitUpload(file) {
            if(file.size > this.fileSize * 1024 * 1024) {
                this.$message({
                    type: 'error',
                    message: `图片大小不允许超过${this.fileSize}M`
                })
                return false
            }
        },
        getObjectURL(file) {
            // this.limitUpload(file)
            let url = null
            if(window.createObjectURL !== undefined) { // basic
                url = window.createObjectURL(file)
            } else if(window.URL !== undefined) { // mozilla(firefox)
                url = window.URL.createObjectURL(file)
            } else if(window.webkitURL !== undefined) { // webkit or chrome
                url = window.webkitURL.createObjectURL(file)
            }
            return url
        },
        change(e) {
            const files = e.target.files || e.dataTransfer.files
            if(!files.length) return
            this.picValue = files[0]
            this.url = this.getObjectURL(this.picValue)
            if(this.cropper) {
                this.cropper.replace(this.url)
            }
            event.target.value = ''
        },
        crop() {
            if(!this.croppable) {
                return
            }
            const croppedCanvas = this.cropper.getCroppedCanvas({
                // 添加参数提升清晰度
                width: 200,
                height: 200,
                imageSmoothingQuality: 'high'
            })
            const roundedCanvas = this.getRoundedCanvas(croppedCanvas)
            //       如果不需要请求接口可直接将url更新到视图
            this.headerImage = roundedCanvas.toDataURL()

            //       接口接收file内容的blob对象
            
          if (!HTMLCanvasElement.prototype.toBlob) {
            Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
              value: function (callback, type, quality) {
                var canvas = this;
                setTimeout(function () {
                  var binStr = atob(canvas.toDataURL(type, quality).split(',')[1]);
                          var len = binStr.length;
                          var arr = new Uint8Array(len);
                          for (var i = 0; i < len; i++) {
                            arr[i] = binStr.charCodeAt(i);
                          }
 
                          callback(new Blob([arr], { type: type || 'image/png' }));
                });
              }
            });
          }
          let bold=this.dataURLtoBlob(this.headerImage)
          let resFile = this.blobToFile(bold, 'filename_' + Math.random(5));
            //传递给父组件,向后端提交
            var datas = {
                    img: this.headerImage,
                    file: resFile
            }
            this.$emit('to-parent', datas)
            //下面写法不兼容ie
//          roundedCanvas.toBlob((data) => {
//              var fileName=new Date().getTime()+'.png'
//              const file = new File([data], fileName, {
//                  type: 'image/png'
//              })
//              //传递给父组件,向后端提交
//              var datas = {
//                  img: this.headerImage,
//                  file: file
//              }
//              this.$emit('to-parent', datas)
//          })
            this.dialogVisible = false
        },
//      base64 转 Blob
        dataURLtoBlob(toDataURL) {
                        var arr = toDataURL.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 Blob([u8arr], {
                            type: mime
                        });
                    },
        //转成file
        blobToFile(Blob, fileName) {
                        Blob.lastModifiedDate = new Date();
                        Blob.name = fileName;
                        return Blob;
                },
        getRoundedCanvas(sourceCanvas) {
            const canvas = document.createElement('canvas')
            const context = canvas.getContext('2d')
            const width = sourceCanvas.width
            const height = sourceCanvas.height

            canvas.width = Math.min(300, width)
            canvas.height = Math.min(300, height)

            context.imageSmoothingEnabled = true
            // context.drawImage(sourceCanvas, 0, 0, width, height)  
            // 原图过大导致上传读取慢,截取300*300的尺寸
            context.drawImage(sourceCanvas, 0, 0, width, height, 0, 0, canvas.width, canvas.height)
            context.globalCompositeOperation = 'destination-in'
            context.beginPath()
            context.arc(width / 2, height / 2, Math.min(width, height) / 2, 0, 2 * Math.PI, true)
            context.fill()
            return canvas
        },
        // 触发上传
        handleAdd() {
            this.$refs.inputFile.dispatchEvent(new MouseEvent('click'))
        },
    }
}
</script>

<style lang="scss">
    .Upload {
        .el-dialog__body {
            padding-bottom: 0;
        }
        .yulan img {
            width: 200px;
            height: 200px;
            margin: 20px 0;
        }
        .small {
            width: 200px;
            height: 200px;
            overflow: hidden;
            margin: 0 auto;
            margin-top: 20px;
            border-radius: 50%;
            img {
                /*border-radius: 50%;*/
            }
        }
        position: relative;
        .handle_add {
            display: none;
        }
        .container {
            width: 100%;
        }
        #image {
            max-width: 100%;
            height: 300px;
        }
    }
    
    .cropper-view-box,
    .cropper-face {
        border-radius: 50%;
    }
    /*!
   * Cropper.js v1.0.0-rc
   * https://github.com/fengyuanchen/cropperjs
   *
   * Copyright (c) 2017 Fengyuan Chen
   * Released under the MIT license
   *
   * Date: 2017-03-25T12:02:21.062Z
   */
    
    .cropper-container {
        font-size: 0;
        line-height: 0;
        position: relative;
        -webkit-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
        user-select: none;
        direction: ltr;
        -ms-touch-action: none;
        touch-action: none
    }
    
    .cropper-container img {
        /* Avoid margin top issue (Occur only when margin-top <= -height) */
        display: block;
        min-width: 0 !important;
        max-width: none !important;
        min-height: 0 !important;
        max-height: none !important;
        width: 100%;
        height: 100%;
        image-orientation: 0deg
    }
    
    .cropper-wrap-box,
    .cropper-canvas,
    .cropper-drag-box,
    .cropper-crop-box,
    .cropper-modal {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
    }
    
    .cropper-wrap-box {
        overflow: hidden;
    }
    
    .cropper-drag-box {
        opacity: 0;
        background: #fff;
    }
    
    .cropper-modal {
        opacity: .5;
        background: #FFFFFF;
    }
    
    .cropper-view-box {
        display: block;
        overflow: hidden;
        width: 100%;
        height: 100%;
        outline: 1px solid #39f;
        outline-color: rgba(51, 153, 255, 0.75);
    }
    
    .cropper-dashed {
        position: absolute;
        display: block;
        opacity: .5;
        border: 0 dashed #eee
    }
    
    .cropper-dashed.dashed-h {
        top: 33.33333%;
        left: 0;
        width: 100%;
        height: 33.33333%;
        border-top-width: 1px;
        border-bottom-width: 1px
    }
    
    .cropper-dashed.dashed-v {
        top: 0;
        left: 33.33333%;
        width: 33.33333%;
        height: 100%;
        border-right-width: 1px;
        border-left-width: 1px
    }
    
    .cropper-center {
        position: absolute;
        top: 50%;
        left: 50%;
        display: block;
        width: 0;
        height: 0;
        opacity: .75
    }
    
    .cropper-center:before,
    .cropper-center:after {
        position: absolute;
        display: block;
        content: ' ';
        /* #eee */
    }
    
    .cropper-center:before {
        top: 0;
        left: -3px;
        width: 7px;
        height: 1px
    }
    
    .cropper-center:after {
        top: -3px;
        left: 0;
        width: 1px;
        height: 7px
    }
    
    .cropper-face,
    .cropper-line,
    .cropper-point {
        position: absolute;
        display: block;
        width: 100%;
        height: 100%;
        opacity: .1;
    }
    
    .cropper-face {
        top: 0;
        left: 0;
        /* #fff; */
    }
    
    .cropper-line.line-e {
        top: 0;
        right: -3px;
        width: 5px;
        cursor: e-resize
    }
    
    .cropper-line.line-n {
        top: -3px;
        left: 0;
        height: 5px;
        cursor: n-resize
    }
    
    .cropper-line.line-w {
        top: 0;
        left: -3px;
        width: 5px;
        cursor: w-resize
    }
    
    .cropper-line.line-s {
        bottom: -3px;
        left: 0;
        height: 5px;
        cursor: s-resize
    }
    
    .cropper-point {
        width: 5px;
        height: 5px;
        opacity: .75;
        /* #39f */
    }
    
    .cropper-point.point-e {
        top: 50%;
        right: -3px;
        margin-top: -3px;
        cursor: e-resize
    }
    
    .cropper-point.point-n {
        top: -3px;
        left: 50%;
        margin-left: -3px;
        cursor: n-resize
    }
    
    .cropper-point.point-w {
        top: 50%;
        left: -3px;
        margin-top: -3px;
        cursor: w-resize
    }
    
    .cropper-point.point-s {
        bottom: -3px;
        left: 50%;
        margin-left: -3px;
        cursor: s-resize
    }
    
    .cropper-point.point-ne {
        top: -3px;
        right: -3px;
        cursor: ne-resize
    }
    
    .cropper-point.point-nw {
        top: -3px;
        left: -3px;
        cursor: nw-resize
    }
    
    .cropper-point.point-sw {
        bottom: -3px;
        left: -3px;
        cursor: sw-resize
    }
    
    .cropper-point.point-se {
        right: -3px;
        bottom: -3px;
        width: 20px;
        height: 20px;
        cursor: se-resize;
        opacity: 1
    }
    
    @media (min-width: 768px) {
        .cropper-point.point-se {
            width: 15px;
            height: 15px
        }
    }
    
    @media (min-width: 992px) {
        .cropper-point.point-se {
            width: 10px;
            height: 10px
        }
    }
    
    @media (min-width: 1200px) {
        .cropper-point.point-se {
            width: 5px;
            height: 5px;
            opacity: .75
        }
    }
    
    .cropper-point.point-se:before {
        position: absolute;
        right: -50%;
        bottom: -50%;
        display: block;
        width: 200%;
        height: 200%;
        content: ' ';
        opacity: 0;
        /* #39f */
    }
    
    .cropper-invisible {
        opacity: 0;
    }
    
    .cropper-bg {
        background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMzTjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC');
    }
    
    .cropper-hide {
        position: absolute;
        display: block;
        width: 0;
        height: 0;
    }
    
    .cropper-hidden {
        display: none !important;
    }
    
    .cropper-move {
        cursor: move;
    }
    
    .cropper-crop {
        cursor: crosshair;
    }
    
    .cropper-disabled .cropper-drag-box,
    .cropper-disabled .cropper-face,
    .cropper-disabled .cropper-line,
    .cropper-disabled .cropper-point {
        cursor: not-allowed;
    }
</style>

3.在你想要使用的页面中调用

<!--头像裁剪-->
      <Cropper ref="Cropper" @to-parent="childData"/>
import Cropper from "@/components/component/cropper.vue"
components:{Cropper},

方法:

//点击调用头像裁剪
    selectPhotos(){
         this.$refs.Cropper.dialogChange();
    },
    //获取裁剪图片数据
    childData(data){
        this.form.headImg=data.img;
        this.file=data.file;
    },

相关文章

网友评论

    本文标题:vue中使用cropperjs实现上传头像并裁剪

    本文链接:https://www.haomeiwen.com/subject/drmjhltx.html