vue利用cropperjs实现图片按比例裁剪
效果图
通过npm或yarn安装cropperjs
npm install cropperjs@next
yarn add cropperjs@next
并在文件内引入对应模块
import Cropper from "cropperjs";
import 'cropperjs/dist/cropper.css';
完整代码:
<template>
<div>
<el-dialog
v-model="uploadDialog"
:title="dialogTitle"
width="600px"
append-to-body
destroy-on-close
:close-on-click-modal="false">
<div class="container">
<!-- 左侧裁剪区 -->
<div class="left">
<!-- 大图显示区 -->
<!-- :style="{ 'background-image': 'url(' + imgUrl + ')' }" -->
<div class="big-image-preview">
<img v-if="imgShow" ref="imageDom" :src="imgUrl" alt="" class="big-image" />
<div v-else style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center"> 点击选择图片 </div>
</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 v-if="aspectRatioShow">
<div style="margin-bottom: 10px">
宽高比设置:
</div>
<div style="display: flex; align-items: center">
<el-input v-model="aspectRatioWidth" placeholder="宽" />
<div style="margin: 0 7px">:</div>
<el-input v-model="aspectRatioHeight" placeholder="高" />
<el-button type="primary" size="small" style="margin-left: 10px; background-color: #78b2c6; border-color: #78b2c6" @click="setDataCropper">确定</el-button>
</div>
</div>
<div>预 览</div>
<div v-for="item in shape" class="image-view" :key="item" :style="{ width: '100px', height: '100px', 'border-radius': item == 'default' ? '10px' : '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 lang="ts">
import {onMounted, ref, reactive, toRefs, nextTick} from "vue";
//引入依赖
import Cropper from "cropperjs";
import 'cropperjs/dist/cropper.css';
import {UploadImgPic} from "@/api";
import {useStore} from "vuex";
export default {
props: {
// 形状
shape: {
type: Array,
default: () => {
return ['default', ] // 'round'
},
request: true,
},
// 上传地址
uploadUrl: {
type: String,
default: ''
},
// 可自定义宽高比例
aspectRatioShow: {
type: Boolean,
default: true,
}
},
setup(props: any, context: any){
const imageDom = ref(null);
const fileRef = ref(null);
const store = useStore();
const pageData: any = reactive({
init: store.state.loginInfo.userInfo,
// 裁剪后的图片
afterImg:'',
imgUrl: '',
// 裁剪的图片
imageDom: imageDom, // img dom元素
fileRef: fileRef,
imgShow: false, // img标签 显示与隐藏
// 进行裁剪
myCropper: null,
uploadDialog: false,
dialogTitle: '',
cropper: Cropper,
aspectRatioWidth: 1,
aspectRatioHeight: 1,
// 打开弹窗方法
openUpload: (imgUrl: any) => {
pageData.uploadDialog = true;
if (imgUrl) {
pageData.imgUrl = imgUrl;
pageData.imgShow = true;
pageData.dialogTitle = '更换头像';
nextTick(() => {
pageData.cropImage()
})
} else {
pageData.imgShow = false;
pageData.dialogTitle = '添加头像';
nextTick(() => {
pageData.chooseImage();
})
}
},
chooseImage: () => {
// 当input的type属性值为file时,点击方法可以进行选取文件
pageData.fileRef.click();
},
// 处理图片上传
getImageInfo: (e: any) => {
// 上传的文件
let file = e.target.files[0];
// let fileSize = (file.size / 1024).toFixed(2);
// if (Number(fileSize) > 1024) {
// ElMessage.warning('图片大小必须在1MB以内!');
// return false;
// }
// 获取 window 的 URL 工具
let URL = window.URL || window.webkitURL;
// 通过 file 生成目标 url
pageData.imgUrl = URL.createObjectURL(file);
pageData.imgShow = true;
// console.log('图片:', imgUrl);
nextTick(() => {
// 判定裁剪对象是否存在
// 存在销毁重新创建(这里不替换图片,图片不一样大时会变形),不存在创建对象
if(pageData.myCropper) {
pageData.myCropper.destroy();
pageData.cropImage();
}else{
pageData.cropImage();
}
})
},
setDataCropper: () => {
if(pageData.myCropper) {
pageData.myCropper.destroy();
pageData.cropImage();
}else{
pageData.cropImage();
}
},
// 裁剪图片
cropImage: () => {
if (pageData.imageDom) {
pageData.myCropper = new Cropper(pageData.imageDom, {
/*
* viewMode 视图控制
- 0 无限制
- 1 限制裁剪框不能超出图片的范围
- 2 限制裁剪框不能超出图片的范围 且图片填充模式为 cover 最长边填充
- 3 限制裁剪框不能超出图片的范围 且图片填充模式为 contain 最短边填充
* */
viewMode: 0,
// 设置图片是否可以拖拽功能
/*
* dragMode 拖拽图片模式
- crop 形成新的裁剪框
- move 图片可移动
- none 什么也没有
* */
dragMode: 'move',
// 是否显示图片后面的网格背景,一般默认为true
background: true,
// 进行图片预览的效果
preview:'.image-view',
// 设置裁剪区域占图片的大小 值为 0-1 默认 0.8 表示 80%的区域
autoCropArea: 0.8,
// 设置图片是否可以进行收缩功能
zoomOnWheel: true,
// 是否显示 + 箭头
center: true,
// 宽高比
aspectRatio: pageData.aspectRatioWidth / pageData.aspectRatioHeight,
// cropBoxResizable: false,
crop(event: any) {
// console.log('裁剪');
}
});
}
},
handleClose: () => {
pageData.imgShow = false;
},
// 旋转
rotateImage: (angle: any) => {
pageData.myCropper?.rotate(angle);
},
// 缩放
zoomImage: (num: any) => {
pageData.myCropper?.zoom(num);
},
// 上传头像
uploadImage: () => {
let cas = pageData.myCropper.getCroppedCanvas({ width: 500, height: 500 });
// let base64url = cas.toDataURL('image/jpeg');
cas.toBlob(async function (e: any) {
console.log(e);
});
},
sureSava: () => {
// 拿到裁剪后的图片
pageData.afterImg = pageData.myCropper.getCroppedCanvas({
imageSmoothingQuality:'high'
}).toDataURL('image/jpeg'); // 设置图片格式
}
})
// 页面刷新自动执行
onMounted(()=>{})
return{
...toRefs(pageData),
}
}
}
</script>
<style scoped lang="less">
#Box{
border: 1px silver solid;
padding: 20px;
margin-top: 20px;
border-radius: 5px;
height: 800px;
}
.before{
width: 150px;
height: 150px;
position: relative;
left: 150px;
overflow: hidden;
}
.box{
display: flex;
column-gap: 6rem;
align-items: center;
justify-content: center;
margin-top: 20px;
div{
flex: 1;
height: 500px;
background: #ccc;
img{
display: block;
}
}
.box_2{
img{
width: 200px;
margin: 0 auto;
}
}
}
//上传的基本样式
.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: 85%;
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: 14%;
display: flex;
flex-direction: column-reverse;
align-items: center;
}
}
}
</style>