当前位置: 首页 > news >正文

实现图片自动压缩算法,canvas压缩图片方法

背景:

在使用某些支持webgl的图形库(eg:PIXI.js,fabric.js)场景中,如果加载的纹理超过webgl可处理的最大纹理限制,会导致渲染的纹理缺失,甚至无法显示。

方案

实现图片自动压缩算法,自动获取 webgl 支持的最大纹理大小,设置一个压缩比率,循环压缩图片的像素,直到小于最大纹理限制。
返回 canvas,方便第三方库继续处理,如果需要 image,可自行调用canvas方法转换成image。

注意:如果不需要像素处理,删除处理像素相关的代码即可。

vim imageHelp.ts

/**** @param imgStr image base64 | url* @param ratio 压缩比率* @returns 压缩后的canvas对象,获取image 使用 canvas.convertToBlob()*/
export async function compressImage(options: { imgStr: string; ratio?: number; negate?: 0 | 1 }) {const { imgStr, ratio = 0.5, negate = 0 } = optionsconst isInverted = negate == 1 // 底色是否反黑色// 2. 添加错误处理if (!imgStr) throw new Error('Invalid image source')if (ratio <= 0 || ratio > 1) throw new Error('Invalid compression ratio')try {const img = await loadImage(imgStr)const maxTextureSize = getMaxTextureSize()// 5. 优化尺寸计算逻辑const { width, height } = calculateTargetSize(img, ratio, maxTextureSize)// 6. 使用 OffscreenCanvas 提升性能const { canvas, ctx } = createCanvasContext(width, height)ctx.drawImage(img, 0, 0, width, height)// 7. 添加 Worker 终止逻辑防止内存泄漏const worker = new CanvasWorker()const cleanup = () => worker.terminate()return await new Promise<{ canvas: OffscreenCanvas; width: number; height: number }>((resolve, reject) => {worker.onmessage = (e) => {try {// imageData.data.buffer 所有权已转移,无法更新数据 imageData.data.buffer//  重新构建 ImageData 对象const updatedImageData = new ImageData(new Uint8ClampedArray(e.data.buffer),canvas.width,canvas.height)// 将修改后的图像数据放回画布ctx.putImageData(updatedImageData, 0, 0)cleanup()if (width > maxTextureSize || height > maxTextureSize) {// 压缩后的图像需要缩放,保持原图像的视觉大小ctx.scale(1 / ratio, 1 / ratio)}resolve({canvas,width,height,})} catch (error) {cleanup()reject(error)}}worker.onerror = (error) => {cleanup()reject(error)}// 8. 优化数据传输const imageData = ctx.getImageData(0, 0, width, height)// 传递图像数据给worker,第二个参数是一个Transferable对象,可以将数据从一个线程传递到另一个线程,而不是复制worker.postMessage({buffer: imageData.data.buffer,targetColor: isInverted ? [0, 0, 0, 255] : [255, 255, 255, 255],tolerance: 50, // 添加颜色容差参数},[imageData.data.buffer])})} catch (error) {throw new Error(`Image processing failed: ${error?.message}`)}
}
function getMaxTextureSize(): number {const gl = document.createElement('canvas').getContext('webgl')return gl ? gl.getParameter(gl.MAX_TEXTURE_SIZE) : 4096 // 默认值
}function calculateTargetSize(img: { width: number; height: number },ratio: number,maxSize: number
) {let width = img.widthlet height = img.height// 压缩图像像素while (width > maxSize || height > maxSize) {width *= ratioheight *= ratio}return {width,height,}
}function createCanvasContext(width: number, height: number) {const canvas = new OffscreenCanvas(width, height)canvas.width = widthcanvas.height = heightreturn {canvas,ctx: canvas.getContext('2d')!,}
}

vim canvas.worker.ts

self.onmessage = (event) => {const { buffer, targetColor, isInverted } = event.data// 转换为 Uint8ClampedArray 进行像素级别的处理const data = new Uint8ClampedArray(buffer);// 遍历每个像素for(let i = 0; i < data.length; i += 4) {const r = data[i];     // 红色通道const g = data[i + 1]; // 绿色通道const b = data[i + 2]; // 蓝色通道// 检查该像素是否为需要删除的颜色if(r === targetColor[0] && g === targetColor[1] && b === targetColor[2]) {// 将黑色像素设置为透明data[i + 3] = 0; // Alpha通道设置为0}// 反转颜色if(isInverted) {data[i] = 255 - data[i]data[i + 1] = 255 - data[i + 1]data[i + 2] = 255 - data[i + 2]}}self.postMessage(data)
}
http://www.xdnf.cn/news/546031.html

相关文章:

  • Java 单元测试框架比较:JUnit、TestNG 哪个更适合你?
  • pycharm无法正常调试问题
  • 【蓝桥杯真题精讲】第 16 届 Python A 组(省赛)
  • 使用 GitHub Pages 部署单页面应用教程
  • ISCC 2025决赛 wp
  • 《深入解析 Django ORM 复杂查询优化:如何提升 SQL 执行效率》
  • 多通道振弦式数据采集仪MCU安装指南
  • SymPy | 获取表达式自由变量方法与因式分解
  • 王树森推荐系统公开课 排序04:视频播放建模
  • 代码随想录算法训练营第60期第四十二天打卡
  • 多类型RFID电子标签定制 助力行业精准化管理
  • Matplotlib imsave() 方法详解
  • 使用 ARCore 和 Kotlin 开发 Android 增强现实应用入门指南
  • STM32之定时器
  • 深入浅出IIC协议 - 从总线原理到FPGA实战开发 -- 第三篇:Verilog实现I2C Master核
  • vscode 常用调试
  • 进程间通信(IPC): POSIX 消息队列
  • 【解决】SSH 远程失败之路由配置问题
  • CNBC专访CertiK联创顾荣辉:从形式化验证到AI赋能,持续拓展Web3.0信任边界
  • 2025 高级 Java 面试宝典 [特殊字符](先放题目后放答案)
  • Unity3D仿星露谷物语开发46之种植/砍伐橡树
  • 使用注解动态映射:根据实体List列表动态生成Excel文件
  • 音视频之H.265/HEVC速率控制
  • P1152 欢乐的跳
  • 第三个小程序动工:一款结合ai的菜谱小程序
  • LeetCode 1345. 跳跃游戏 IV(困难)
  • 基于Django开发校园食堂美食推荐系统
  • 如何查看与设置电脑静态IP地址:完整指南
  • Vue 3.0 中 Teleport 详解
  • Redisson分布式集合原理及应用