基于顶级编解码器实现纯前端高效图片压缩

笔记哥 / 05-21 / 2点赞 / 0评论 / 987阅读
![image](https://cdn.res.knowhub.vip/c/2505/21/c33628a6.jpg?G1YAAER17rxga90i%2bp14TA0KCTQDElkElRLW615rzFPk%2bYJKy9dfW4%2f54S%2b19RCq0wqESldH8gTMqNjAZMUPc%2bye3xE%3d) Google的Squoosh项目封装了MozJPEG、libwebp、rust Oxipng等顶级图像编解码器,但是使用依赖于Node.js,而jSquash项目对此进行了二次封装,将底层编解码器以WebAssembly的形式提供,实现不依赖node的纯前端的压缩方式。 经测试可以将10mb的png压缩为500kb的jpg,肉眼画质无损失,95%的优化。 文末会给出我的使用代码。github项目地址也在文末。 ## jSquash jSquash是一个轻量级、基于WebAssembly(WASM)的图像处理库,旨在为浏览器和V8运行时(如Cloudflare Workers)提供高效的图像编解码能力。其设计来源于Squoosh,强调浏览器兼容性和模块化。特点: - 专注于浏览器和Web Worker,适合前端环境。 - 模块化设计:支持多种图像格式(如@jSquash/jpeg、@jSquash/png、@jSquash/webp等),按需引入。 - 无动态代码执行:可在严格环境中运行,如Cloudflare Workers。 而WASM优势: - 接近原生代码的执行速度,适合计算密集型任务如图像压缩。 - 无需依赖特定运行时环境,浏览器即可运行。 - WASM运行在沙箱环境中,适合严格的执行环境。 比如@jSquash/jpeg使用MozJPEG库进行JPEG图像的编解码,@jSquash/webp基于libwebp实现WebP格式支持。通过Emscripten编译为WASM模块,并通过JavaScript接口暴露给开发者。 jSquash采用ES Module(ESM)格式,所有模块(如@jSquash/avif、@jSquash/png)均为独立包,可按需引入,减少了打包体积,适合Vite、Webpack这些前端构建工具。 jSquash也支持通过CDN直接使用(大陆环境不稳定): ```javascript import { decode } from 'https://unpkg.com/@jsquash/jpeg?module'; ``` ## 处理流程: 1. 解码(Decode):将输入的图像二进制数据(ArrayBuffer)解码为原始RGB图像数据(ImageData)。例如,@jSquash/jpeg的decode方法将JPEG文件转换为ImageData对象。 2. 处理(可选):支持调整大小(@jSquash/resize)或优化(如@jSquash/oxipng)。调整大小支持多种算法(如Lanczos3、Magic Kernel)。 3. 编码(Encode):将处理后的ImageData编码为目标格式的二进制数据。例如,@jSquash/webp的encode方法生成WebP格式的ArrayBuffer。 jSquash的WASM模块通常由生成的胶水代码(glue code)自动初始化,支持大多数Web打包工具 ## 图像格式支持 每种格式由独立的模块实现: - @jSquash/avif - 使用 libavif 库的 AVIF 图像编码器和解码器 - @jSquash/jpeg - 使用 MozJPEG 库的 JPEG 图像编码器和解码器 - @jSquash/jxl - 使用 libjxl 库的 JPEG XL 图像编码器和解码器 - @jSquash/oxipng - 使用 Oxipng 的 PNG 图像优化器 - @jSquash/png - 使用 rust PNG crate 的 PNG 图像编解码器 - @jSquash/qoi - 使用官方库的 "相当好的图像格式" 编解码器 - @jSquash/resize - 使用 rust resize、hqx 和 magic-kernel 库的图像缩放工具。支持降缩和升缩。 - @jSquash/webp - 使用 libwebp 的 WebP 图像编码器和解码器 完整见官方readme,按需npm install即可(install的是@jsquash/xx而不是@jSquash/xx,要小写)。 ## 代码 只包括图像处理的核心部分,图像是上传还是直接读取本地自行处理。 注意每个模块的使用方式是不一样的,比如oxipng的输入类型为arrayBuffer,而MozJpeg为imageData,具体需要查看每个封装模块的readme。 **jpeg、webp:** 这里encode可以传入配置quality,默认为75(0-100),具体库配置不同。png配置为level(1-6) ```js import { decode } from '@jsquash/jpeg'; import { encode } from '@jsquash/webp'; async function convertToWebP(url) { try { const response = await fetch(url); const imageBuffer = await response.arrayBuffer(); // 或者直接处理blob对象 // file.arrayBuffer() const imageData = await decode(imageBuffer); // 这里encode可以传入配置{ quality },默认为75(0-100),具体库配置不同。png配置为level(1-6) const webpBuffer = await encode(imageData); const blob = new Blob([webpBuffer], { type: 'image/webp' }); return URL.createObjectURL(blob); } catch (error) { console.error('转换失败:', error); } } ``` **rust oxipng:** 这里用canvas解码,因为这个库没提供解码函数。 ```js const bitmap = await createImageBitmap(file); const canvas = new OffscreenCanvas(bitmap.width, bitmap.height); const ctx = canvas.getContext('2d'); ctx.drawImage(bitmap, 0, 0); const imageData = ctx.getImageData(0, 0, bitmap.width, bitmap.height); bitmap.close(); return imageData; ``` *编码:* ```js import {optimise as encodePNG} from '@jsquash/oxipng'; let buffer = await encodePNG(buffer, { level: 2 }), return new Blob([buffer], {type: 'image/png'}); ``` 没有明确要求(比如png保留透明通道)的话建议输出格式转为jpeg,能大幅度减小体积。 ## 注意 vite优化的时候要避开这些库,不能影响wasm,否则会导致无法生效: ```js export default defineConfig({ optimizeDeps: { exclude: ["@jsquash/jpeg", "@jsquash/oxipng", "@jsquash/webp"] } }) ``` ## 参考资料 - jSquash GitHub仓库:https://github.com/jamsinclair/jSquash - Squoosh 项目:https://github.com/GoogleChromeLabs/squoosh - WebAssembly 文档:https://webassembly.org/ - Emscripten 文档:https://emscripten.org/