图片压缩原理
将图片重新画入一个canvas中。可设置最大宽度,再按图片宽高比例定义canvas画布的宽高。
完整代码demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>上传图片</title>
<meta name="renderer" content="webkit">
<meta name="viewport"
content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"/>
<link rel="stylesheet" href="css/upload-img.css">
</head>
<body>
<div id="app" class="container" v-cloak>
<div class="uploading-data" v-if="isUploading"></div>
<div class="upload-img-column">
<div class="words">上传图片 ({{imgTempList.length}}/5)</div>
<div class="upload-wrap">
<div class="box">
<label class="p dotted">
<input type="file" accept="image/jpg,image/jpeg,image/png" name="file"
@change="onChooseImage($event)"/>
<img src="./img/jiahao.png" alt="">
</label>
</div>
<template v-for="(imgItem, imgIndex) in imgTempList">
<div class="box">
<div class="p">
<img :src="imgItem">
<div class="delete" @click.stop="deleteImg(imgIndex)">
<img src="./img/guanbi.png" alt="">
</div>
</div>
</div>
</template>
</div>
</div>
<button class="l-btn" @click="onUploadImg">上传</button>
<!-- 图片上传成功后返回的路径(没必要的) -->
<div class="success-path">
<template v-for="(item, index) in successPath">
<a :href="item" target="_blank">{{item}}</a>
</template>
</div>
</div>
<script src="js/vue-2.5.21.js"></script>
<script src="js/axios.js"></script>
<script>
var app = new Vue({
el: '#app',
data: {
imgTempList: [], //图片临时路径列表
isUploading: false, //是否正在上传
successPath: [], //上传成功后的路径(没必要)
},
mounted: function () {
var that = this;
},
watch: {},
methods: {
//选择图片
onChooseImage: function (event) {
var that = this;
//判断图片数量是否已上限
var currentImgTempArray = that.imgTempList;
if (currentImgTempArray.length >= 5) {
alert("最多上传5张图片");
return false;
}
//使用FileReader对文件对象进行操作
var reader = new FileReader();
reader.readAsDataURL(event.target.files[0]); //将读取到的文件编码成Data URL
reader.onload = function () { //读取完成时
var replaceSrc = reader.result; //文件输出的内容
//调用图片压缩处理方法
that.compressedImage({
src: replaceSrc,
quality: 0.8,
success: function (src) {
//将压缩后的路径 追加到临时路径数组中
var totalList = [];
if (currentImgTempArray.length > 0) {
totalList = currentImgTempArray.concat(src);
} else {
totalList[0] = src;
}
that.imgTempList = totalList;
}
});
};
},
//删除某张图片
deleteImg: function (idx) {
var that = this;
that.imgTempList.splice(idx, 1);
},
//提交上传图片
onUploadImg: function () {
var that = this;
var imgTempList = that.imgTempList;
if (imgTempList.length > 0) {
that.isUploading = true; //正在上传 显示遮罩层 防止连续点击
var countNum = 0; //计算数量用的 判断上传到第几张图片了
//map循环遍历上传图片
imgTempList.map(function (imgItem, imgIndex) {
var files = that.dataURLtoFile(imgItem, 'pj' + Date.now() + '.jpg'); //DataURL转File
//创FormData对象
var formdata = new FormData();
//append(key,value)在数据末尾追加数据。 这儿的key值需要和后台定义保持一致
formdata.append('img', files);
//用axios上传,
axios({
method: "POST",
url: "http://www.clluo.com:8060/uploadImg",
data: formdata,
headers: {
"Content-Type": "multipart/form-data"
}
}).then(function (res) {
countNum++;
//图片全部上传完后去掉遮罩层
if (countNum >= imgTempList.length) {
that.isUploading = false;
}
//没必要的代码 👇
var list = [];
if (that.successPath.length > 0) {
list = that.successPath.concat(res.data.path);
} else {
list[0] = res.data.path;
}
that.successPath = list;
}).catch(function (error) {
console.error(error);
});
});
}
},
/**
* 压缩图片处理
* @src 需要压缩的图片base64路径
* @quality 图片质量 0-1,默认1
* @success() 成功后的回调
* */
compressedImage: function (params) {
var that = this;
var initParams = {
src: params.src || "",
quality: params.quality || 1,
};
var image = new Image();
image.src = initParams.src;
image.onload = function () {
//获取图片初始宽高
var width = image.width;
var height = image.height;
//判断图片宽度,再按比例设置宽度和高度的值
if (width > 1024) {
width = 1024;
height = Math.ceil(1024 * (image.height / image.width));
}
//将图片重新画入canvas中
var canvas = document.getElementById("compressCanvas");
if(!canvas){ //创建一个canvas画布
var body = document.body;
canvas = document.createElement("canvas"); //创建canvas标签
canvas.id = "compressCanvas"; //给外层容器添加一个id
canvas.style.position = "fixed";
canvas.style.zIndex = "-1";
canvas.style.opacity = "0";
canvas.style.top = "-100%";
canvas.style.left = "-100%";
body.append(canvas);
}
var context = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
context.beginPath();
context.fillStyle = "#ffffff";
context.fillRect(0, 0, width, height);
context.fill();
context.closePath();
context.drawImage(image, 0, 0, width, height);
var replaceSrc = canvas.toDataURL("image/jpeg", initParams.quality); //canvas转DataURL(base64格式)
params.success && params.success(replaceSrc);
};
},
/**
* 将base64转换为文件
* @dataUrl base64路径地址
* @fileName 自定义文件名字
* */
dataURLtoFile: function (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>
</body>
</html>
css代码
[v-cloak] {
display: none;
}
* {
margin: 0;
padding: 0;
list-style: none;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
word-break: break-all;
word-wrap: break-word;
}
.container {
width: 100%;
max-width: 600px;
min-width: 320px;
margin: 0 auto;
}
.container .l-btn {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
width: 150px;
height: 40px;
color: #fff;
background: #05AFAE;
border: none;
border-radius: 5px;
-webkit-box-shadow: 1px 2px 3px rgba(0, 0, 0, .1);
box-shadow: 1px 2px 3px rgba(0, 0, 0, .1);
margin: 50px auto;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
-ms-flex-direction: row;
flex-direction: row;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
}
.container .l-btn:focus {
outline: none;
}
.container .l-btn:active {
-webkit-transition: .1s;
-o-transition: .1s;
transition: .1s;
-webkit-transform: scale(.95);
-ms-transform: scale(.95);
transform: scale(.95);
}
/* 上传图片栏目 START */
.upload-img-column {
width: 100%;
padding: 10px;
}
.upload-img-column .words {
font-size: 14px;
}
.upload-img-column .upload-wrap {
width: 100%;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
-ms-flex-direction: row;
flex-direction: row;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
}
.upload-img-column .upload-wrap .box {
width: 33.33%;
padding: 10px 10px 0 0;
}
.upload-img-column .upload-wrap .box .p {
width: 100%;
height: 0;
padding-bottom: 100%;
border-radius: 5px;
position: relative;
overflow: hidden;
}
.upload-img-column .upload-wrap .box .p img {
position: absolute;
width: 100%;
height: 100%;
border-radius: 5px;
overflow: hidden;
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.upload-img-column .upload-wrap .box .p .delete {
width: 30px;
height: 30px;
text-align: center;
border-radius: 50%;
background: #e0e0e0;
position: absolute;
right: 0;
top: 0;
z-index: 2;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
-ms-flex-direction: row;
flex-direction: row;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
opacity: 0.8;
}
.upload-img-column .upload-wrap .box .p .delete:active {
-webkit-transform: scale(0.95);
-ms-transform: scale(0.95);
transform: scale(0.95);
}
.upload-img-column .upload-wrap .box .p .delete img {
width: 14px;
height: 14px;
}
.upload-img-column .upload-wrap .box .p.dotted {
display: block;
border: dotted 2px #999;
}
.upload-img-column .upload-wrap .box .p.dotted input {
display: none;
}
.upload-img-column .upload-wrap .box .p.dotted img {
width: 60%;
height: auto;
position: absolute;
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
opacity: .8;
}
/* 上传图片栏目 END */
.uploading-data {
position: fixed;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.3);
left: 0;
top: 0;
z-index: 1000;
}
.success-path {
width: 100%;
padding: 0 10px;
}
.success-path a {
display: block;
margin-top: 6px;
}