使用到的组件:vueCropper,van-popup,van-uploader
首先基于vueCropper封装了一个子组件页面:ImageCropper.vue
<template>
<!-- 剪裁图片组件 -->
<van-popup
class="bg-tran image-cropper"
v-model="visible"
closeable
position="top"
:lock-scroll="false"
:style="{ height: '100%' }"
>
<div class="flex-column-center height100">
<vueCropper
ref="imageCropper"
:img="url"
:outputSize="option.outputSize"
:outputType="option.outputType"
:info="option.info"
:full="option.full"
:autoCropWidth="option.autoCropWidth"
:autoCropHeight="option.autoCropHeight"
:canMove="option.canMove"
:canMoveBox="option.canMoveBox"
:original="option.original"
:autoCrop="option.autoCrop"
:fixed="option.fixed"
:fixedNumber="option.fixedNumber"
:centerBox="option.centerBox"
:infoTrue="option.infoTrue"
:fixedBox="option.fixedBox"
:high="option.high"
:mode="option.mode"
></vueCropper>
<van-col span="24" class="font14 col-white">
<van-col span="8" class="p-2" @click="onCancel"><span>取消</span></van-col>
<van-col span="8" class="p-2 text-center" @click="rotateImage"><span class="font18"><van-icon name="replay" /></span></van-col>
<van-col span="8" class="p-2 text-right" @click="onConfirm"><span>确定<i class="el-icon-loading" v-if="loading"></i></span></van-col>
</van-col>
</div>
</van-popup>
</template>
<script>
import {
//随机字符串
randomString as utilRandomString
} from "@/utils/utility";
import { VueCropper } from 'vue-cropper';
const COS = require('cos-js-sdk-v5');
export default {
components: {
VueCropper,
},
props: {
visible: Boolean, //控制是否显示
url: String //裁剪的图片
},
data() {
return {
loading: false,
option: {
outputSize: 0.8,
info: false, // 裁剪框的大小信息
outputType: 'jpeg', // 裁剪生成图片的格式
canScale: false, // 图片是否允许滚轮缩放
autoCrop: true, // 是否默认生成截图框
autoCropWidth: window.innerWidth - 100 + 'px', // 默认生成截图框宽度
autoCropHeight: window.innerWidth - 100 + 'px', // 默认生成截图框高度
high: true, // 是否按照设备的dpr 输出等比例图片
fixedBox: true, // 固定截图框大小 不允许改变
fixed: true, // 是否开启截图框宽高固定比例
fixedNumber: [1, 1], // 截图框的宽高比例
full: true, // 是否输出原图比例的截图
canMoveBox: false, // 截图框能否拖动
original: false, // 上传图片按照原始比例渲染
centerBox: false, // 截图框是否被限制在图片里面
infoTrue: false, // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
mode: '100% auto' // 图片默认渲染方式
}
}
},
methods: {
rotateImage() {
//旋转图片
this.$refs.imageCropper.rotateRight();
},
onCancel() {
//取消
this.$emit('cancel');
},
onConfirm() {
//确定
if(this.loading) {
return ;
}
this.loading = true;
this.$refs.imageCropper.getCropBlob((file) => {
//此时可以拿file进行文件上传了,以下为腾讯云COS的上传demo,可以改为自己的上传方式
this.handleChange(file).then((data)=>{
//上传成功 执行回调
this.$emit('success', file);
this.loading = false;
//请求接口,更新头像操作
}, (error) => {
this.loading = false;
});
});
},
handleChange(file) {
//文件上传
return new Promise((resolve, reject)=>{
//获取腾讯云COS临时密钥
axios("xxxxx", "get", {
fileType: 'file'
})
.then((response) => {
var credentials = response.credentials;
var cos = new COS({
getAuthorization: function (options, callback) {
callback({
TmpSecretId: credentials.tmpSecretId,
TmpSecretKey: credentials.tmpSecretKey,
SecurityToken: credentials.sessionToken,
StartTime: credentials.startTime, // 时间戳,单位秒
ExpiredTime: credentials.expiredTime, // 过期时间 时间戳
})
},
})
// 上传文件
cos.putObject({
Bucket: 'xxxx', //存储桶
Region: 'xxxx', //存储桶所在地域
Key: 'xxxx', //在前面加上路径写法可以生成文件夹
StorageClass: 'xxxx', //设置对象的存储类型,枚举值
Body: file, //上传文件对象
}, (error, data) => {
if(error == undefined) {
//返回数据
resolve(data)
}else {
//返回异常
reject(error);
}
})
})
.catch((error) => {
reject(error);
});
})
}
}
}
</script>
<style>
.image-cropper .height100 {
height: 100vh;
}
.image-cropper .flex-column-center {
display: flex;
flex-flow: column;
justify-content: center;
align-items: center;
}
.image-cropper .vue-cropper {
background: #000;
}
.image-cropper .vue-cropper .cropper-view-box {
outline: 1px solid #fff !important;
outline-color: #fff !important;
}
.image-cropper .van-col {
height: 50px;
line-height: 50px;
margin-bottom: 30px;
}
.image-cropper .van-col .el-icon-loading {
margin-left: 5px;
}
</style>
父页面引入方式如下
<template>
<div class="my-info">
<div>上传图片裁剪</div>
<img :src="avatarUrl" />
<van-uploader :before-read="beforeRead" :after-read="afterRead" accept="image/png, image/jpeg" :max-size="1024 * 1024 * 11" @oversize="onOversize">
<van-button type="primary">上传头像</van-button>
</van-uploader>
<image-cropper
:visible="cropperVisible"
:url="imgUrl"
:file-info="fileInfo"
@cancel="cropperVisible = false"
@success="cropperSuccess"
/>
</div>
</template>
<script>
import ImageCropper from '@/components/ImageCropper'
import { Toast } from 'vant';
export default {
components: { ImageCropper },
data() {
return {
fileInfo: {},
imgUrl: '',
avatarUrl: require('@/assets/img/default-avatar.png'), //默认头像
cropperVisible: false
};
},
methods: {
onOversize() {
Toast('图片大小不能超过10M');
},
beforeRead(file) {
if (file.type !== 'image/png' && file.type !== 'image/jpeg') {
Toast('请上传图片');
return false;
}
return true;
},
cropperSuccess(file) {
//裁剪上传完成
this.avatarUrl = 'https://' + file.location;
this.cropperVisible = false;
//请求保存头像操作
axios("/upload/xxxx", "post", {})
.then((response) => {
Toast('上传头像成功');
})
.catch((error) => {
Toast('上传头像失败,请重试');
});
},
afterRead(file) {
//图片加载完成 进行裁剪
file = file.file;
const fileReader = new FileReader();
fileReader.readAsDataURL(file);
fileReader.onload = () => {
//显示裁剪框
this.imgUrl = fileReader.result;
//将文件信息传过去
this.fileInfo = {
name: file.name,
suffix: file.name.substring(file.name.lastIndexOf(".") + 1)
};
this.cropperVisible = true;
};
}
},
};
</script>
这样就大功告成了!