效果图
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/e111d82e67f54aef6289b2b62fdbb880.png)
安装 Vue-cropper
npm install vue-cropper
yarn add vue-cropper
引入及封装组件
<template>
<div class="user-info-head" @click="editCropper()">
<el-image
:src="options.img"
title="点击上传头像"
class="img-circle img-lg"
/>
<el-dialog v-model="dialogVisible" title="图片裁剪" width="50%">
<el-row>
<el-col :xs="24" :md="13" :style="{ height: '350px' }">
<VueCropper
ref="cropperRef"
:img="options.img"
:info="true"
:infoTrue="options.infoTrue"
:auto-crop="options.autoCrop"
:fixed-box="options.fixedBox"
:fixedNumber="options.fixedNumber"
:fixed="options.fixed"
:centerBox="options.centerBox"
@realTime="realTime"
>
</VueCropper>
</el-col>
<el-col :xs="24" :md="11" :style="{ height: '350px' }">
<div class="avatar-upload-preview">
<img :src="previews.url" :style="previews.img" />
</div>
</el-col>
</el-row>
<el-row class="ptb2">
<el-col :lg="2" :md="2">
<el-upload
action="#"
:http-request="requestUpload"
:show-file-list="false"
:before-upload="beforeUpload"
>
<el-button>
选择
<el-icon class="el-icon--right"><Upload /></el-icon>
</el-button>
</el-upload>
</el-col>
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="Plus" @click="changeScale(1)"></el-button>
</el-col>
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="Minus" @click="changeScale(-1)"></el-button>
</el-col>
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button
icon="RefreshLeft"
@click="rotateClick('left')"
></el-button>
</el-col>
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button
icon="RefreshRight"
@click="rotateClick('right')"
></el-button>
</el-col>
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button @click="refreshCrop()">重 置</el-button>
</el-col>
<el-col :lg="{ span: 1, offset: 7 }" :md="2">
<el-button type="primary" @click="uploadImg()">提 交</el-button>
</el-col>
</el-row>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref, toRefs, onMounted } from "vue";
import VueCropper from "vue-cropper/src/vue-cropper.vue";
import utils from "@/utils/utils";
interface Props {
avatar: string;
}
const props = withDefaults(defineProps<Props>(), {
avatar: "",
});
let { avatar } = toRefs(props);
interface Options {
img: string | ArrayBuffer | null;
info: true;
outputSize: number;
outputType: "jpeg";
canScale: boolean;
autoCrop: boolean;
autoCropWidth: number;
autoCropHeight: number;
fixedBox: boolean;
fixed: boolean;
fixedNumber: Array<number>;
full: boolean;
canMoveBox: boolean;
original: boolean;
centerBox: boolean;
infoTrue: boolean;
}
const options: Options = reactive({
img: "",
autoCrop: true,
autoCropWidth: 150,
autoCropHeight: 150,
fixedBox: false,
info: true,
outputSize: 0.5,
outputType: "jpeg",
canScale: false,
fixed: false,
fixedNumber: [1, 1],
full: true,
canMoveBox: false,
original: false,
centerBox: true,
infoTrue: true,
});
const previews: any = ref({});
const cropperRef: any = ref({});
const dialogVisible = ref(false);
let rotateClick = (type: string) => {
if (type == "left") {
cropperRef.value.rotateLeft();
}
if (type == "right") {
cropperRef.value.rotateRight();
}
};
let changeScale = (num: number) => {
const scale = num || 1;
cropperRef.value.changeScale(scale);
};
let refreshCrop = () => {
cropperRef.value.refresh();
};
const realTime = (data: any) => {
previews.value = data;
};
let requestUpload = () => {};
let beforeUpload = (file: any) => {
let img = [
"image/png",
"image/jpg",
"image/jpeg",
"image/PNG",
"image/JPG",
"image/JPEG",
];
if (!img.includes(file.type)) {
utils._message(`图片格式有误,请重新上传`, "error");
return false;
}
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
options.img = reader.result;
};
};
function editCropper() {
dialogVisible.value = true;
}
let uploadImg = () => {
cropperRef.value.getCropData((data: any) => {
let formData = dataURLtoFile(data,'fileName.jpg')
console.log(formData);
dialogVisible.value = false;
});
};
const dataURLtoFile = (dataUrl, filename) => {
const arr = dataUrl.split(',')
const mime = arr[0].match(/:(.*?);/)[1]
const bstr = atob(arr[1])
let len = bstr.length
const u8arr = new Uint8Array(len)
while (len--) {
u8arr[len] = bstr.charCodeAt(len)
}
return new File([u8arr], filename, { type: mime })
}
onMounted(() => {
setTimeout(() => {
options.img = avatar.value;
}, 100);
});
</script>
<style scoped lang="less">
.avatar-upload-preview {
position: absolute;
top: 50%;
transform: translate(50%, -50%);
width: 200px;
height: 200px;
border-radius: 50%;
box-shadow: 0 0 4px #ccc;
overflow: hidden;
}
.user-info-head {
position: relative;
display: inline-block;
height: 120px;
}
.user-info-head:hover:after {
content: "+";
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
color: #eee;
background: rgba(0, 0, 0, 0.5);
font-size: 24px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
cursor: pointer;
line-height: 110px;
border-radius: 50%;
}
.img-circle {
border-radius: 50%;
}
.img-lg {
width: 120px;
height: 120px;
cursor: pointer;
}
</style>
页面中引入
<UserAvatar :avatar="userInfo.icon" />
import UserAvatar from "@/components/userAvatar/index.vue";