前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何用 JavaScript 制作一个好用又好玩的图片压缩工具

如何用 JavaScript 制作一个好用又好玩的图片压缩工具

作者头像
独元殇
发布2023-03-21 17:22:24
9440
发布2023-03-21 17:22:24
举报
文章被收录于专栏:独元殇的文章

前言

现在的设备发达了,图片拍下来动辄 5MB 10MB,单反相机歘欻欻一张经能达到 40MB,手机的内部储存也跟着很大,随便一个手机都 100G 。

但对于我来讲,反而不舒服。一张照片,占用 5mb 10mb 的空间真的合适吗?不舒服不是因为居安思危,杞人忧天,觉得体积大未来会把地球憋爆炸,而是觉得一张图片可以比文字占得空间大点,但 5mb 10mb 着实不配它占。它不配。

文字,真的太节约体积了,余华花很久写个《活着》,全书保存成 GBK 编码也不过占用 500kb,一张图片,用那么多体积确实不合算。大概看个样子就行了,只有极少数像素会经过中枢神经前额叶意识区域的处理。

另一方面,体积大了,在本地还行,硬盘上千个 GB,不碍事,但在服务器上,网页上,体积小的需求还是挺大,要不然谷歌也不会研制 webp 什么格式,emlog、七牛、阿里云也不会刻意考虑为图片压缩尺寸等措施。

所以,在适当情况下,如果图片能压缩到一定程度,确实是网络从业者的福音。尤其对我这种,以前特别害怕在网站上传图片,因为即使是 CDN ,也是花钱的,当然钱是小事,5 块 10 块够我用好几年,主要是如果一张图片 5M 的话,到时候七牛云倒闭,迁移资源时,工程量可大了!如果一张图片就几十 KB 的话,加起来那么多图也就几十 mB ,几百 mb,简洁可爱!(就像七八年前的 微信 一样,可惜物是人非今不如昔)

当然,图片压缩从来不是卡脖子的技术,微信、各种 APP 、PS 都能灵活的压缩图片,甚至 AI。即使懒得下载,点击,直接打开万能的互联网浏览器,搜索在线压缩图片,也能,不过还是效率不够行,不够方便。

中文互联网真的,处处都是注册、、、而且没啥技术含量,纯粹抄袭的别人的东西。为了更自由,我决定自己做一个,使用 JavaScript。而且使用的都是浏览器自带的 API ,什么 canvas API ,blob API....

功夫不负有心人,花了一傍晚的时间,我做出来了。

最终成果

就是这个链接。 https://www.ccgxk.com/249.html 【导航】---【小工具】---【图片超级压缩】。

(压缩我的头像)

(压缩上面那张截图「压缩我的头像」)

由图可见,这种压缩效率还是很厉害的,虽然原图才 几十几百 kb,但如果原图是 5M 10M 也是可以压缩到 20 --- 30 kb 的。说实话 20 kb 的图,虽然模糊点,但足够把很多信息传递明白了。

其实,这个主要是有文字,模糊起来会看不清。如果是「风景图片」的话,越模糊,越有意境哈哈。

代码的话,还是花了很多功夫的。不一段一段讲了,先直接上最终的 html + javascript 。

代码语言:javascript
复制
<style>
  .c {
    margin-top: 20px;
    margin-inline: auto;
  }
  i {
      color:#c9c9c9;
  }
  .e2 {
      background: aliceblue;
      border: 0px;
  }
  .markdown {
    text-align: inherit;
}
</style>
<div class="c" >
  复制图片,在下面蓝框中粘贴,会自动按照下面设置的规则来压缩图片体积
<br><br>
<i>注意,直接鼠标复制处理后的图片,其体积会增长一部分(因浏览器本身特性),获取真实压缩图片应单击「下载最终结果」。</i>
  <br><br>

  <textarea class="e2" style="width: 100%;" rows="2" id="output"></textarea>

<br><br>
<button id="img_download" onclick="base64ToFile(out_base64, 'download.jpeg')">下载最终结果</button>(<span id="img_size"></span>):
  <p id="imga">
    <img id="testimg" src="" alt="" />
  </p>
</div>

  <fieldset style="width: 230px">
    <legend>压缩规则</legend>
    最大宽度 (px)<input type="number" id="in_maxwidth" onchange="re_config()" value="400" /><br><br>
  质量 (1 - 10)<input type="range" name="points" min="1" max="100" id="in_quality" onchange="re_config()" value="50" /><span id="in_q_msg">5</span><br><br>
  是否黑白化<input type="checkbox" id="in_balck" onchange="re_config()" checked /><br>
  </fieldset>

<script>

/* 配置区 */ 
let drawWidth = 400; // 统一宽度值
let imgQuality = 0.5; // 质量
let is_balck = true; // 黑白
// ----------------
const c=document.createElement("canvas");
const ctx=c.getContext("2d");

let domImg;
let s_imgSize;
let r_imgSize;
let base64data;
let out_base64;

/* 程序入口 */ 
function drawimg(base64data){
  creatDomImg(base64data);
  setTimeout(function(){canvdraw()}, 1000);
}

/* 把图片弄到 domImg 中 */
function creatDomImg(base64data){
  s_imgSize = parseInt(base64data.length / 1024 * 0.75) + "kb";
  domImg = document.createElement("img");
  domImg.src = base64data;
}

function canvdraw(){

  /* 计算画布的宽高值 */
  let scale = domImg.height / domImg.width;
  let domImg_w = (domImg.width > drawWidth) ? drawWidth         : domImg.width;
  let domImg_h = (domImg.width > drawWidth) ? drawWidth * scale : domImg.height;

  /* 画布生成 */
  c.width = domImg_w;
  c.height = domImg_h;

  /* 在画布画图 */
  ctx.drawImage(domImg, 0, 0, domImg_w, domImg_h);

  /* 黑白化 */
  if(is_balck){
    const imgArrData = ctx.getImageData(0, 0, domImg_w, domImg_h);
    for (let i = 0; i < imgArrData.data.length; i += 4) {
        let r = imgArrData.data[i],
            g = imgArrData.data[i + 1],
            b = imgArrData.data[i + 2];
        const avg = (r + g + b) / 3;
        imgArrData.data[i] = imgArrData.data[i + 1] = imgArrData.data[i + 2] = avg;
    }
    ctx.putImageData(imgArrData, 0, 0);
  }

  /* 图片展示 */
  out_base64 = c.toDataURL('image/jpeg', imgQuality);
  testImg = document.getElementById("testimg");
  testImg.src = out_base64;

  /* 处理后的大小 */
  r_imgSize = parseInt(out_base64.length / 1024 * 0.75) + "kb";
  img_size.innerHTML = s_imgSize + " -> " + r_imgSize;
}

/* 粘贴事件后:获取粘贴图片,把 base64 数据扔给 drawimg() */
document.getElementById("output").addEventListener("paste", function (e) {
  if ( !(e.clipboardData && e.clipboardData.items) ) return

  var pasteData = e.clipboardData || window.clipboardData
  pasteAnalyseResult = new Array

  for(var i = 0; i < pasteData.items.length; i++) {
      var item = pasteData.items[i]

      if((item.kind == "file") && (item.type.match('^image/'))){
          let imgData = item.getAsFile();
          if (imgData.size === 0) return;

          let reader = new FileReader();
          reader.readAsDataURL(imgData);
          reader.onload = function(){
              base64data = this.result;
              drawimg(base64data);  // 获得图片 base64 数据,开始处理
          }
          break;
      };
  }
}, false);

/* 用户在界面自定义配置 */
function re_config(){
  drawWidth = in_maxwidth.value;
  imgQuality = Math.floor(in_quality.value) / 100;
  in_q_msg.innerHTML = (imgQuality * 10).toString().match(/^\d+(?:\.\d{0,1})?/);
  is_balck = in_balck.checked;

  if(typeof base64data === 'undefined') return
  img_size.innerHTML = "处理中...";
  drawimg(base64data);
}

/* 下载 */
function base64ToFile(base,fileName) {
  console.log(base)
  if(typeof base === 'undefined') return

  const arr = base.split(',');
  const mime = arr[0].match(/:(.*?);/)[1];
  const bstr = atob(arr[1]);
  let n = bstr.length;
  const u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  if (window.navigator.msSaveBlob) {
    // for ie 10 and later
    try {
      const blobObject = new Blob([u8arr], { type: mime });
      window.navigator.msSaveBlob(blobObject, 'aaa.xls');
    } catch (e) {
      console.log(e);
    }
  } else {
    const url = window.URL.createObjectURL(new Blob([u8arr], { type: mime }));
    const link = document.createElement('a');
    link.href = url;
    link.setAttribute('download', fileName);
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link); // 下载完成移除元素
    window.URL.revokeObjectURL(url); // 释放掉blob对象
  }
}
</script>

代码讲解

获取粘贴板图片 base64

获取剪切板的程序,是固定写法,那么一大群 addEventListener ,复制粘贴就行。

最终获取剪切板里图片的 base64 ,放到 drawimg(base64data); 里。

虚拟 < img > 放图片

然后就要过流水线了。先创建一个虚拟的 DOM < img > 放内存里,(创建实体 也行,但没必要)。然后图片 src 就是这个 base64,这样,就有了这个 img 元素了。

为什么创建 img ,因为目前我只知道 < canvas > 画照片的办法,就是得有 < img > 才行。然后按照 api 方式,画图就行。

间隔 1 s 向 < canvas > 放图片

不过很可惜,这两个不能同时进行,创建了 < img >,还得等一段时间,可能这是单进程吧,所以我写了个延迟函数 setTimeout(function(){canvdraw()}, 1000); ,1s 后再画。当然视觉效果就好像是,机器处理了 1 s 才放出来,其实不是,机器不到 10 毫秒就基本完成了.......

代码语言:javascript
复制
/* 在画布画图 */
 // ctx.drawImage(domImg, 0, 0, domImg_w, domImg_h);
ctx.drawImage(图的< img >, 起画点左坐标, 起画点上坐标, 落笔点右坐标, 落笔点下坐标);

这些照着手册写就行。关键是下面 3 点。

  1. 把图导出来,导出 base64 格式和独立图片文件。
  2. 图片黑白化(黑白图片也能为压缩助力)
  3. 如何计算图片的体积?

把图导出来,导出 base64 格式和独立图片文件。

第一点,canvas 转 base64 好说。现成的 API

代码语言:javascript
复制
out_base64 = c.toDataURL('image/jpeg', imgQuality);

这一句就行了,c 是那个 < canvas >,后面的第二个属性是质量,也就是导出 JPEG 的质量 0 -- 1 之间。压缩质量。比如 0.5。

至于下载独立文件,从网上复制粘贴了个 base64ToFile() 函数就好了。

图片黑白化

第二句图片黑白化。这个可让我真的见识到 JavaScript 是多么快的了。我注释 /* 黑白化 */ 下面的句子,把像素点从 < canvas > 一个个取出来,一个个加减乘除分析,就那个 for 循环。诸位可知,随便处理一张图片,这个句子在谈笑间能跑多少次吗?我还专门写了个 console.log ,我的头像,就跑了 20 多万次......

这,要是让我笔算,就我这计算力,一年都算不完。

原理也很简单,就是每个像素点都有 R G B 三个值,只要让 R G B 三个值相等,且等于它们三者的平均数就行。这就是黑白原理了。

如何计算图片体积

第三句,如何计算图片体积?其实已经能拿到图片的 base64 源码了,那离计算其体积就不远了。

根据 base64 的编码原理,六位二进制 101010 可以代表一个字母,但文本格式的 base 64 则需要 10101010 八位二进制才能表示。体积会增长 \frac{4}{3}

也就是说,6 kb 的内容,转成 base 64 会变成 8 kb,那直接把 base64 的长度 乘上 0.75 就是文件体积了。

代码如下。

代码语言:javascript
复制
 /* 处理后的大小 */
  r_imgSize = parseInt(out_base64.length / 1024 * 0.75) + "kb";

结语

至此,程序就完成了。

以后,写文章上传图片,就能上传很小的图片了,太爽了。

不过,以后,也可以再加个 自定义文件名 的功能。这样也便于整理。或者做成 emlog 插件.....

因为压缩完的图片,还得再进行下载才行,直接复制会失真...... 目前还没找到把独立文件放到剪切板里的办法,估计这样做也有安全问题。能下载就很不戳了。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-12-12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 最终成果
  • 代码讲解
    • 获取粘贴板图片 base64
      • 虚拟 < img > 放图片
        • 间隔 1 s 向 < canvas > 放图片
          • 把图导出来,导出 base64 格式和独立图片文件。
            • 图片黑白化
              • 如何计算图片体积
              • 结语
              相关产品与服务
              图片处理
              图片处理(Image Processing,IP)是由腾讯云数据万象提供的丰富的图片处理服务,广泛应用于腾讯内部各产品。支持对腾讯云对象存储 COS 或第三方源的图片进行处理,提供基础处理能力(图片裁剪、转格式、缩放、打水印等)、图片瘦身能力(Guetzli 压缩、AVIF 转码压缩)、盲水印版权保护能力,同时支持先进的图像 AI 功能(图像增强、图像标签、图像评分、图像修复、商品抠图等),满足多种业务场景下的图片处理需求。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档