单页面实现图片压缩
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>canvas图片压缩算法</title> </head> <body> <!-- 图片上传到服务端之前进行图片压缩 --> <input type="file" id="file" /> <script> const ACCEPT = ['image/jpg', 'image/png', 'image/jpeg'] const upload = document.getElementById("file") const MAXSIZE = 1024 * 1024 * 10; const MAXTIP = "20MB" function convertImageToBase64(file, callback) { // 创建一个FileReader对象,它允许Web应用程序异步读取存储在计算机上的文件 // 也就是file对象 let reader = new FileReader() // 添加一个load事件,load会在加载完毕之后进行触发(也就是readAsDataURL读取完毕后触发) reader.addEventListener("load", function (e) { const base64Image = e.target.result // 相当于reader.result 获取文件的Base64 // 回收内存 callback && callback(base64Image) // 调用callback压缩 reader = null }) // readAsDataURL方法读取指定的file或blob对象 reader.readAsDataURL(file) } // 压缩算法函数 /* 1.首先拿到了base64的图片字符串 2.创建一个image对象,获得原始图片的宽度和高度 3.对原始图片的宽度和高度进行压缩达到符合条件(第一次压缩-从尺寸压缩) 4.调用canvasAPI进行绘制新的图片 5.绘制成功之后调用canvasAPI进行绘制(canvasAPI支持压缩-二次压缩-从质量压缩) 6.得到压缩后的base64 */ function compress(base64Image, callback) { let maxW = 1024; let maxH = 1024; const image = new Image() // 创建image对象 相当于创建a标签 image.addEventListener('load', function (e) { // image加载完成后就会触发 也就是src加载后 let radio; // 压缩比例 let needCompress = false; // 是否需要压缩 // image.naturalWidth/naturalHeight H5新属性 获取源生图片的宽高 console.log('原生图片大小:',image.src.length) console.log('原生图片宽高:',image.naturalWidth,image.naturalHeight) if (image.naturalWidth > maxW) { needCompress = true; // 获得压缩宽高过后的大小(保证等比例缩放) radio = image.naturalWidth / maxW maxH = image.naturalHeight / radio } // 同样宽度压缩之后 还要看压缩后的高度是否满足 不满足则继续压缩宽高 if (image.naturalHeight > maxH) { needCompress = true; radio = image.naturalHeight / maxH maxW = image.naturalWidth / radio } // 不需要压缩 if (!needCompress) { maxW = image.naturalWidth; maxH = image.naturalHeight; } // 第一次压缩完成 // 接下来使用canvas进行质量压缩 const canvas = document.createElement('canvas') canvas.height = maxH; canvas.width = maxW; canvas.setAttribute("id", "_compress_") // visibility hidden 需要创建的canvas隐藏 而不是不渲染DOM canvas.style.visibility = 'hidden' document.body.appendChild(canvas) const ctx = canvas.getContext('2d') // canvas.clearRect() 方法清空给定矩形内的指定像素。(x1,y1,width,height) // 防止重新上传覆盖 ctx.clearRect(0, 0, maxW, maxH) // canvas.drawImage() 方法在画布(canvas)上绘制图像、画布或视频。 // 传入 视频/图片对象 起始点x 起始点y 绘制宽 绘制高 ctx.drawImage(image, 0, 0, maxW, maxH) // 接来下就是压缩canvas 通过API将canvas输出成base64格式 /** * @HTMLCanvasElement.toDataURL(type, encoderOptions); 注意调用这是 Canvas的Dom对象而非ctx * @param {String} 可以使用 type 参数其类型, type 图片格式,默认为image/png,图片的分辨率为96dpi。 * @param {Number} encoderOptions 可选 指定图片格式是image/jpeg或image/webp的情况下,可以从0到1区间内进行选择图片的质量(1原质量)。如果超出取值方位,使用默认0.92 * @returns {dataURL} 方法返回一个包含图片展示的 data URI (Base64) */ console.log(base64Image, 'base64Image') /* 注意这里是Canvas DOM 节点 而非canvas对象*/ const compressImage = canvas.toDataURL('image/jpeg', 0.8) // 通常压缩是0.8-0.9 callback && callback(compressImage); // 压缩完成进行后台传输逻辑 // 压缩完的图片就已经保存在内存(compressImage)中了 // 接下来移除canvas元素 调用DOM.remove() canvas.remove() // 需要的上传预览的话可以单独建一个new image进行预览 const _image = new Image() _image.src = compressImage; document.body.appendChild(_image) //计算压缩比 使用src(base)的长度就可以对比了 console.log(`压缩比${image.src.length/_image.src.length}`) }) image.src = base64Image; // document.body.appendChild(image) // 挂载 } // 绑定上传图片的change事件 // mock上传后台逻辑 function uploadToServer(cImage) { console.log("上传后台",cImage.length) } upload.addEventListener('change', function (e) { // 获取上传的文件 解构Arry 拿到第一个元素 console.log('e:',e) const [file] = e.target.files; if (!file) { return; } const { type: fileType, size: fileSize } = file; // 上传校验逻辑 if (!ACCEPT.includes(fileType)) { alert(`不支持[${fileType}]类型`) upload.value = "" return } if (fileSize > MAXSIZE) { alert(`文件超${MAXTIP}`) upload.value = "" return } // 压缩图片 需要转成base64进行压缩 convertImageToBase64(file, (base64Image) => { compress(base64Image, uploadToServer) }) }) </script> </body> </html>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通