官网:https://github.com/xyxiao001/vue-cropper
- Npm安装及导入
- 主要代码
- 组件封装
- 组件使用
- 实现效果
一、Npm安装及导入
npm install vue-cropper@next
Vue3
组件内引入
import 'vue-cropper/dist/index.css'
import { VueCropper } from "vue-cropper";
Vue3
全局引入
import VueCropper from 'vue-cropper';
import 'vue-cropper/dist/index.css'
const app = createApp(App)
app.use(VueCropper)
app.mount('#app')
二、主要代码
组件封装
<template>
<el-row>
<!-- 上传文件 -->
<el-col :span="20">
<input
style="display: none;"
type="file"
ref="uploadRef"
accept="image/"
@change="uploadImg($event,1)"
>
<!-- 上传修改样式 -->
<div class="cursor-pointer mb-5" @click="uploadRef.click()">
<el-input v-model="imgName" :placeholder="请选择图片" readonly>
<template #prepend><el-icon><Picture /></el-icon></template>
</el-input>
</div>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<!-- 裁剪 -->
<VueCropper
style="width:320px;height:320px"
ref="cropperRef"
:img="imgUrl"
:autoCrop="options.autoCrop"
:fixedBox="options.fixedBox"
:canMoveBox="options.canMoveBox"
:centerBox="options.centerBox"
:outputType="options.outputType"
:outputSize="options.outputSize"
:fixed="options.fixed"
:fixedNumber="options.fixedNumber"
@realTime="realTime"
>
</VueCropper>
</el-col>
<!-- 预览 -->
<el-col :span="12">
<div class="text-base">{{"图标预览"}}</div>
<div class="preview" :style="previewFileStyle">
<img class="preview_img" v-if="previews.url" :src="previews.url" :style="previews.img">
</div>
</el-col>
</el-row>
</template>
<script>
import 'vue-cropper/dist/index.css'
import { VueCropper } from "vue-cropper";
import { ref } from 'vue-demi';
import { toast } from '@/utils/common';
export default {
props: {
options: {
type: Object,
default: () => {}
},
// 预览框宽度
previewWidth: {
type: Number,
default: 300
},
},
components: {
VueCropper
},
emits: ['croppedData'],
setup(props, {emit}) {
const previews = ref({});
const imgUrl = ref('');
const imgName = ref('');
const uploadRef = ref();
// 上传显示选择的图片
const uploadImg = (e,num) => {
var file = e.target.files[0];
if(!/\.(jpg|png|JPG|PNG)$/.test(e.target.value)){
toast('文件类型必须是jpg,png中的一种', 'error');
return false;
}
if ((e.target.files[0].size / 1024 ) > 500) {
toast('图片大小不能超过500KB', 'error');
return false;
}
imgName.value = file?.name;
//fileReader 接口,用于异步读取文件数据
var reader = new FileReader();
//以dataURL形式读取显示文件
reader.readAsDataURL(file);
reader.onload = e => {
let data = e.target.result;
imgUrl.value=data;
}
};
// 实时预览
const previewFileStyle = ref({});
const realTime = (data) => {
previews.value=data;
previewFileStyle.value = {
width: data.w + 'px',
height: data.h + 'px',
margin: 0,
overflow: 'hidden',
zoom: props.previewWidth / data.w, // 固定显示宽度
border: data.w && data.h ? '1px solid #868484': '0px',
'border-radius': '50%'
}
}
const cropperRef = ref();
const croppedData = ref('');
const getCroppedData = () => {
cropperRef.value.getCropData(data => {
croppedData.value = data;
emit('croppedData',data);
});
}
return {
realTime,
uploadImg,
imgUrl,
imgName,
previews,
previewFileStyle,
getCroppedData,
cropperRef,
uploadRef
}
}
}
</script>
<style scoped lang="scss">
.preview{
.preview_img {
max-width: 100000%; // 预览图片缩放预留宽度
}
}
:deep(.el-input__inner){
cursor: pointer;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
</style>
组件使用
<CCropper
ref="cropperRef"
:options="options"
@croppedData="croppedData">
</CCropper>
<button @click="handleLogoSubmit">确认</button>
const options = ref({
autoCrop:true, //默认生成截图框
fixedBox:false, //固定截图框大小
canMoveBox:true, //截图框不能拖动
centerBox:false, //截图框被限制在图片里面
// autoCropWidth:"300px", //默认生成截图框宽度 默认:80% 可选:0-max
// autoCropHeight: "104.04px", //默认生成截图框高度 默认:80% 可选:0-max
fixed:true, //截图框宽高固定比例
fixedNumber:[1,1], //宽高比例
outputType:"png" //裁剪生成图片的格式
});
// 点击上传
const cropperRef = ref();
const handleLogoSubmit = async() => {
// 组件中的去获取截取的数据传到父组件
await cropperRef.value.getCroppedData();
}
// 取得截取base64格式数据并上传(通常需要转为FormData格式上传)
const croppedData = (data) => {
// 格式转换
const logoFile = dataURLtoBlob(data);
let formData = new FormData();
formData.append('file', logoFile, "logo.png");
// 上传图片的接口
uploadIcon(formData);
}
// base64 转 二进制流(blob)
const dataURLtoBlob = (dataUrl) => {
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 Blob([u8arr], { type: mime })
}
三、实现效果
欢迎交流~