image-conversion库压缩png图片背景黑色问题解决

首先说一下,压缩主要是通过减低清晰度和缩小图片宽高进行的。

使用这里不讲,请看官方结束: 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-conversion库压缩png图片背景黑色问题解决
但是问题来了,设置了这个确实不会出现黑底的情况了, 但是压缩效果又没了, 原因就是我们设置了 image/png 格式后, 那么cavans转base646就是按照png格式转,但png格式不管怎么转,转出的大小还是和原来一样,也就是png格式不支持设置清晰度,也即是这个方法中

canvas.toDataURL(type, quality);

这个quality参数是指定清晰度的,只支持jpeg格式,不支持png,png设置了没用

那么,不能通过设置清晰度方式进行压缩,那只能指定最大宽度了
所以,当我们要指定输出是png格式, 一定要指定宽度, 这样才会有压缩效果。
image-conversion库压缩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问题,入坑了,花了两天时间研究。。

上一篇:使用CANVAS实现表盘刻度过渡效果


下一篇:Android实现人脸识别动画效果