深入前端图片压缩

article/2025/10/11 10:18:03

前言

任何研究都不是凭空产生,至少博主是这样的。

在手机端拍照后图片很大,有的甚至有 10M 多。这个时候再去上传图片,可想而知,速度是很慢的。正因如此,便有了前端图片压缩这个需求。

图片格式

传统的图片格式有 git、jpg/jpeg、png 等,谷歌后来推出了另一种压缩比更高的格式 webp。

在介绍这几种图片格式前,先说下图片的一些特性。

有损和无损

  • 有损:指在图片压缩过程中,会丢失一部分图片信息,并且这种丢失是不可逆的。
  • 无损:指在图片压缩过程中,图片质量没有损失,任何时候都可以从无损压缩过的图片中恢复出原本的信息。

索引色和直接色

  • 索引色:用一个数字来代表一种颜色,在存储图片的时候,存储一个数字的组合,同时存储数字到图片颜色的映射。一般是用一个字节(8 位)来存储,这样颜色只有 2^8,即 256 种。
  • 直接色:使用四个数字来代表颜色,这四个数字分别代表这个颜色中红色、绿色、蓝色以及透明度。大家应该用过 rgb(255, 255, 255) 这种方式表示颜色,所以不难理解这种方式呈现的颜色大约是 2^24 种。

点阵图和矢量图

  • 点阵图:也叫做位图,像素图。构成点阵图的最小单位是象素,位图就是由象素阵列的排列来实现其显示效果的,每个象素有自己的颜色信息。这种图放大后,图片会模糊,有颗粒感。
  • 矢量图:也叫做向量图。矢量图并不纪录画面上每一点的信息,而是纪录了元素形状及颜色的算法。这种图无论放大多少倍,由于算法不变,所以图片还是会保持原样,不会模糊。
格式特性备注
bmp无损、直接色、点阵图体积较大,目前很少适用了
git无损、索引色、点阵图适用于动图
jpg/jpeg有损、直接色、点阵图适用于色彩丰富的图像,如风景和人物照片
png8无损、索引色、点阵图适用于有透明要求,不需要特别丰富色彩的图像
png24无损、直接色、点阵图适用于有透明要求,需要特别丰富色彩的图像。该格式体积要比 jpg 大的多
webp无损/无损、直接色、点阵图适用于网页,但是目前兼容性稍差

图片压缩试手

如果自己从底层实现图片压缩,那可以涉及到颜色编码处理了。对于,前端工程师来说,难度还是很大的。博主这里提到的图片压缩是借助 Canvas 的 API 来实现图片的压缩处理。

先简要说下思路:

  1. 通过 input 标签得到图片文件
  2. 通过 URL.createObjectURL 将 file 对象转为一个内存地址(或者用 FileReader 得到 base64)
  3. 创建一个 img 元素,将其 src 指向上一步的内存地址
  4. 创建一个 canvas 对象,将 img 写到 canvas 画布上
  5. 通过 canvas 的 toDataURL 方法压缩图片

这里给出代码:

<input type="file" id="file">
<a id="downImg" download="compress">download</a>
<script>const file = document.getElementById('file')file.addEventListener('change', function (e) {const imageFile = e.target.files[0]const imgUrl = URL.createObjectURL(imageFile)const imgEle = document.createElement('img')imgEle.src = imgUrlimgEle.onload = function () {const canvas = document.createElement('canvas')const ctx = canvas.getContext('2d')canvas.width = imgEle.widthcanvas.height = imgEle.heightctx.drawImage(imgEle, 0, 0)const data = canvas.toDataURL('image/jpeg', 0.7)const downImg = document.getElementById('downImg')downImg.href = data}}, false)
</script>

看到这里,基本上就实现了图片压缩的基本需求。但如果是这么简单,博主就不会专门写一篇文章来介绍了。

图片压缩方向

一般来说,我们压缩图片,是希望能够无损压缩的。但是这种无损压缩对于前端来说,比较困难。博主做了一些研究,还没有找到用 JS 实现的方式。

而有损压缩,有两个方向:

  1. 降低图片质量
  2. 缩小图片大小

降低图片质量

这个比较简单就是调整一个参数值。

canvas.toDataURL(type, encoderOptions);

encoderOptions:在指定图片格式为 image/jpegimage/webp 的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92。其他参数会被忽略。

经过一些测试比较,encoderOptions 设为 0.7 比较合适(这个测试比较粗糙,仅供参考)。既能保证比较大的压缩比,又能较少损失图片的成像质量。

缩小图片大小

一般来说我们是等比例的缩小原图,canvas 提供了一个 API 可以很方便的帮助我们去缩小图片的大小。

ctx.drawImage(image, dx, dy, dWidth, dHeight)
  • dx:image 的左上角在目标 canvas 上 X 轴坐标。
  • dy:image 的左上角在目标 canvas 上 Y 轴坐标。
  • dWidth:image 在目标 canvas 上绘制的宽度。 允许对绘制的 image 进行缩放。 如果不说明, 在绘制时 image 宽度不会缩放。
  • dHeight:image 在目标 canvas 上绘制的高度。 允许对绘制的 image 进行缩放。 如果不说明, 在绘制时 image 高度不会缩放。

下面这张图,大家可以借助它理解。
在这里插入图片描述

如果我们要调整图片的大小,只需要改变 dWidth 和 dHeight 的值即可。

压缩算法设计

普通版

如果我们的诉求比较简单,那么我们可以采用简单的设计思路。

经过我们测试发现,图片在 200k 以下,我们的传输速度在 1 秒以内,可以接受。所以,如果原图小于 200k,我们不进行压缩。

如果原图是 png 格式,我们需要衡量下是否进行压缩。因为 canvas 是不支持无损的 png 压缩的。如果我们要进行压缩,那就需要转为 jpeg 或者 webp 格式,同时 png 的透明效果将会消失。

一般来说,我们是希望保存原始图片的长宽的。那么我们可用的方式就是改变图片的质量了,前面已经提到比较适合的一个值是 0.7。通过这个设置,一般图片的压缩率可以达到 60~95%。图片越大,压缩的效果越明显。png 格式转为 jpeg 情况压缩率也非常高。

加强版

前面的算法,虽然可以帮助我们较大程度的压缩图片,但是最终的体积还是不确定。有的时候,我们希望图片最终压缩到一个指定的大小。

从前面的内容来看,并没有什么方法可以使图片压缩到指定的大小。不过,这里我们有一个接近指定大小的压缩算法,那就是递归压缩。当我们第一次压缩后,如果得到的图片大小和指定的大小不一样,我们就再次进行压缩。

这里我们需要注意,即使是递归压缩,我们也没办法指定一个确定的大小。因为很难在某一次压缩后,图片的大小正好是我们期望的大小。如果不理解,先仔细想一想。

既然不能给一个确定的值,那我们该怎么给出大小呢?答案就是给一个范围。只要图片压缩后的大小落在这个范围内,那么我们就停止压缩。

这里,博主不建议只给一个边界值,因为这样图片可能压缩的过小,导致图片质量过差而无法查看。 我们可以给一个下界和一个上界,当图片小于下界时,我们重新压缩将其调大。

还有一个情况需要注意,有可能我们给了一个范围。但是最终无论我们怎么调整,图片都无法压缩到指定大小,这个时候就会陷入死循环。所以,我们需要统计压缩次数。当压缩的次数大于某个值时,我们就停止压缩。

其他问题

目前这个图片压缩算法基本可以投入使用了,但是在实际使用中,博主还发现了一些其他问题。

图片发生旋转

一些安卓机型,比如红米等,拍出的照片直接预览没有问题。但是上传后,再通过 img 标签或者背景图显示时,就会发现照片发生了旋转,本来竖着的照片横着呈现了。

这个问题比较主流的方法就是通过 exif-js 这个库来获取图片的方向,然后再进行处理。
在这里插入图片描述
打个比方:如果我们预览的图片的方向值为 6,那么我们只需要将其顺时针旋转 90 度即可正常查看。

关于旋转的内容可以查看 http://sylvana.net/jpegcrop/exif_orientation.html

透明区域变黑

如果图片有透明区域,上传后,由于压缩成了 jpeg 格式,所以透明区域消失了,默认呈现黑色。有个比较简单的处理方法,我们在将图片画到 canvas 之前,先将 canvas 的背景设为白色。但是如果原图非透明部分的边缘是白色,那样效果会很糟糕。

ctx.fillStyle = '#fff'
ctx.fillRect(0, 0, canvas.width, canvas.height)

IOS 兼容性问题

在部分 IOS 机型上,如果图片的分辨率过大,那么将无法转化成 Base64 数据。


参考:

  • Exif Orientation Tag
  • 图片格式那么多,哪种更适合你?

http://chatgpt.dhexx.cn/article/EYHZTrNL.shtml

相关文章

html图片无损压缩,有损压缩和无损压缩的区别是什么

区别:无损压缩是可以完全还原的;而有损压缩还原后不能和原来文件一样,是有一定损耗的。有损压缩两种的基本机制(有损变换编解码和预测编解码);而无损压缩原理有行程编码、霍夫曼编码和算术编码等。 本教程操作环境:windows10系统、thinkpad t480电脑。 有损压缩是利用了人…

6种比较好的“在线图片无损压缩工具”+PDF转换工具

目录 第一&#xff1a;Tinypng 第二&#xff1a;Compressor.io 第三&#xff1a;Kraken.io 第四&#xff1a;Giftofspeed 第五&#xff1a; OptimiZilla 第六&#xff1a;Imagerecycle 第七&#xff1a;&#xff08;PDF转换工具&#xff09;ToePub 第一&#xff1a;Tiny…

Android图片无损压缩库ImageCompress使用(最新优化OOM问题的1.0.10版本)

转载请注明出处&#xff1a;https://blog.csdn.net/hh7181521/article/details/81014839 图片压缩一直是Android的一个痛点&#xff0c;相比较于iOS的效果&#xff0c;Android的压缩简直可以用惨不忍睹来形容&#xff01; 开源库ImageCompress是基于libjpeg-turbo来进行图片的压…

无损压缩技术

一个视频经过有损压缩完成后&#xff0c;会再进行无损压缩&#xff0c;让视频进一步变小。 DCT 变化 DCT 变化的过程又叫量化&#xff0c;经过有损压缩后&#xff0c;数据分散在二维图标中的各个节点上&#xff0c;经过 DCT 变化&#xff0c;会将所有数据集中到某一块。 转换…

使用Tuimgs在线工具对图片无损压缩教程

正文: 图片体积压缩是很多设计师都不陌生的事情&#xff0c;在多数情况下&#xff0c;即使你并不是一位设计师&#xff0c;但是你也可能需要使用到图片压缩&#xff0c;就例如:我们需要存储一张图片到手机的时候&#xff0c;但是这张图片的体积实在是太大了&#xff0c;会占用…

图片无损压缩工具(报名照片压缩至30Kb以下

% 我姐报名的需求 &#xff08;文中图片都是直接拖入博客编辑器的&#xff09; 素材选用&#xff1a;邓紫棋照片一张 【排雷】吐槽一下知乎高赞答案提供的方法&#xff1a;右键编辑&#xff0c;重新设置大小 198赞&#xff0c;评论一堆尬吹 用此方法将上图压缩至27.7KB结果如…

图片无损压缩软件哪个好用:试试完全免费的JPG-C 图片批量修整压缩减肥工具吧 | 最新jpg批量修整工具下载

JPG-C是一款完全免费且非常实用的电脑批量图片无损压缩软件&#xff0c;基于Net开发&#xff0c;也叫jpg批量修整工具&#xff0c;第一个版本始于2013年4月份&#xff0c;区区几百KB的超迷你身材但是能力确不容小觑&#xff0c;用户可以根据自定的压缩级别以单个或批量的方式对…

win7查询计算机硬盘sn,电脑win7系统查询硬盘序列号的方法

近些年来,科学技术的迅猛发展,极大地推动了计算机网络技术的进步,计算机网络的日益发展,使人们的生活也发生着巨大的变化。下面是学习啦小编为大家整理的关于电脑win7系统查询硬盘序列号的方法&#xff0c;一起来看看吧! 电脑win7系统查询硬盘序列号的方法 方法一&#xff1a;我…

win7/win10下装centos7双系统

工具及材料 1、一台PC 2、一个U盘,8G以上 3、需要的文件:CentOS-7-x86_64-DVD-1511.iso(其他版本也可以如 1611) 4、需要的软件:USBwriter(优于UltraISO) 第一步:从windows硬盘分区中再划一个用于装centos的分区 我的电脑->右键->管理,然后 到这里…

计算机系统盘涨满怎么办,win7系统C盘突然就满了怎么办?win7系统盘突然爆满了三种解决方法图文教程...

最近,有win7系统用户反映,电脑的系统盘突然就爆满了,这该怎么办呢?大家可以释放C盘空间,下面脚本之家的小编和大家讲解win7系统盘突然就满了三种解决方法,一起来看看吧。 win7系统盘突然就满了解决方法一:移走虚拟内存文件到非系统盘 大家都知道,为了加快系统的运行,W…

win7修复计算机 正常启动不了怎么办,Win7系统无法正常启动怎么办

Win7系统开机自检之后&#xff0c;始终无法进入系统&#xff0c;重启也无法解决。不要紧&#xff0c;下面是学习啦小编给大家整理的一些有关Win7系统无法正常启动的解决方法&#xff0c;希望对大家有帮助! Win7系统无法正常启动的解决方法一 1.我们可以开机F8进入系统高级选项菜…

Win7系统文件缺失怎么修复

Win7系统文件缺失怎么修复&#xff1f;有些朋友的电脑经常会提示缺失某些文件&#xff0c;比较严重的连系统桌面都进不去&#xff0c;大家想要了解有什么方法可以修复系统文件缺失的问题。今天俺就给大家分享几个恢复系统文件的方法。 一、进入安全模式 在重启或者电脑开机的时…

关于Win7系统“U盘拒绝访问”解决问题

问题场景&#xff1a; 提示&#xff1a;U盘无损坏&#xff0c;不能显示容量&#xff0c;并且提示拒绝访问 在属性中有很明显的不能够更改操作组和用户组的权限&#xff0c;无法添加用户权限名称 解决方案&#xff1a; 1、在系统桌面上&#xff0c;按组合键&#xff08;win键…

服务器虚拟机怎么安装win7系统教程,虚拟机怎么安装win7系统 虚拟机安装win7系统教程...

有的时候我们会想要通过虚拟机来进行安装win7系统&#xff0c;但如果不会安装该怎么办呢?这个问题好解决&#xff0c;不是大事&#xff0c;你们就继续往下看&#xff0c;看看虚拟机怎么安装win7系统的吧。 虚拟机安装win7系统教程方法 1.打开运行虚拟机&#xff0c;然后点击界…

用u盘给服务器装win7系统,用U盘装系统装Win7图文教程

win7之家小编认为用U盘装系统装Win7是一个非常好用的方法,那么我们到底要如何利用u盘装win7系统呢?其实方法非常简单,下面小编就来教大家u盘装win7系统,一起来看看下面这篇用U盘装系统装Win7图文教程吧,保证不会让大家失望的哦。 工具/原料: U盘一个 方法/步骤: 首先我们…

电脑W7系统怎样安装鸿蒙系统,真正纯净版的win7系统

真正纯净版的win7系统很多的小伙伴都非常的想要下载,很想试试这个版本的win7吧,win是目前兼容性比较高的一个系统,也是很多用户选择的一个版本,喜欢的用户赶紧下载试试吧。 真正纯净版的win7系统特色 人性化的设计师资源保证所有的非系统都保存在磁盘上,不会再次丢失。 系…

联想微型计算机能装光盘,联想光盘装系统win7安装方法

随着正版Windows7用户的不断增加&#xff0c;许多用户开始尝试自己对重新安装或是帮朋友安装Windows7的系统。但是Windows 7的安装过程&#xff0c;可能会因为许多外在原因导致安装不能够继续进行。 有朋友想要自己安装win7操作系统&#xff0c;这里简单的整理下方便需要的朋友…

win7查看计算机硬盘序列号,win7系统通过cmd查看硬盘序列号的操作方法

很多小伙伴都遇到过对win7系统通过cmd查看硬盘序列号进行设置的困惑吧&#xff0c;一些朋友看过网上对win7系统通过cmd查看硬盘序列号设置的零散处理方法&#xff0c;并没有完完全全明白win7系统通过cmd查看硬盘序列号是如何设置的&#xff0c;今天小编准备了简单的操作办法&am…

戴尔服务器改win7系统教程,新款戴尔win10改win7系统教程

现在市面上大部分戴尔dell电脑都预装了win10系统&#xff0c;但是有些用户可能用不习惯&#xff0c;就想要将其改为win7系统&#xff0c;却不知道要怎么操作&#xff0c;那么戴尔dell预装win10怎么改win7系统呢&#xff1f;下面给大家讲解一下具体的操作步骤吧。 1、开机&#…

win7计算机系统减肥,Win7系统如何减少C盘空间,给系统盘瘦身

当系统使用久了就会发现&#xff0c;C盘(系统盘)在不断地变大&#xff0c;有些用户的系统盘甚至出现了红色的条条&#xff0c;提示即将爆满。C盘可用空间过少&#xff0c;在一定程度上会影响系统的运行速度&#xff0c;当然若系统盘内存变无时&#xff0c;还会导致系统无法开机…