玩转前端图片压缩

2,076 阅读2分钟

上传图片到服务端是很常见的应用场景,由于图片尺寸不可控,在图像从前端传输给服务器之前,将图片压缩就显得很有意义,这样不仅可以节省带宽提高上传速率,还可以减轻服务器的存储压力。

这是我参与更文挑战的第10天,活动详情查看: 更文挑战

前端压缩,一般考虑两个维度:图片尺寸剪切和降低质量,本文将从这两点展开介绍。

图片剪切

关于图像剪切,我们需要借助ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)通过原始图像、位置和尺寸信息重新生成一份新的图片数据

  • image。表示显示原始图像的image元素
  • sx,sy。图片的剪切位置
  • sWidth,sHeight。图片的剪切尺寸
  • dx,dy。画布上的放置位置
  • dWidth,dHeight。新图像的尺寸

image.png

再说回图片剪切:当dWidth、dHeight小于sWidth、sHeight时,图片的尺寸就减小了,自然也就达到了压缩的目的。

拿老婆的照片来做测试:

<img src="./wife.png" alt="">
    <canvas width="254" height="419"></canvas>
    <script>
        var img = document.querySelector('img');
        //等待图像加载完毕
        img.onload = function(){
            //获取图像的原始尺寸
            var naturalImgSize = [img.naturalWidth,img.naturalHeight];
            //设置新图像的尺寸(相对原始图像尺寸缩小一倍)
            var finalImgSize = [naturalImgSize[0]/2,naturalImgSize[1]/2]
            //获取画布上下文
            var ctx = document.querySelector('canvas').getContext("2d");
           //绘制改变尺寸后的图像到画布上
           ctx.drawImage(img,0,0,naturalImgSize[0],naturalImgSize[1],0,0,finalImgSize[0],finalImgSize[1]);
        }
    </script>

效果图: Snipaste_2021-06-17_22-29-49.png

降低图像的质量

再说如何降低图像质量,这里我们需要借助一个方法:canvas.toBlob(callback, mimeType, qualityArgument);

  • callback是转换成功后的回调函数
  • mineType是将canvas转换图片的类型
  • qualityArgument是图像转换的质量,是一个0-1的数字(仅当第二个参数mimeType为jpg或者webp格式时才有效)

接下来我们将之前缩小了一倍的canvas上的图像无损转换成Blob对象:

canvas.toBlob(blob=>{
    console.log(blob)
},"image/jpeg",1)

image.png

可以看到最终的图像尺寸是102KB,而原始图像是868KB。压缩效果非常明显。

不过,如果我们需要对图片的size进行限制怎么办?比如不能超过80KB

这样我们就要用到第三个参数qualityArgument了,写个工具函数:

function compressImage(canvas,maxSize,quality){
    canvas.toBlob(blob=>{
        if(blob.size > maxSize){
            //这里将每次递减定小一些,防止图像质量过损
            compressImage(canvas,maxSize,quality-0.01)
        }else{
            console.log(blob)
        }
    },"image/jpeg",quality)
}
//限制图像尺寸为80KB
compressImage(canvas,80*1024,1)

可以看到图像的最终尺寸为:

image.png

输出压缩后的内容

上面已经讨论了对图像压缩,那么如何将压缩后的内容输出呢? 这里我们考虑两种常见的输出:

  • 图像文件
  • base64
function compressImage(canvas,maxSize,quality,cb){
    canvas.toBlob(blob=>{
        if(blob.size > maxSize){
            compressImage(canvas,maxSize,quality-0.01,cb)
        }else{
            var file = new File([blob], "image.jpeg");
            var base64 = canvas.toDataURL("image/jpeg",quality)
            cb({
                file,
                base64
            })
        }
    },"image/jpeg",quality)
}
compressImage(canvas,80*1024,1,(opt)=>{
    console.log(opt);
})

效果:

image.png

最后

最好,由于作者才疏学浅,内容难免出现疏漏,不当之处还望不吝赐教。

如果本文对你有用,麻烦留个赞再走,感谢阅读!