基于cropperjs和vue3的头像上传组件

前言

参考文章:关于input type=“file”的及其files对象的深层探究

cropperjs 不了解的可以看一下我的这篇文章:

cropperjs的简单使用

注: 只是写出来了,但是还没有实际用,如果存在bug欢迎留言

代码

<template>
    <div>
        <div
            :class="[
                'upload',
                `upload-${shape}`
            ]"
            :style="{ backgroundImage: 'url(' + (imgUrl) + ')' }"
            @click="openUpload"></div>
        <!-- 上传弹窗 -->
        <el-dialog
            v-model="uploadDialog"
            title="更换头像"
            width="560px"
            append-to-body
            :close-on-click-modal="false"
            @close="handleClose">
            <div class="container">
                <!-- 左侧裁剪区 -->
                <div class="left">
                    <!-- 大图显示区 -->
                    <!-- :style="{ 'background-image': 'url(' + imgUrl + ')' }" -->
                    <div class="big-image-preview">
                        <img :src="imgUrl" alt="大图" class="big-image" ref="imageRef" />
                    </div>
                    <div class="tool">
                        <i class="el-icon-refresh-left" @click="rotateImage(-45)"></i>
                        <i class="el-icon-circle-plus-outline" @click="zoomImage(0.2)"></i>
                        <i class="el-icon-remove-outline" @click="zoomImage(-0.2)"></i>
                        <i class="el-icon-refresh-right" @click="rotateImage(45)"></i>
                    </div>
                </div>
                <!-- 右侧预览区 -->
                <div class="right">
                    <!-- 小图预览区域 -->
                    <div class="right-top">
                        <div>预览</div>
                        <div
                            class="image-view"
                            :style="{ width: '100px', height: '100px', 'border-radius': shape == 'default' ? '10px' : '50%' }"></div>
                        <div>100 x 100</div>
                        <div
                            class="image-view"
                            :style="{ width: '50px', height: '50px', 'border-radius': shape == 'default' ? '10px' : '50%' }"></div>
                        <div>50 x 50</div>
                    </div>
                    <!-- 按钮 -->
                    <div class="right-bottom">
                        <div>
                            <el-button type="primary" size="small" @click="chooseImage">上传</el-button>
                            <el-button type="primary" size="small" @click="uploadImage">确定</el-button>
                        </div>
                    </div>
                </div>
            </div>
            <!-- 只用input来实现上传,但是不显示input -->
            <input
                v-show="false"
                ref="fileRef"
                type="file"
                accept="image/png, image/jpeg"
                @change="getImageInfo" />
        </el-dialog>
    </div>
</template>

<script>
import { defineComponent, nextTick, onMounted, ref } from 'vue';
import 'cropperjs/dist/cropper.css';
import Cropper from 'cropperjs';
import { ElMessage } from 'element-plus';
export default defineComponent({

    props: {
        // 形状
        shape: {
            type: String,
            default: 'round',
            validator: (val) => {
                return ['default', 'round'].includes(val);
            }
        },
        // 上传地址
        uploadUrl: {
            type: String,
            default: ''
        }
    },
    setup() {
        // 默认显示的图片
        let imgUrl = ref(require('../img/1111.png'));
        // 裁剪对象
        let cropper;

        /**
         * 处理弹窗相关
         */
        let handleDialog = () => {
            // 打开上传弹窗
            let uploadDialog = ref(false);
            // 打开弹窗方法
            let openUpload = () => {
                uploadDialog.value = true;
            };
            // 关闭弹窗
            let handleClose = () => {
                uploadDialog.value = false;
                cropper.destroy();
            };

            // input文件ref
            let fileRef = ref(null);
            // 选择图片
            let chooseImage = () => {
                // 当input的type属性值为file时,点击方法可以进行选取文件
                fileRef.value.click();
            };
            // 上传头像
            let uploadImage = () => {
                let cas = cropper.getCroppedCanvas();
                let base64url = cas.toDataURL('image/jpeg');
                cas.toBlob(function (e) {
                    console.log(e); // 生成Blob的图片格式
                });
                console.log('图片:',base64url); // 生成base64图片的格式
            };

            return {
                uploadDialog,
                openUpload,
                handleClose,
                fileRef,
                chooseImage,
                uploadImage
            };
        };

        // 处理图片上传
        let handleImageUpload = () => {
            // 裁剪的图片
            let imageRef = ref(null);

            // 获取文件信息
            let getImageInfo = (e) => {
                // 上传的文件
                let file = e.target.files[0];
                let fileSize = (file.size / 1024).toFixed(2);
                if(fileSize > 1024) {
                    ElMessage.warning('图片大小必须在1MB以内!');
                    return false;
                }
                // 获取 window 的 URL 工具
                let URL = window.URL || window.webkitURL;
                // 通过 file 生成目标 url
                imgUrl.value = URL.createObjectURL(file);
                // console.log('图片:', imgUrl);
                nextTick(() => {
                    // 判定裁剪对象是否存在
                    // 存在销毁重新创建(这里不替换图片,图片不一样大时会变形),不存在创建对象
                    if(cropper) {
                        cropper.destroy();
                        cropImage();
                    }else{
                        cropImage();
                    }
                });
            };

            // 裁剪图片
            let cropImage = () => {
                if (imageRef.value) {
                    cropper = new Cropper(imageRef.value, {
                        // 宽高比
                        aspectRatio: 1,
                        viewMode: 1,
                        // 预览
                        preview: '.image-view',
                        // cropBoxResizable: false,
                        background: false,
                        crop(event) {
                            // console.log('裁剪');
                        }
                    });
                }
            };

            // 旋转
            let rotateImage = (angle) => {
                cropper.rotate(angle);
            };

            // 缩放
            let zoomImage = (num) => {
                cropper.zoom(num);
            };

            return {
                imageRef,
                getImageInfo,
                cropImage,
                rotateImage,
                zoomImage
            };
        };

        return {
            imgUrl,
            ...handleDialog(),
            ...handleImageUpload()
        };
    }

});
</script>

<style scoped lang="scss">
//上传的基本样式
.upload {
    width: 150px;
    height: 150px;
    border: 5px solid #eeeeee;
    box-sizing: border-box;
    cursor: pointer;
    background-position: center center;
    background-size: 100%;
}

//hover的基本样式
.base-hover {
    position: absolute;
    width: 100%;
    height: 100%;
    content: "更换头像";
    background: black;
    color: #ffffff;
    display: flex;
    justify-content: center;
    align-items: center;
    opacity: 0.6;
}

//默认形状
.upload-default {
    border-radius: 5px;
    position: relative;

    &:hover {
        &::before {
            @extend .base-hover;
            border-radius: 5px;
        }
    }
}

//圆形
.upload-round {
    border-radius: 50%;
    position: relative;

    &:hover {
        &::before {
            @extend .base-hover;
            border-radius: 50%;
        }
    }
}

.container {
    width: 520px;
    height: 400px;
    display: flex;
    .left {
        width: 65%;
        height: 100%;

        .big-image-preview {
            width: 100%;
            height: 85%;
            background-size: 100% 100%;
            background-repeat: no-repeat;
            background-position: center center;
        }

        .tool {
            width: 100%;
            height: 15%;
            font-size: 30px;
            display: flex;
            justify-content: center;
            align-items: center;
            i {
                margin: 0px 10px;
                cursor:pointer;
            }
        }
        .big-image {
            width: 100%;
            height: 100%;
            display: block;
            max-width: 100%;
        }
    }
    .right {
        width: 35%;
        height: 100%;
        font-size: 14px;

        .right-top {
            width: 100%;
            height: 70%;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: space-evenly;
            .image-view {
                border: 1px solid gray;
                overflow: hidden;
            }
        }
        .right-bottom{
            width: 100%;
            height: 30%;
            display: flex;
            flex-direction: column-reverse;
            align-items: center;
        }
    }
}
</style>

效果图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无知的小菜鸡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值