前端 JS 压缩图片的思路(附源码)

            <div id="content_views" class="htmledit_views">
                <h2><a name="t0"></a>前言</h2> 

相信大家都做过图片上传相关的功能,在图片上传的过程中,不知道大家有没有考虑过文件体积的问题,如果我们直接将原图片上传,可以图片体积比较大,一是上传速度较慢,二是前端进行渲染时速度也比较慢,比较影响客户的体验感。所以在不影响清晰度的情况下,前端可以在上传前对图片的大小体积进行压缩,压缩到一个比较合适的大小进行上传,本文就带大家一起来看看前端 JS 如何实现图片压缩,有需要的小伙伴抓紧收藏一下吧!

原理(必看)

省流:主要使用 canvas的 drawImage 方法先绘制为 canvas 图像,再结合 toDataURL 转化为DataURl 进行存储图片链接。

drawImage简单介绍

Canvas 2D API 中的 CanvasRenderingContext2D.drawImage() 方法提供了多种在画布Canvas)上绘制图像的方式。

用法如下:

CanvasRenderingContext2D.drawImage() - Web API 接口参考 | MDN (mozilla.org)

语法如下:


 
 
  1. drawImage(image, dx, dy);
  2. drawImage(image, dx, dy, dWidth, dHeight);
  3. drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);

 我们使用第二种进行绘制,参数含义如下:

image:绘制到上下文的元素。

dximage 的左上角在目标画布上 X 轴坐标。

dy:image 的左上角在目标画布上 Y 轴坐标。

dWidth:image 在目标画布上绘制的宽度。允许对绘制的 image 进行缩放。如果不说明,在绘制时 image 宽度不会缩放。

dHeight:image 在目标画布上绘制的高度。允许对绘制的 image 进行缩放。如果不说明,在绘制时 image 高度不会缩放。

简单示例

注意:如果随意的修改图像的尺寸,会导致图像失真,我们可以先获取到图像资源的原始尺寸,然后进行等比缩放,意思就是当我们确定设置宽度之后,高度要进行等比调整。公式就是交叉相乘积相等。

        // 如果宽度设置为 500, 那么高度也应该进行等比缩放
        // naturalWidth         =>  500
        // naturalHeight        =>  X
        // naturalWidth * X     =   naturalHeight * 500

       //  计算得出高度
        X =   naturalHeight * 500 / naturalWidth


 
 
  1. var can = document. querySelector( 'canvas')
  2. var context = can. getContext( '2d')
  3. var imgDom = new Image();
  4. imgDom. src = './img.jpg';
  5. imgDom. onload = function ( ) {
  6. // 注意:图像绘制时,必须保证资源已经加载完成
  7. console. log( '图片的原始宽度', imgDom. naturalWidth);
  8. console. log( '图片的原始高度', imgDom. naturalHeight);
  9. context. drawImage(
  10. imgDom,
  11. 0, 0,
  12. 500, imgDom. naturalHeight * 500 / imgDom. naturalWidth
  13. );
  14. }

toDataURL简单介绍

我们将图片绘制到 canvas 之后,还需要将 canvas 转化为 Data URl,转化为 DataURl 之后可以显示到我们的屏幕上面,也可以存放到后端服务器,使用 canvas 所提供的 toDataURL 实例方法即可。

​ 官方解释:HTMLCanvasElement.toDataURL() 方法返回一个包含图片展示的 data URI ​

HTMLCanvasElement.toDataURL() - Web API 接口参考 | MDN (mozilla.org)

语法:canvas.toDataURL(type, encoderOptions);

type(可选):图片格式,默认为 image/png

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

简单示例 

 
 
  1. // 获取压缩后的图片数据
  2. can. width = imgDom. naturalWidth
  3. can. height = imgDom. naturalHeight
  4. const compressedData = can. toDataURL( 'image/jpeg', 0.6) // 可调整质量参数
  5. console. log( 'compressedData: ', compressedData)

转化后 DataURL 结果如下 

实现

先奉上全部代码,方便大家看,下面进行解释!


 
 
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>图片压缩上传 </title>
  5. <meta charset="UTF-8">
  6. </head>
  7. <body>
  8. <input type="file" id="fileInput" accept="image/*">
  9. <button onclick="compressAndUpload()">压缩并上传图片 </button>
  10. <canvas id="canvas" style="display: none;"> </canvas>
  11. <script>
  12. function compressAndUpload( ) {
  13. const fileInput = document. getElementById( 'fileInput');
  14. const file = fileInput. files[ 0];
  15. if (!file) {
  16. alert( '请先选择要上传的图片');
  17. return;
  18. }
  19. const reader = new FileReader();
  20. reader. onload = function ( ) {
  21. const img = new Image();
  22. img. src = reader. result;
  23. img. onload = function ( ) {
  24. const canvas = document. getElementById( 'canvas');
  25. const ctx = canvas. getContext( '2d');
  26. const maxWidth = 800; // 设置最大宽度为800像素
  27. let width = img. width;
  28. let height = img. height;
  29. // 判断是否需要缩放
  30. if (width > maxWidth) {
  31. height *= maxWidth / width;
  32. width = maxWidth;
  33. }
  34. // 设置 canvas 的宽高
  35. canvas. width = width;
  36. canvas. height = height;
  37. // 将图片绘制到 canvas 上
  38. ctx. drawImage(img, 0, 0, width, height);
  39. // 获取压缩后的图片数据
  40. const compressedData = canvas. toDataURL( 'image/jpeg', 0.7); // 可调整质量参数
  41. // 创建一个新的压缩后的 File 对象
  42. const compressedFile = dataURItoBlob(compressedData, file. type);
  43. compressedFile. lastModifiedDate = file. lastModifiedDate;
  44. compressedFile. name = file. name;
  45. // 上传压缩后的图片文件
  46. uploadImage(compressedFile);
  47. };
  48. };
  49. reader. readAsDataURL(file);
  50. }
  51. function dataURItoBlob( dataURI, mimeType) {
  52. const binary = atob(dataURI. split( ',')[ 1]);
  53. const array = [];
  54. for ( let i = 0; i < binary. length; i++) {
  55. array. push(binary. charCodeAt(i));
  56. }
  57. return new Blob([ new Uint8Array(array)], { type: mimeType });
  58. }
  59. function uploadImage( compressedFile) {
  60. const formData = new FormData();
  61. formData. append( 'image', compressedFile);
  62. fetch( '/upload', {
  63. method: 'POST',
  64. body: formData
  65. })
  66. . then( response => {
  67. if (response. ok) {
  68. console. log( '图片上传成功');
  69. } else {
  70. console. error( '图片上传失败');
  71. }
  72. })
  73. . catch( error => {
  74. console. error( '发生错误:', error);
  75. });
  76. }
  77. </script>
  78. </body>
  79. </html>

我们看一下压缩前后体积对比,压缩前550290,压缩后31523,缩小了十几倍,这个压缩还是很明显的。

 首先我们看这三行代码

我们先初始化一个 reader 是一个 FileReader 对象的实例 

reader.readAsDataURL(file),这行代码的作用是将选择的文件读取为 Data URI 格式的字符串。

当执行 reader.readAsDataURL(file) 时,会发生以下几件事情:

  1. FileReader 对象开始异步读取 file 中的数据。
  2. 一旦读取完成,FileReader 的 onload 事件将被触发。
  3. 读取的结果将存储在 FileReader 对象的 result 属性中,格式为 Data URI 字符串。

看一下 FileReader 对象的 result  的打印结果,为 Data URL格式

最终我们将读取出来的 Data URI 字符串赋值给 Image 的 src,也就是下面这行代码,然后等待 img 加载完毕开始对 img 进行压缩,具体怎么压缩上面已经简单演示过。


 
 
  1. const img = new Image()
  2. img. src = reader. result
  3. img. onload = function ( ) {}

接下来我们设置了一个最大宽度为800,然后判断当前图片宽度是否大于该值,如果大于进行缩放计算,小于就不进行等比缩放计算。最后将计算出的值使用 drawImage 绘制到 canvas 上面。


 
 
  1. const maxWidth = 800; // 设置最大宽度为800像素
  2. let width = img. width;
  3. let height = img. height;
  4. // 判断是否需要缩放
  5. if (width > maxWidth) {
  6. height *= maxWidth / width;
  7. width = maxWidth;
  8. }
  9. // 设置 canvas 的宽高
  10. canvas. width = width;
  11. canvas. height = height;
  12. // 将图片绘制到 canvas 上
  13. ctx. drawImage(img, 0, 0, width, height);

现在我们将 canvas 转化成Data URI 字符串,canvas.toDataURL('image/jpeg', 0.7),这行代码的作用是将 canvas 上绘制的图像数据导出为 JPEG 格式的 Data URI 字符串,并设置图像质量为 0.7。


 
 
  1. // 获取压缩后的图片数据
  2. const compressedData = canvas. toDataURL( 'image/jpeg', 0.7); // 可调整

我们看一下 compressedData,是一个Data URI 字符串,其实到这里就可以了,我们可以将 Data URI 传到后端进行存储起来,也可以转化为文件格式进行存储,我这里选择使用文件格式进行存储,不需要的可以不使用下面的方式。

接下来就是创建一个新的压缩后的 File 对象


 
 
  1. const compressedFile = dataURItoBlob(compressedData, file. type)
  2. compressedFile. lastModifiedDate = file. lastModifiedDate
  3. compressedFile. name = file. name
  4. function dataURItoBlob( dataURI, mimeType) {
  5. const binary = atob(dataURI. split( ',')[ 1])
  6. const array = []
  7. for ( let i = 0; i < binary. length; i++) {
  8. array. push(binary. charCodeAt(i))
  9. }
  10. return new Blob([ new Uint8Array(array)], { type: mimeType })
  11. }

新的文件对象

最后直接使用 FormData 进行上传即可,这一块就不说了。

总结

前端实现图片压缩主要是利用的 canvas 来实现,实现思路为使用 canvas 的 drawImage 方法先绘制为 canvas 图像,再结合 toDataURL 转化为 DataURl 进行存储图片链接以及压缩图像质量。在toDataURL 中可以调整图像质量,需要注意的是我们在压缩图像时要注意等宽高缩放,否则会导致图像出现失真的情况。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值