今天分享一个图片上传和截取功能,需求要求固定尺寸的截取,按照自己的需求进行配置,
实现效果如下:
首先参考element的上传参考地址:Element - The world's most popular Vue UI framework,
截取的部分主要用到了vue-cropper
第一步:
安装 npm install vue-cropper
第二步:
1.全局使用 main.js
import VueCropper from 'vue-cropper'
Vue.use(VueCropper)
2.组件内使用
import { VueCropper } from 'vue-cropper'
components: {
VueCropper,
},
接下来直接上代码:
<template>
<div>
<el-upload
class="heightCredential"
:action="action"
:headers="headers"
:file-list="list"
:show-file-list="isList"
:accept="accept"
:on-success="handleSuccess"
:before-upload="beforeUpload"
>
<div class="uploadBorder flex_jc_center flex_ai_center">
<img v-if="!isList && list.length" :src="list[0].url" @click.stop=""/>
<i v-else class="el-icon-plus"></i>
<slot/>
</div>
<i v-show="!isList && list.length" class="el-icon-close img-close" @click.stop="clearFile"></i>
</el-upload>
<el-dialog
append-to-body
title="广告裁剪"
ref="dialog"
width="1200"
class="img-cropper"
:visible.sync="dialogVisible"
:close-on-click-modal="false"
>
<div class="cropper-w">
<div class="cropper" :style="{ width: '100%', height: '360px' }">
<vueCropper
ref="cropper"
:img="option.img"
:outputSize="option.size"
:outputType="option.outputType"
:info="option.info"
:full="option.full"
:canMove="option.canMove"
:canMoveBox="option.canMoveBox"
:original="option.original"
:autoCrop="option.autoCrop"
:autoCropWidth="option.autoCropWidth"
:autoCropHeight="option.autoCropHeight"
:fixedBox="option.fixedBox"
></vueCropper>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="finish">确认</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'BaseCropperUpload',
props: ["fileList"],
computed: {
list() {
return this.fileList
}
},
data() {
return {
action: 上传图片地址,
dialogVisible:false,
option: {
img: "", // 裁剪图片的地址
info: true, // 裁剪框的大小信息
outputSize: 0.8, // 裁剪生成图片的质量
outputType: "jpeg", // 裁剪生成图片的格式
canScale: false, // 图片是否允许滚轮缩放
autoCrop: true, // 是否默认生成截图框
autoCropWidth: 1200, // 默认生成截图框宽度
autoCropHeight: 158, // 默认生成截图框高度
fixedBox: true, // 固定截图框大小 不允许改变
fixed: false, // 是否开启截图框宽高固定比例
fixedNumber: [7, 5], // 截图框的宽高比例
full: true, // 是否输出原图比例的截图
canMoveBox: true, // 截图框能否拖动
original: true, // 上传图片按照原始比例渲染
centerBox: true, // 截图框是否被限制在图片里面
infoTrue: true, // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
},
}
},
created() {
},
methods: {
beforeUpload(file) {
// 图片转为base64
let self = this
const _URL = window.URL || window.webkitURL
const image = new Image();
image.src = _URL.createObjectURL(file);
self.option.img = ''
image.onload = function () {
let base64 = self.transBase64FromImage(image);
self.option.img = base64
}
this.picWidth = 1200
this.dialogVisible = true
},
// 将网络图片转换成base64格式
transBase64FromImage(image) {
let canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
let ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 0, image.width, image.height);
// 可选其他值 image/jpeg
return canvas.toDataURL("image/png");
},
// base64转成files
transformToFiles(dataurl, filename) {
var arr = dataurl.split(","),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, { type: mime });
},
handleSuccess({data: url, code, msg}, {name}) {
if (code !== 200) {
this.$message.error(msg)
} else {
this.$emit('handleFile', [{url, name}])
}
},
clearFile() {
this.$emit('removeImage')
this.$emit('handleFile', [])
},
finish() {
let _this = this
this.$refs.cropper.getCropBlob(async (data) => {
let formData = new FormData();
console.log(data);
if(data){
var timestamp = new Date().getTime() + '.jpg'
// 将blob格式转换为文件。File() 构造器创建新的 File 对象实例。
const newFile = new window.File([data], timestamp);
formData.append('file',newFile)
//调用axios上传
let {data: res} = await axios.post(上传文件地址, formData)
if(res.code === 200){
let data = res.data.replace('[','').replace(']','').split(',');
let imgInfo = {
name : _this.Name,
url : data[0]
};
this.dialogVisible = false
}
} else {
this.$message.warning('请重新截图')
}
})
_this.$forceUpdate()
},
},
}
}
具体的配置在option里面,finish方法主要是截取并上传,这快用到的是axios进行上传的。
完整代码:
<template>
<div>
<el-upload
class="avatar-uploader"
action="上传图片地址"
:show-file-list="false"
:before-upload="beforeAvatarUpload"
:data="{ deadline: 0, file_size: 512 * 1024 }"
:model="imageUrl"
ref="upload"
accept="image/*"
:limit="1"
>
<img v-if="imageUrl" :src="imageUrl" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
<el-dialog
title="封面裁剪"
ref="dialog"
width="40%"
:visible.sync="dialogVisible"
:close-on-click-modal="false"
>
<div class="cropper-w">
<div class="cropper" :style="{ width: '100%', height: '280px' }">
<vueCropper
ref="cropper"
:img="option.img"
:outputSize="option.size"
:outputType="option.outputType"
:info="option.info"
:full="option.full"
:canMove="option.canMove"
:canMoveBox="option.canMoveBox"
:original="option.original"
:autoCrop="option.autoCrop"
:autoCropWidth="option.autoCropWidth"
:autoCropHeight="option.autoCropHeight"
:fixedBox="option.fixedBox"
></vueCropper>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="handleConfirm">确认</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
data() {
return {
dialogVisible: false,
imageUrl: "",
option: {
img: "", // 裁剪图片的地址
info: true, // 裁剪框的大小信息
outputSize: 0.8, // 裁剪生成图片的质量
outputType: "jpeg", // 裁剪生成图片的格式
canScale: false, // 图片是否允许滚轮缩放
autoCrop: true, // 是否默认生成截图框
autoCropWidth: 380, // 默认生成截图框宽度
autoCropHeight: 100, // 默认生成截图框高度
fixedBox: true, // 固定截图框大小 不允许改变
fixed: true, // 是否开启截图框宽高固定比例
fixedNumber: [7, 5], // 截图框的宽高比例
full: true, // 是否输出原图比例的截图
canMoveBox: true, // 截图框能否拖动
original: true, // 上传图片按照原始比例渲染
centerBox: false, // 截图框是否被限制在图片里面
infoTrue: true, // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
},
};
},
methods: {
beforeAvatarUpload(file) {
this.filename = file.name;
this.openCropper(file);
return false;
},
openCropper(file) {
var _this = this;
console.log(_this);
const isJPG =
file.type === "image/jpeg" ||
file.type === "image/jpg" ||
file.type === "image/png" ||
file.type === "image/PNG" ||
file.type === "image/JPG";
if (!isJPG) {
this.$message.error("上传图片只能为jpg或png格式");
return;
}
var reader = new FileReader();
reader.onload = (e) => {
let data;
if (typeof e.target.result === "object") {
// 把Array Buffer转化为blob 如果是base64不需要
data = window.URL.createObjectURL(new Blob([e.target.result]));
} else {
data = e.target.result;
}
_this.option.img = data;
_this.dialogVisible = true;
};
// 转化为base64
reader.readAsDataURL(file);
// 转化为blob
// reader.readAsArrayBuffer(file);
},
handleConfirm() {
console.log(this.$refs.cropper);
this.$refs.cropper.getCropBlob((data) => {
// if (data.size > 2097152) {
// this.showMsg("图片大于2M,请进行裁剪或重新选择");
// }
let blob = window.URL.createObjectURL(data);
this.downImg = blob;
var base64;
var img = new Image();
img.src = blob;
var _that = this;
img.onload = function () {
var that = this;
//生成比例
var w = that.width,
h = that.height,
scale = w / h;
h = w / scale;
//生成canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = w;
canvas.height = h;
ctx.drawImage(that, 0, 0, w, h);
// 生成base64
_that.cropperPic = canvas.toDataURL("image/jpeg", 0.8);
let files = _that.transformToFiles(_that.cropperPic, _that.filename);
_that.temporaryCloseCropper = true;
// XMLHttpRequest 请求 --最后决定使用 XMLHttpRequest 来进行上传图片
var xhr = new XMLHttpRequest();
xhr.timeout = 3000;
xhr.ontimeout = function (event) {
console.log("请求超时!");
};
let param = new FormData();
param.append("file", files, _that.filename);
param.append("deadline", 0); // 按照接口需求情况添加
param.append("file_size", 512 * 1024); // 按照接口需求情况添加
xhr.open(
"POST",
"上传图片地址"
);
xhr.send(param);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
console.log(JSON.parse(xhr.responseText));
let res = JSON.parse(xhr.responseText);
if(res.code==200){
_that.imageUrl = res.data;
console.log(_that.imageUrl);
_that.dialogVisible = false;
}else {
console.log(res.msg);
}
} else {
console.log(xhr.statusText);
}
};
// 生成图片
// _that.$refs.upload.$children[0].handleChange({
// target: { files: [files] },
// });
// 使用此方法 需要在 upload 里 action 设置接口地址
// _that.$refs.upload.$children[0].post(files)
};
});
},
// base64转成files
transformToFiles(dataurl, filename) {
var arr = dataurl.split(","),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, { type: mime });
},
},
};
</script>
<style lang='scss'>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409eff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
width: 178px;
height: 178px;
display: block;
}
</style>