整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

推荐!开箱即用的前端图片压缩方案(附源码)

推荐!开箱即用的前端图片压缩方案(附源码)

端实现图片压缩的背景

我们都知道在“寸土寸金”的互联网时代, 速度是第一竞争力, 虽然我们的5G发展已经摇摇领先, 但是也经不住用户在一个网页里传很多“巨无霸”图片, 最终导致的结果就是页面“龟速”打开......

那么作为技术人, 当然也有一堆的解决方案, 比如:

  • 压缩图片再上传
  • 将图片上传到图床, 利用图床压缩能力和CDN节点就近分发
  • 图片流式加载
  • 图片懒加载/ 预加载

当然聪明的小伙伴也会将上面的方案组合, 设计更优秀的图片“提速”方案.

今天不会和大家把所有方案都介绍一遍, 因为网上也有很多实践, 接下来会从前端技术提升的角度, 分享一下如何用原生 javascript, 实现从图片上传图片自定义压缩的完整方案. 大家可以把文章中介绍的方案直接用于自己的实际开发中, 或者基于它设计更棒的图片压缩方案.

实现图片压缩的方案

前端实现图片压缩无非就是在用户上传图片文件后, 将file转换成image对象, 然后再利用canvas 及其 api 将图片压缩成指定体积. 如下流程:

代码实现

首先我们先实现将file转换成image对象, 这里我们用到了FileReader API, 代码如下:

// 压缩前将file转换成img对象
function readImg(file:File) {
    return new Promise((resolve, reject)=> {
     const img=new Image()
     const reader=new FileReader()
     reader.onload=function(e:any) {
      img.src=e.target.result
     }
     reader.onerror=function(e) {
      reject(e)
     }
     reader.readAsDataURL(file)
     img.onload=function() {
      resolve(img)
     }
     img.onerror=function(e) {
      reject(e)
     }
    })
}

这里使用 promise 来设计生成图片数据的方法, 接下来我们看看核心的图片压缩源码:

/**
 * 压缩图片
 * @param img 被压缩的img对象
 * @param type 压缩后转换的文件类型
 * @param mx 触发压缩的图片最大宽度限制
 * @param mh 触发压缩的图片最大高度限制
 * @param quality 图片质量
 */
 function compressImg(img: any, type:string, mx: number, mh: number, quality:number=1) {
    return new Promise((resolve, reject)=> {
     const canvas=document.createElement('canvas')
     const context=canvas.getContext('2d')
     const { width: originWidth, height: originHeight }=img
     // 最大尺寸限制
     const maxWidth=mx
     const maxHeight=mh
     // 目标尺寸
     let targetWidth=originWidth
     let targetHeight=originHeight
     if (originWidth > maxWidth || originHeight > maxHeight) {
      if (originWidth / originHeight > 1) {
       // 宽图片
       targetWidth=maxWidth
       targetHeight=Math.round(maxWidth * (originHeight / originWidth))
      } else {
       // 高图片
       targetHeight=maxHeight
       targetWidth=Math.round(maxHeight * (originWidth / originHeight))
      }
     }
     canvas.width=targetWidth
     canvas.height=targetHeight
     context?.clearRect(0, 0, targetWidth, targetHeight)
     // 图片绘制
     context?.drawImage(img, 0, 0, targetWidth, targetHeight)
     canvas.toBlob(function(blob) {
      resolve(blob)
     }, type || 'image/png', quality) 
    })
}

这里通过控制 canvas的宽高, 以及对 canvastoBlob设置参数, 来实现自定义的图片压缩.

如果大家对代码有不理解的地方, 也可以在文末发表问题, 我会做出对应的解答.

更多前端提效方案

  • xijs 一款面向复杂业务场景的javascript工具库
  • react-slider-vertify 基于react实现的滑动验证码组件
  • react-cropper-pro 支持图片上传+裁切+压缩的组件
  • h5-dooring 在线H5页面制作工具
  • v6.Dooring 可视化大屏搭建平台


的选择是做或不做,但不做就永远不会有机会。

前言

监听剪切板粘贴事件,读取剪切板中的图片文件,转成base64通过img标签显示出来,此时可能会存在剪切板中图片过大,产生上传速度慢问题,接下来就跟大家分享下如何将base64图片进行压缩。先跟大家展示下最终实现的效果:

实现思路

  • 监听剪切板粘贴事件
  • 从事件回调中获取clipboardData中的image对象声明一个变量接收该对象
  • 使用reader.readAsDataURL方法加载clipboardData中的image对象
  • 在reader.onload回调中获取图片base64码
  • 创建Image对象,赋值图片base64码至当前对象的src属性
  • 调用Image对象的onload函数,获取图片宽高等信息
  • 声明canvas画布宽高分别为当前图片宽高除以缩放比例的值
  • 使用drawImage方法绘制当前图片

实现过程

本篇文章主要讲解剪切板图片压缩的实现,效果图中如何将剪切板的图片插入可编辑div以及如何发送,请移步我的另一篇文章:Vue解析剪切板图片并实现发送功能

  • 监听剪切板粘贴事件: 实现图片粘贴
    const that=this;
    document.body.addEventListener('paste', function (event) {
        that.$fullScreenLoading.show("读取图片中");
        // 获取当前输入框内的文字
        const oldText=that.$refs.msgInputContainer.textContent;
        // 读取图片
        let items=event.clipboardData && event.clipboardData.items;
        let file=null;
        if (items && items.length) {
            // 检索剪切板items
            for (let i=0; i < items.length; i++) {
                if (items[i].type.indexOf('image') !==-1) {
                    file=items[i].getAsFile();
                    break;
                }
            }
        }
        // 预览图片
        const reader=new FileReader();
        reader.onload=function(event) {
            // 图片内容
            const imgContent=event.target.result;
            // 创建img标签
            let img=document.createElement('img');//创建一个img
            // 获取当前base64图片信息,计算当前图片宽高以及压缩比例
            let imgObj=new Image();
            let imgWidth="";
            let imgHeight="";
            let scale=1;
            imgObj.src=imgContent;
            imgObj.onload=function() {
                // 计算img宽高
                if(this.width<400){
                    imgWidth=this.width;
                    imgHeight=this.height;
                }else{
                    // 输入框图片显示缩小10倍
                    imgWidth=this.width/10;
                    imgHeight=this.height/10;
                    // 图片宽度大于1920,图片压缩5倍
                    if(this.width>1920){
                        // 真实比例缩小5倍
                        scale=5;
                    }
                }
                // 设置可编辑div中图片宽高
                img.width=imgWidth;
                img.height=imgHeight;
                // 压缩图片,渲染页面
                that.compressPic(imgContent,scale,function (newBlob,newBase) {
                    // 删除可编辑div中的图片名称
                    that.$refs.msgInputContainer.textContent=oldText;
                    img.src=newBase; //设置链接
                    // 图片渲染
                    that.$refs.msgInputContainer.append(img);
                    that.$fullScreenLoading.hide();
                });
            };
        };
        reader.readAsDataURL(file);
    });
  • 实现base64图片压缩函数
       // 参数: base64地址,压缩比例,回调函数(返回压缩后图片的blob和base64)
    compressPic:function(base64, scale, callback){
        const that=this;
        let _img=new Image();
        _img.src=base64;
        _img.onload=function() {
            let _canvas=document.createElement("canvas");
            let w=this.width / scale;
            let h=this.height / scale;
            _canvas.setAttribute("width", w);
            _canvas.setAttribute("height", h);
            _canvas.getContext("2d").drawImage(this, 0, 0, w, h);
            let base64=_canvas.toDataURL("image/jpeg");
            // 当canvas对象的原型中没有toBlob方法的时候,手动添加该方法
            if (!HTMLCanvasElement.prototype.toBlob) {
                Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
                    value: function (callback, type, quality) {
                        let binStr=atob(this.toDataURL(type, quality).split(',')[1]),
                            len=binStr.length,
                            arr=new Uint8Array(len);
                        for (let i=0; i < len; i++) {
                            arr[i]=binStr.charCodeAt(i);
                        }
                        callback(new Blob([arr], {type: type || 'image/png'}));
                    }
                });
            }else{
                _canvas.toBlob(function(blob) {
                    if(blob.size > 1024*1024){
                        that.compressPic(base64, scale, callback);
                    }else{
                        callback(blob, base64);
                    }
                }, "image/jpeg");
            }
        }
    }

github: https://github.com/likaia/chat-system/blob/master/src/components/message-display.vue

作者:神奇的程序员K

转发链接:https://mp.weixin.qq.com/s/hADXM37cactAGFf2vduQJw

实现 HTML 压缩,可以使用 JavaScript 中的正则表达式来去除 HTML 中的空格和注释。以下是一个简单的 HTML 压缩函数:

function compressHTML(html) {
  // 去除注释
  html=html.replace(/<!--[\s\S]*?-->/g, "");
  // 去除多余空白
  html=html.replace(/\s+/g, " ");
  // 去除标签之间空格
  html=html.replace(/>\s+</g, "><");
  return html.trim();
}

该函数首先使用正则表达式去除 HTML 中的注释。然后,它使用另一个正则表达式去除 HTML 中的多余空格。最后,它使用另一个正则表达式去除标签之间的空格。

为了测试该函数,您可以创建一个 HTML 文件,并在其中添加一些冗余的空格和注释。例如:

<!DOCTYPE html>
<html>
  <head>
    <title>My Website</title>
  </head>
  <body>
    <!-- This is a comment -->
    <h1> Welcome to my website! </h1>
    <p> This is some text. </p>
  </body>
</html>

然后,您可以在Node.JS中使用以下代码将 HTML 文件加载为字符串并压缩它:

// 加载 HTML 文件
const fs=require("fs");
const html=fs.readFileSync("index.html", "utf8");
// 压缩 HTML
const compressedHtml=compressHTML(html);
console.log(compressedHtml);

输出是一个压缩后的 HTML 字符串,其中不包含注释或冗余空格。

或者直接在IE中测试,代码如下:

function compressHTML(html) {
  // 去除注释
  html=html.replace(/<!--[\s\S]*?-->/g, "");
  // 去除多余空白
  html=html.replace(/\s+/g, " ");
  // 去除标签之间空格
  html=html.replace(/>\s+</g, "><");
  return html.trim();
}
var html=`
<!DOCTYPE html>
<html>
  <head>
    <title>My Website</title>
  </head>
  <body>
    <!-- This is a comment -->
    <h1> Welcome to my website! </h1>
    <p> This is some text. </p>
  </body>
</html>
`;
console.log(compressHTML(html));

运行效果: