首先说一下,压缩主要是通过减低清晰度和缩小图片宽高进行的。
使用这里不讲,请看官方结束: https://www.npmjs.com/package/image-conversion
1.问题分析
由于项目需要压缩图片上传oss,一开始使用image-conversion库,但是发现某写png格式图片透明背景压缩后会变成黑色,看了源码,其实问题就出现在canvas转base64的方法上
/**
* 将一个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格式不能没有底色, 所以默认会填充黑色
2.解决思路
(1) 设置type和width
一开始想到的是官方设置提供配置参数 type, 设置成’image/png’
但是问题来了,设置了这个确实不会出现黑底的情况了, 但是压缩效果又没了, 原因就是我们设置了 image/png 格式后, 那么cavans转base646就是按照png格式转,但png格式不管怎么转,转出的大小还是和原来一样,也就是png格式不支持设置清晰度,也即是这个方法中
canvas.toDataURL(type, quality);
这个quality参数是指定清晰度的,只支持jpeg格式,不支持png,png设置了没用
那么,不能通过设置清晰度方式进行压缩,那只能指定最大宽度了
所以,当我们要指定输出是png格式, 一定要指定宽度, 这样才会有压缩效果。
但是哦,这样又有问题了,这样size参数又没有效果了,没有达到预期想法,因为我想的是直接压缩到指定大小。
(2) 手动对透明图片进行转换
既然透明图片会出现黑底,那么我们压缩前通过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);
完整的一个文件处理方法:
pngToWhiteBg(file) {
let read = new FileReader();
read.readAsDataURL(file); // 文件转base64
return new Promise((resolve, reject) => {
read.onload = (e) => {
let img = new Image();
img.src = e.target.result;
img.onload = async () => {
// 生成canvas
let canvas = document.createElement("canvas");
let context = canvas.getContext("2d");
// 绘制图片到canvas上
canvas.width = img.width;
canvas.height = img.height;
// 在canvas绘制前填充白色背景
context.fillStyle = "#fff";
context.fillRect(0, 0, canvas.width, canvas.height);
context.drawImage(img, 0, 0);
let base64 = canvas.toDataURL(file["type"], 1);
let newFile = this.dataUrlToFile(base64);
resolve(newFile);
};
};
});
},
dataUrlToFile(dataurl) {
let 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 Blob([u8arr], { type: mime });
},
因为我后面自己重新写了一份压缩代码工具,思路参考image-conversion库,所以我直接在image转canvas时进行透明图转白色底,工具库我写在公司的,暂时不贴出来。
总结
这里只讨论了png问题,入坑了,花了两天时间研究。。