上一篇写到压缩图片的相关代码,后面开发中还有一些bug
例如无法同时上传多张图片,这里需要遍历压缩,还有压缩png图片背景黑色的问题解决 我调整了部分代码
<van-uploader :max-count="9" :after-read="afterReadImg" @delete="onDelete"
v-model="fileList" @click-upload="setBannerMax" multiple>
<div class="upload flex flex-col" v-if="fileList.length < 9">
<img class="add-img" src="@/assets/images/add_img.png" />
<span v-if="!fileList.length">拍照上传</span>
<span v-else>{{ fileList.length }}/9</span>
</div>
</van-uploader>
因为异步获取图片问题我这边把before-read方法去掉了,调取压缩放在了after-read方法里面
// 上传图片
async afterReadImg(file) {
let num = '';
for (let i = 0; i < 13; i++) { num += Math.floor(Math.random() * 10); }
//上传一张图片或拍照
if(!file.length){
file.status = 'uploading'
file.message = '上传中...'
this.blobToBase64(file.file).then(result=>{
//加水印
// this.addwater(file)
// this.$nextTick(()=>{
fileupload(result,this.form.service_id,num,this.address).then(res => {
if (res.status === 's') {
// this.form.banner = res.imgUrl
this.imgList.push({
url:res.imgUrl,
id: file.lastModified
})
this.changeImgString()
file.status = 'done'
file.message = '上传完成'
} else {
setTimeout(() => {
file.status = 'failed'
file.message = '上传失败'
}, 1000)
}
})
})
// })
}else{
//上传多张图片
file.forEach(item=>{
item.status='uploading'
item.message = '上传中...'
this.blobToBase64(item.file).then(result=>{
fileupload(result,this.form.service_id,num,this.address).then(res => {
if (res.status === 's') {
this.imgList.push({
url:res.imgUrl,
id: item.file.lastModified
})
this.changeImgString()
setTimeout(() => {
item.status = 'done'
item.message = '上传完成'
}, 1000)
} else {
setTimeout(() => {
item.status = 'failed'
item.message = '上传失败'
}, 1000)
}
})
})
})
}
}
这边是我根据项目需求添加了部分别的代码,可根据实际调整修改
将获取到的二进制文件 转 base64文件
async blobToBase64 (blob) {
const self = await this // 绑定this
const reader = await new FileReader() // 实例化一个reader文件
await reader.readAsDataURL(blob) // 添加二进制文件
return new Promise(function (resolve, reject) {
reader.onload = async function (event) {
const base64 = await event.target.result // 获取到它的base64文件
const scale = 0.70 // 设置缩放比例 (0-1)
let file = await self.compressImg(base64, scale)
resolve(file)
}
})
},
调用压缩图片方法
async compressImg (base64, scale) {
console.log(`执行缩放程序,scale=${scale}`)
// 处理缩放,转换格式
// 下面的注释就不写了,就是new 一个图片,用canvas来压缩
const img = new Image()
img.src = base64
return new Promise(function (resolve, reject) {
img.onload = async function () {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
canvas.setAttribute('width', this.width)
canvas.setAttribute('height', this.height)
// 在canvas绘制前填充白色背景 解决出现压缩png图片背景黑色问题
ctx.fillStyle = "#fff";
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
// 转成base64 文件
base64 = canvas.toDataURL('image/jpeg', 0.3)
const arr = base64.split(',')
const mime = arr[0].match(/:(.*?);/)[1]
const bytes = atob(arr[1])
const bytesLength = bytes.length
const u8arr = new Uint8Array(bytesLength)
for (let i = 0; i < bytes.length; i++) {
u8arr[i] = bytes.charCodeAt(i)
}
const file = await new File([u8arr], 'filename', { type: mime })
resolve(file)
}
})
},
开发过程中发现某些png格式图片透明背景压缩后会变成黑色
其实问题就出现在canvas转base64的方法上,不指定类型的话默认是使用image/jpeg格式转的,但是jpeg格式不能没有底色, 所以默认会填充黑色,如下图
详图如下
canvas.toDataURL(type, quality);
这个quality参数是指定清晰度的,只支持jpeg格式,不支持png,png设置了没用
既然透明图片会出现黑底,那么我们压缩前通过canvas把图片底色变成 白色不就可以了吗
有两种思路,一种是通过读取图片数据,设置透明的部分颜色成白色,一种是直接将canvas画一层白色底色再绘画图片
第一种:
这个我没有封装,需要的自行封装
// 将canvas的透明背景设置成白色
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
for(var i = 0; i < imageData.data.length; i += 4) {
// 当该像素是透明的,则设置成白色
if(imageData.data[i + 3] == 0) {
imageData.data[i] = 255;
imageData.data[i + 1] = 255;
imageData.data[i + 2] = 255;
imageData.data[i + 3] = 255;
}
}
context.putImageData(imageData, 0, 0);
第二种:我现在用这个方式
关键代码:
// 在canvas绘制前填充白色背景
context.fillStyle = "#fff";
context.fillRect(0, 0, canvas.width, canvas.height);
总结
以上是我压缩图片所用的所有方法和bug解决,代码还有许多可以优化,我一小白只能写到这个程度了