使用`Canvas`实现图片压缩,压缩率高达99%

102 阅读2分钟

先看布局

<label class="container">
    <div class="select-file"></div>
    <input type="file" id="compress" />
    <p class="compress-bef">
        压缩前文件大小:
        <span></span>
    </p>
</label>

<div class="preview">
    <p class="compress-aft">
        压缩后文件大小:
        <span></span>
    </p>
</div>

样式我就略过了 底部放源码自行查阅

 const container = document.querySelector('.container'),
    inp = container.querySelector('input'),
    seleFile = container.querySelector('.select-file'),
    preview = document.querySelector('.preview'),
    befSize = document.querySelector('.compress-bef'),
    aftSize = preview.querySelector('.compress-aft');

先获取需要的元素

再来个创建图片的函数

function createImg(blob) {
    const img = new Image();

    // 如果传入`Blob`对象 则进行转换 否则当作`base64`
    if (typeof blob !== 'string') {
        const fr = new FileReader();
        fr.onload = function () {
            img.src = this.result;
            blob = this.result;
        };
        fr.readAsDataURL(blob);
    }
    else {
        img.src = blob;
    }

    return new Promise((resolve) => {
        img.onload = () => {
            resolve([img, blob]);
        };
    });
}

这个函数通过FileReader获取base64,然后给图片赋值。

可能有人不懂base64,我就简单解释一下吧。

Base64释义

  • 26 * 2个的英文字母组成,也就是大小写
  • 加上10个数字
  • 再加上/或者+,全部加起来一共64的个基础字符串

问答环节

??为什么要用他

  • 字符串方便网络传输
  • 减少http请求 适用于小图片
  • Base64编码可以避免数据在传输过程中被修改或损坏,因为它使用了64个可打印字符,这些字符在大多数编码和系统中都是通用和安全的

绑定事件

inp.onchange = async ({ target: { files: [file] } }) => {
    const [img, base64] = await createImg(file);
    container.appendChild(img);
    console.log('bef', base64)
    befSize.textContent = base64.length;
    setImgWrap();  // 无关紧要的设置样式
    
    // 压缩
    const compressBase64 = createCompressImg(file);
    console.log('aft', compressBase64)
    const compressImg = await createImg(compressBase64);
    preview.appendChild(compressImg[0]);
    aftSize.textContent = compressBase64.length;
}

上面把图片加入元素,然后对图片进行压缩,来看看压缩函数的实现

function createCompressImg(file, quality) {
    const cvs = document.createElement('canvas'),
        ctx = cvs.getContext('2d'),
        img = container.querySelector('img');
    cvs.width = img.width;
    cvs.height = img.height;

    ctx.drawImage(img, 0, 0, img.width, img.height);
    return cvs.toDataURL('image/jpeg', quality);
}

首先创建canvas,宽高设置为图片宽高,再获取上下文,并把图片画上去。

来看MDNtoDataURL的释义

image.png

第一个参数

  • MIME文件类型: String,当使用jpeg/ webp时,支持压缩
  • 压缩率: Number,0 ~ 1的范围,默认0.92
  • @return: String 返回一个base64字符串

通过上面的函数,就实现了图片压缩,来看压缩对比,我用的是默认压缩率。

image.png

可以看到,默认设置压缩率就很高了,而且你传入的压缩率参数影响不大

源码 gitee.com/cjl2385/dig…