一、安装
npm install vue-cropper@next
二、引入 Vue Cropper
1.组件引入
import 'vue-cropper/dist/index.css'
import {
VueCropper } from "vue-cropper";
2.全局引入
import VueCropper from 'vue-cropper';
import 'vue-cropper/dist/index.css'
const app = createApp(App)
app.use(VueCropper)
app.mount('#app')
三、页面中使用
html
<template>
<div class="drawing-container">
<vueCropper
ref="cropper"
:img="option.img"
:outputSize="option.outputSize"
:outputType="option.outputType"
:info="option.info"
:canScale="option.canScale"
:autoCrop="option.autoCrop"
:autoCropWidth="option.autoCropWidth"
:autoCropHeight="option.autoCropHeight"
:fixedBox="option.fixedBox"
:fixed="option.fixed"
:fixedNumber="option.fixedNumber"
:canMove="option.canMove"
:canMoveBox="option.canMoveBox"
:original="option.original"
:centerBox="option.centerBox"
:infoTrue="option.infoTrue"
:full="option.full"
:enlarge="option.enlarge"
:mode="option.mode"
></vueCropper>
<div class="update-img-btn">
<el-upload
class="avatar-uploader"
action=""
accept="image/png, image/jpeg"
:show-file-list="false"
:on-change="beforeAvatarUpload"
:auto-upload="false"
>
<el-link :underline="false" type="primary">Select a photo</el-link>
</el-upload>
<el-button type="primary" @click="handleSaveAvatar">Save</el-button>
</div>
</div>
</template>
js
<script setup>
import {
reactive, ref, toRaw} from "vue";
import {
fileToBase64Async} from "../../utils/fileUtils.js";
const cropper=ref()
let option=reactive( {
img: '', // 裁剪图片的地址 url 地址, base64, blob
outputSize: 1, // 裁剪生成图片的质量
outputType: 'jpeg', // 裁剪生成图片的格式 jpeg, png, webp
info: true, // 裁剪框的大小信息
canScale: false, // 图片是否允许滚轮缩放
autoCrop: true, // 是否默认生成截图框
autoCropWidth: 150, // 默认生成截图框宽度
autoCropHeight: 150, // 默认生成截图框高度
fixedBox: false, // 固定截图框大小 不允许改变
fixed: true, // 是否开启截图框宽高固定比例
fixedNumber: [1, 1], // 截图框的宽高比例 [ 宽度 , 高度 ]
canMove: true, // 上传图片是否可以移动
canMoveBox: true, // 截图框能否拖动
original: false, // 上传图片按照原始比例渲染
centerBox: true, // 截图框是否被限制在图片里面
infoTrue: true, // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
full: true, // 是否输出原图比例的截图
enlarge: '1', // 图片根据截图框输出比例倍数
mode: 'contain' // 图片默认渲染方式 contain , cover, 100px, 100% auto
})
const beforeAvatarUpload=async (file) => {
option.img = await toRaw(fileToBase64Async(file.raw))
}
const handleSaveAvatar=()=>{
cropper.value.getCropBlob((data)=>{
//此处为上传图片逻辑
console.log(data)
})
}
</script>
fileUtils
/**
* file 转Base64 DataURL
* @param {File} file
* @returns
*/
export function fileToBase64Async(file) {
return new Promise((resolve, reject) => {
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = (e) => {
resolve(e.target.result);
};
});
}
/**
* 解析base64中的数据信息
* @param {String} base64
* @returns
*/
function parseBase64(base64) {
let arr = base64.split(","),
mime = arr[0].match(/:(.*?);/)[1];
return {
mime,
data: arr[1],
};
}
/**
* base64转Uint8
* @param {String} base64
* @returns
*/
function base64ToUint8Array(base64) {
let parsedBase64 = parseBase64(base64);
let bstr = atob(parsedBase64.data);
let n = bstr.length;
let u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return u8arr;
}
/**
* 将base64转换为blob
* @param {String} base64
* @returns
*/
function base64ToBlob(base64) {
let parsedBase64 = parseBase64(base64);
let u8arr = base64ToUint8Array(base64);
return new Blob([u8arr], {
type: parsedBase64.mime });
}
/**
* 将blob转换为file
* @param {Blob} blobData
* @returns
*/
function blobToFile(blobData) {
let date = new Date();
blobData.lastModifiedDate = date;
blobData.lastModified = date.getTime();
blobData.name = blobData.type.replace("/", ".");
return blobData;
}
/**
* base64 转 File
* @param {String} base64
* @returns
*/
export function base64ToFile(base64) {
let file = null;
// 浏览器兼容
if (window.File != undefined) {
let parsedBase64 = parseBase64(base64);
let u8arr = base64ToUint8Array(base64);
file = new File([u8arr], parsedBase64.mime.replace("/", "."), {
type: parsedBase64.mime,
});
} else {
file = blobToFile(base64ToBlob(base64));
}
return file;
}