Chrome插件:切图压缩工具

分享
程序员 2024-9-24 16:57:50 47 0 来自 中国
媒介

在前端项目开辟中,尤其是运动项目,大量使用未压缩的图片必将会影响页面打开速度,低落用户体验。因此,我们须要对下载的切图举行压缩处理惩罚。常见的图片压缩工具有 TinyPNG 和 PP鸭,但这两款软件是收费的,而且不支持定制化。使用这些软件压缩图片的过程更是复杂繁琐,假如有一款工具可以在下载切图时就资助我们压缩图片,或直接提供压缩后的图片地点,那将会大大进步当前的工作服从。本文将先容实现如许一个切图压缩工具的关键技能点。
获取原图片

常用的计划稿软件有两个,蓝湖和 Figma。这里用蓝湖作例简述怎样获取原图。起首蓝湖是一个网页,须要用 Chrome 打开。这时间就必须祭出 F12 这个大杀器了,直接调试源码来定位下载利用的走向。会发现在终极下载图片的代码块中有以下一段。
这里的 t 变量就是一个 a 标签,通过调用 dispatchEvent 方法来触发 click 变乱举行文件下载。知道了下载方式,下一步就是怎样去拦截它。直接上原型大法,把 dispatchEvent 方法给重写以便拿到 a 标签实例,来获取要下载的文件信息。
const originDispatchEvent = EventTarget.prototype.dispatchEvent;Object.defineProperty(HTMLAnchorElement.prototype, 'dispatchEvent', {    writable: true,    configurable: true,    enumerable: true,    value: function (event) {        const nodeName = this.nodeName;        const href = this.href;        const filename = this.download;        if (nodeName === 'A' && filename && /^blob:/.test(href)) {              console.warn(filename, href);            return false;        }        return originDispatchEvent.apply(this, [event]);    }});把以上代码输入到控制台,点击下载图片就会看到如许的日记输出。至此便能拿到下载的切图数据了。
2.png 插件注入脚本

有了可以拦截下载数据的脚本,那怎样把它使用起来,实现自动注入呢?这就必须使用到 Chrome 插件了。可以使用其提供的 scripting_api 实现。
function inject(eventName) {    const originDispatchEvent: Function = EventTarget.prototype.dispatchEvent;    Object.defineProperty(HTMLAnchorElement.prototype, 'dispatchEvent', {        writable: true,        configurable: true,        enumerable: true,        value: function (event) {            const nodeName = this.nodeName;            const href = this.href;            const filename = this.download;            if (nodeName === 'A' && filename && /^blob:/.test(href)) {                // ...                return false;            }            return originDispatchEvent.apply(this, [event]);        }    });}// 在网页革新后,注入拦截脚本chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {    if (tab.status === 'complete' && /^https?/.test(tab.url || '')) {          // 在指定的tab页下实行函数        chrome.scripting.executeScript({            func: inject,            target: { tabId },            world: 'MAIN',            args: [SITE_DOWN_IMAGE]        }).catch((err) => {              console.error(err);        });    }});须要留意的是注入时设置 world: 'MAIN' 是必须的,否则注入的脚本将在隔离环境中运行,就无法访问页面上的 JS 环境了。
拦截到下载的切图数据后,可以通过 postMessage 发送给插件的内容脚本。下面就是在内容脚本中来实现图片压缩的能力。
压缩能力实现

那要怎样实现一个可以在网页中使用的压缩工具呢?先看下现有的压缩工具 TinyPNG 和 PP鸭,一个是网页一个是当地 App。当地 App 肯定是不可了,TinyPNG 理论上是没题目的,其有提供压缩接口。但由于它是收费的且须要上传,也没法直接使用。颠末查询资料,相识到业界开源的 PNG 图片压缩工具有 pngquant,advpng,oxipng 等,颠末实验,选取 pngquant 和 advpng 来举行 PNG 图片的压缩。

  • pngquant
这是一个命令行工具,用于 PNG 图片的有损压缩。其转换后的图片可以低落高达 70% 的巨细,而且保存了完备的 alpha 透明度,天生的图像与全部 Web 欣赏器和利用体系兼容。具有以下特点:

  • 使用矢量量化算法的组合,天生高质量的调色板
  • 与标准的 Floyd-Steinberg 相比,独特的自适应抖动算法为图像增长了更少的噪音
引用自 pngquant 官网简介


  • advpng
这是一个 PNG 图片的无损压缩库,通过移除 PNG 图片中的辅助块,整合 IDAT 数据块和使用 7zip Deflate 举行更高比例的压缩实现。
那怎样把他们用到 Web 上呢?那必须要是 WebAssembly 呀。起首把这两个库整合到一起,使用 Emscripten 编译成 Wasm,提供接口以便前端调用。这个过程中你大概会碰到这些题目:

  • 内存文件转换
由于 pngquant 是一个命令行工具,其压缩利用都是基于磁盘文件读写的,但是在 WebAssembly 传参时须要的是字节数组,都是在内存中的。就须要对源码举行肯定的改造。重要是将 fopen 更换为 fmemopen 或 open_memstream 实现在内存数据中举行 FILE 的利用。如读取文件修改。
FILE *infile;if ((infile = fopen(filename, "rb")) == NULL) {  fprintf(stderr, "  error: cannot open %s for reading\n", filename);  return READ_ERROR;}// 修改为如下if ((infile = fmemopen(file_buffer, file_size, "rb")) == NULL) {  return READ_ERROR;}

  • 内置 libpng 和 zlib 包
在通过 emcmake 构建时,会发现无法使用体系安装的 libpng 和 zlib 动态库。须要将这两个库的源码下载到项目中,一起举行编译。
...file(GLOB PNG_SOURCE libpng/*.c)file(GLOB ZLIB_SOURCE zlib/*.c)...add_library(${PROJECT_NAME} STATIC pngquant.c rwpng.c ${PNG_SOURCE} ${ZLIB_SOURCE} ${QUA_SOURCE} ${ADVPNG_SOURCE})在内容脚本中,就能直接通过这个 Wasm 模块来实现图片的压缩了。在蓝湖中,内容脚本直接使用 Wasm 时并不会有任何阻力,但是放到 figma 中就会被内容安全计谋所克制。由于 figma 在相应时就直接设置了内容安全计谋,这时间就要借助 Chrome 插件提供的 sandbox 能力,通过在页面中新增 iFrame 页面来实现。
到此该工具的根本能力就都已经实现了,可以把要下载的切图数据拿到,颠末内容脚本压缩后再下载。为了更方便使用切图,下一步就是要把压缩后的图片上传到 CDN 并提供 URL 来举行复制。
切图上传

到这里就简单起来了,以上已经可以在内容脚本中获取到压缩后的切图数据,下面就是把它上传到一个符合的图床平台了。如使用七牛云,唯一大概会碰到的题目是上传接口不支持跨域。这是就须要使用 Chrome 插件的主机权限,把用的的接口设置上。
// manifest.json{  ...  "host_permissions": [    "https://rsf-z0.qiniuapi.com/*",    "https://*.qiniu.com/*"  ]  ...}关于怎样直接在前端上传文件到七牛,可以参考七牛开辟者文档。这里唯一贫苦的大概是上传凭据的天生,七牛官方保举的是在服务端天生凭据,前端 SDK 就没直接提供天生方法。可以参考天生算法在前端实现。
import { urlSafeBase64Encode } from 'qiniu-js';import HmacSHA1 from 'crypto-js/hmac-sha1';import encBase64 from 'crypto-js/enc-base64';function getUploadToken(bucket, secretKey) {  const returnBody = {    key: '$(key)',    hash: '$(etag)',    name: '$(fname)',    size: '$(fsize)',    width: '$(imageInfo.width)',    height: '$(imageInfo.height)'  };  const putPolicy = JSON.stringify({    scope: bucket,    deadline,    returnBody: JSON.stringify(returnBody)  });  const encodedPolicy = urlSafeBase64Encode(putPolicy);  const hash = HmacSHA1(encodedPolicy, secretKey);  const encodedSigned = hash.toString(encBase64);  return this.accessKey + ':' + safe64(encodedSigned) + ':' + encodedPolicy;}切图管理

假如你使用的 CDN 有现成的列表接口,那直接调用就行。但如七牛并没有提供好用的列表接口,为了管理上传的图片列表,就须要在前端保存切图列表,这时你就要选择一个符合的存储。你能想到的大概会有 localStorage,Cookie,IndexedDB,大概还会有 chrome.stroage。思量到切图列表的性子,其须要较大的存储空间而且要能方便的举行分页查询,那如许使用 IndexedDB 作为存储将会是一个更好的选择。
下面就来用 IndexedDB 实现切图管理的功能,方便查看已上传的切图列表。起首明确两个接口定义:

  • 新增已上传的切图
  • 分页查询已上传的切图
// 存储的数据布局interface ImageEntry {  name: string  width: number  height: number  size: number  cdnUrl: string  uploadTime: number}// 接口定义interface IImageDB {  add(...images: ImageEntry[]): Promise<void>  findPage(page: number, limit: number): Promise<ImageEntry>}借助开源库 dexie 实现起来也很简单
import Dexie from 'dexie';export default class ImageDB {    constructor(name = 'zimagedb') {        let db = new Dexie(name);        db.version(1).stores({            images: '++_id,name,cdnUrl'        });        this.db = db;    }    async add(...datas) {        for (const item of datas) {            await this.db.images.add(item);        }    }    async findPage(page = 0, limit = 10) {        const offset = page * limit;        return this.db.images.limit(limit).offset(offset).toArray();    }}为了能让 IndexedDB 储存唯一,你应该把它放在 Chrome 扩展的背景页内,再通过消息通讯在内容脚本中使用。
// 如background.js监听消息chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {  if (message.type === 'getImages') {    db.findPage(message.page).then(images => {      sendResponse({ success: true, images });    }).catch(err => {      ...    });    return true;  }});// 在content.js中查询切图列表function getImages(page = 0) {  return new Promise((resolve, reject) => {    chrome.runtime.sendMessage({type: 'getImages', page}, response => {      if (response?.success) {        resolve(response.images);      } else {        reject(...);      }    });  });}总结

以上简述了该切图压缩工具的一些关键技能点。涉及到有 Chrome 扩展开辟,JavaScript 原型的使用,WebAssembly 开辟,IndexedDB 存储等。尤其是 WebAssembly 和 IndexedDB 这让本需依靠服务端才气实现的一些功能在前端也能很好的完成,给前端带来了更多的大概性。
参考资料


  • https://developer.chrome.com/docs/extensions/mv3/
  • https://github.com/kornelski/pngquant
  • https://www.advancemame.it/doc-advpng.html
  • https://webassembly.org/
  • https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
  • https://developer.qiniu.com/kodo/1283/javascript
  • https://dexie.org/
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-18 22:37, Processed in 0.178575 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表