首页前端开发JavaScript如何用 JavaScript 制作一个好用又好玩的图片压缩工具

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

时间2023-03-24 17:27:46发布访客分类JavaScript浏览1801
导读:前言现在的设备发达了,图片拍下来动辄 5MB 10MB,单反相机歘欻欻一张经能达到 40MB,手机的内部储存也跟着很大,随便一个手机都 100G 。但对于我来讲,反而不舒服。一张照片,占用 5mb 10mb 的空间真的合适吗?不舒服不是因为...

前言

现在的设备发达了,图片拍下来动辄 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 。

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 毫秒就基本完成了.......

/* 在画布画图 */
 // ctx.drawImage(domImg, 0, 0, domImg_w, domImg_h);
    
ctx.drawImage(图的 img >
    , 起画点左坐标, 起画点上坐标, 落笔点右坐标, 落笔点下坐标);
    

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

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

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

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

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 就是文件体积了。

代码如下。

 /* 处理后的大小 */
  r_imgSize = parseInt(out_base64.length / 1024 * 0.75) + "kb";
    

结语

至此,程序就完成了。

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

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

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

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!

javascript微信上传图片压缩原理

若转载请注明出处: 如何用 JavaScript 制作一个好用又好玩的图片压缩工具
本文地址: https://pptw.com/jishu/205.html
尝试使用 JavaScript 写脚本来辅助记忆单词(也是一种单词记忆方式的构想) 开发者必备的 7 款效率提升工具!

游客 回复需填写必要信息