背景:
最近项目需求需要使用到base64的图片格式,需要对用户上传的图片压缩处理,所以打算自己动手写一个图片选择、压缩、转换的方法(需要压缩到指定大小)。
思路:
- 调用 FileReader的 reader.readAsDataURL(img)方法, reader.readAsDataURL 读取图片信息。
- 在reader.onload的事件中,新建Image对象,并将reader.result的值赋给img.src,在img.onload中判断尺寸大小,并且根据 exif-js 判断图片角度是否需要校正,再通过canvs 将图片压缩至合适尺寸。
既然思路已经有了,那么接下来就可以动手去尝试操作了…
-
第一步先引入项目所需要的依赖文件,因为写的时候我是本地直接原生js做的所以使用的 cdn的方式引入。
<script src="https://cdn.jsdelivr.net/npm/exif-js"></script>
-
创建项目,编写基础代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>图片压缩转换base64</title> <style> .ui_button { background: #0066cc; color: #fff; padding: 10px 20px; margin: 10px; display: block; width: 100px; text-align: center; cursor: pointer; border-radius: 5px; } img { height: 100px; width: 100px; display: none; } </style> </head> <body> <label class="ui_button" for="xFile" label>上传文件</label> <form> <input type="file" accept="image/*" onchange="fileChange(this)" style="position: absolute; clip: rect(0 0 0 0)" /> </form> <img src="" alt="" id="showImg" /> </body> <script src="https://cdn.jsdelivr.net/npm/exif-js"></script> </html>
-
开始写获取图片、处理图片方法 (大概的思路以及逻辑如下)
- 首先获取到按钮以及 input 并且给按钮添加点击事件,同时触发 input 的点击事件达到选择文件的目的。
- 定义 imageCompression 文件压缩方法,方法需要接收三个参数( file 文件 、处理完成的 callBack 回调、 params {绘制 canvas 需要的参数、图片压缩的大小、返回文件的格式} )
- 因为需要使用到 FileReader 方法 所以在处理之前先判断浏览器是否支持 不支持就直接返回文件并打印错误信息,如果浏览器支持,那就使用 FileReader 读取传入的文件
- 在 FileReader.onload 方法中进行 img 创建,
- 监听 img.onload 事件 在图片加载完成后进行 canvas 绘制
- 绘制完之后我们就需要根据 orientation 值判断图片是否需要旋转
- 在将图片旋转正常后,使用递归将突破压缩至设置的大小
function imageCompression( file, callBack = () => {}, params = { width: 1000, LIMIT_SIZE: 0.5 * 1024 * 2014, isBase64: true, } ) { let compressCount = 0; const { width, LIMIT_SIZE, isBase64 } = params; const orientation = EXIF.getTag(file, "Orientation") || 1; // 图片压缩出错函数 const errorFn = (e) => { console.error("图片压缩出问题了", e); callBack(file); }; if (!window.FileReader) { console.error("浏览器不支持 window.FileReader 方法哦"); callBack(file); } else { try { let reader = new FileReader(); reader.readAsDataURL(file); reader.onload = function () { let img = new Image(); img.src = reader.result; img.onload = function () { try { const canvas = document.createElement("canvas"); const context = canvas.getContext("2d"); const canvasWidth = width; const canvasHeight = canvasWidth / (img.width / img.height); canvas.width = canvasWidth; canvas.height = canvasHeight; let angle = 0; switch (orientation) { case 1: break; case 6: // 逆时针90°,需要顺时针旋转90° angle = (90 * Math.PI) / 180; canvas.width = canvasHeight; canvas.height = canvasWidth; context.rotate(angle); context.translate(0, -canvas.width); break; case 8: // 顺时针90°,需要顺时针旋转270° angle = (270 * Math.PI) / 180; canvas.width = canvasHeight; canvas.height = canvasWidth; context.rotate(angle); context.translate(-canvas.height, 0); break; case 3: // 顺时针180°,需要顺时针旋转180° angle = (180 * Math.PI) / 180; canvas.width = canvasWidth; canvas.height = canvasHeight; context.rotate(angle); context.translate(-canvas.width, -canvas.height); break; default: break; } context.drawImage(img, 0, 0, canvasWidth, canvasHeight); context.setTransform(1, 0, 0, 1, 0, 0); canvas.toBlob( (blob) => { if (blob.size > LIMIT_SIZE) { compressCount += 1; imageCompression(blob, callBack); } else { compressCount = 0; if (isBase64) { callBack(canvas.toDataURL( 'image/png', 1 )); } else { callBack(blob); } } }, "image/jpeg", 0.9 - compressCount * 0.1 ); } catch (error) { errorFn(error); } }; }; reader.onerror = (error) => { errorFn(error); }; } catch (error) { errorFn(error); } } }
-
示例代码,复制至页面可以直接查看效果
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>图片压缩转换base64</title> <style> .ui_button { background: #0066cc; color: #fff; padding: 10px 20px; margin: 10px; display: block; width: 100px; text-align: center; cursor: pointer; border-radius: 5px; } img { height: 100px; width: 100px; display: none; } </style> </head> <body> <label class="ui_button" for="xFile" label>上传文件</label> <form> <input type="file" accept="image/*" onchange="fileChange(this)" style="position: absolute; clip: rect(0 0 0 0)" /> </form> <img src="" alt="" id="showImg" /> </body> <script src="https://cdn.jsdelivr.net/npm/exif-js"></script> <script> let btn = document.getElementsByClassName("ui_button")[0]; let fileInput = document.getElementsByTagName("input")[0]; btn.onclick = function () { fileInput.click(); }; function fileChange(e) { let file = e.files[0]; // 判断图片大小是否需要压缩 (不能大于4M) if (file.size > 4 * 1024 * 1024) { console.log("图片过大不能上传哦"); } else { imageCompression(file, getBase64); } } function imageCompression( file, callBack = () => {}, params = { width: 1000, LIMIT_SIZE: 0.5 * 1024 * 2014, isBase64: true, } ) { let compressCount = 0; const { width, LIMIT_SIZE, isBase64 } = params; const orientation = EXIF.getTag(file, "Orientation") || 1; // 图片压缩出错函数 const errorFn = (e) => { console.error("图片压缩出问题了", e); callBack(file); }; if (!window.FileReader) { console.error("浏览器不支持 window.FileReader 方法哦"); callBack(file); } else { try { let reader = new FileReader(); reader.readAsDataURL(file); reader.onload = function () { let img = new Image(); img.src = reader.result; img.onload = function () { try { const canvas = document.createElement("canvas"); const context = canvas.getContext("2d"); const canvasWidth = width; const canvasHeight = canvasWidth / (img.width / img.height); canvas.width = canvasWidth; canvas.height = canvasHeight; let angle = 0; switch (orientation) { case 1: break; case 6: // 逆时针90°,需要顺时针旋转90° angle = (90 * Math.PI) / 180; canvas.width = canvasHeight; canvas.height = canvasWidth; context.rotate(angle); context.translate(0, -canvas.width); break; case 8: // 顺时针90°,需要顺时针旋转270° angle = (270 * Math.PI) / 180; canvas.width = canvasHeight; canvas.height = canvasWidth; context.rotate(angle); context.translate(-canvas.height, 0); break; case 3: // 顺时针180°,需要顺时针旋转180° angle = (180 * Math.PI) / 180; canvas.width = canvasWidth; canvas.height = canvasHeight; context.rotate(angle); context.translate(-canvas.width, -canvas.height); break; default: break; } context.drawImage(img, 0, 0, canvasWidth, canvasHeight); context.setTransform(1, 0, 0, 1, 0, 0); canvas.toBlob( (blob) => { if (blob.size > LIMIT_SIZE) { compressCount += 1; imageCompression(blob, callBack); } else { compressCount = 0; if (isBase64) { callBack(canvas.toDataURL( 'image/png', 1 )); } else { callBack(blob); } } }, "image/jpeg", 0.9 - compressCount * 0.1 ); } catch (error) { errorFn(error); } }; }; reader.onerror = (error) => { errorFn(error); }; } catch (error) { errorFn(error); } } } // 获取base64 function getBase64(base64) { let showImg = document.getElementById("showImg"); showImg.src = base64; showImg.style.display = "block"; console.log(base64); } </script> </html>
-
效果图