项目需求
对用户上传的图片,压缩后再上传到服务器。
发现的问题
如果选择的图片背景图是透明的,压缩后的图片,背景竟然变成了黑色!!!
案例:随便选择一张透明图片,发现,压缩后,背景变成黑色。ps:图片的高宽,暂时随便设置的,导致图片变形了,不用理会。我们只关注背景色。
分析原因
查看image-conversion
源码,发现
/**
* 将一个Canvas对象转变为一个dataURL字符串
* 该方法可以做压缩处理
*
* @param {canvas} canvas
* @param {number=} quality - 传入范围 0-1,表示图片压缩质量,默认0.92
* @param {string=} type - 确定转换后的图片类型,选项有 "image/png", "image/jpeg", "image/gif",默认"image/jpeg"
* @returns {Promise(string)} Promise含有一个dataURL字符串参数
*/
export default async function canvastoDataURL(canvas: HTMLCanvasElement, quality: number = 0.92, type: EImageType = EImageType.JPEG): Promise<string> {
if (!checkImageType(type)) {
type = EImageType.JPEG; // 没有指定类型默认是jpeg
}
console.log('122 canvastoDataURL',type)
return canvas.toDataURL(type, quality);
};
可以看到不指定类型的话默认是使用image/jpeg格式转的,但是jpeg格式不能没有底色, 所以默认会填充黑色。
如果将官方设置提供配置参数 type, 设置成’image/png’
,可不可行?
设置了这个确实不会出现黑底的情况了, 但是压缩效果又没了, 原因就是我们设置了 image/png
格式后, 那么cavans转base646就是按照png格式转,但png格式不管怎么转,转出的大小还是和原来一样。因为png格式不支持设置清晰度,也就是这个方法:
canvas.toDataURL(type, quality);
怎么办?
解决方案
思路:先将图片转成白色底图,再调用image-conversion
方法对图片压缩。
先创建一个file.js
文件,代码如下:
import * as imageConversion from 'image-conversion';
export const compressImageConversion = (file, base64, callBack) => {
compressPic(file, base64).then(handleFile => {
// 将图片压缩到100kb
imageConversion.compressAccurately(handleFile, 100).then(res => {
res = new File([res], file.name);
callBack(res)
})
})
}
export const compressPic = (file, base64) => {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = () => {
const img = new Image();
img.src = base64;
img.onload = () => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
canvas.width = img.width;
canvas.height = img.height;
// ctx.fillStyle ctx.fillRect 这两行代码是重点,给图片添加白色底图
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, img.width, img.height)
// 这里的图片类型,改成动态获取,即上传的图片是什么类型,就获取它的类型,传进来就好
const handleBase64 = canvas.toDataURL("imgae/png", 1);
const handleFile = base642File(handleBase64, "test.png");
resolve(handleFile);
};
};
reader.readAsDataURL(file);
});
}
export const base642File = (base64, fileName) => {
let arr = base64.split(',')
let mime = arr[0].match(/:(.*?);/)[1]
let bstr = atob(arr[1])
let n = bstr.length
let u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], fileName, { type: mime })
}
export const file2Base64 = file => {
return new Promise((resolve) => {
let reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = (e) => {
resolve(e.target.result)
}
})
}
重要代码!!!
ctx.fillStyle = “#ffffff”;
ctx.fillRect(0, 0, canvas.width, canvas.height);
在vue中使用
<template>
<div>
<van-uploader :after-read='afterRead' accept='.jpg,.jpeg,.png' />
<div class="pic">
<div class="pic-item">
<p style="font-size: 16px;">压缩前的图片展示:</p>
<div>
<img :src="base64Before" alt="" style="width: 400px;height: 300px;">
</div>
</div>
<div class="pic-item">
<p style="font-size: 16px;">压缩后的图片展示:</p>
<div>
<img :src="base64After" alt="" style="width: 400px;height: 300px;">
</div>
</div>
</div>
</div>
</template>
<script>
import { compressImageConversion, file2Base64 } from '../utils/file.js'
export default {
data() {
return {
base64Before: '',
base64After: '',
}
},
methods: {
afterRead(file) {
console.log('压缩前', file)
this.base64Before = file.content
compressImageConversion(file.file, file.content, (handleFile) => {
console.log('压缩后', handleFile)
file2Base64(handleFile).then(handleBase64 => {
this.base64After = handleBase64
})
})
}
}
}
</script>
<style>
.pic {
display: flex;
}
.pic-item {
margin: 10px;
}
</style>
效果:
封装的file.js
里面的方法,可以直接复制到项目中使用。