图片转换base64或blob(对图片旋转进行校正)并在页面显示

背景:

最近项目需求需要使用到base64的图片格式,需要对用户上传的图片压缩处理,所以打算自己动手写一个图片选择、压缩、转换的方法(需要压缩到指定大小)。

思路:

  1. 调用 FileReader的 reader.readAsDataURL(img)方法, reader.readAsDataURL 读取图片信息。
  2. 在reader.onload的事件中,新建Image对象,并将reader.result的值赋给img.src,在img.onload中判断尺寸大小,并且根据 exif-js 判断图片角度是否需要校正,再通过canvs 将图片压缩至合适尺寸。

既然思路已经有了,那么接下来就可以动手去尝试操作了…

  1. 第一步先引入项目所需要的依赖文件,因为写的时候我是本地直接原生js做的所以使用的 cdn的方式引入。

    <script src="https://cdn.jsdelivr.net/npm/exif-js"></script>
    
  2. 创建项目,编写基础代码

    <!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>
    
  3. 开始写获取图片、处理图片方法 (大概的思路以及逻辑如下)

    1. 首先获取到按钮以及 input 并且给按钮添加点击事件,同时触发 input 的点击事件达到选择文件的目的。
    2. 定义 imageCompression 文件压缩方法,方法需要接收三个参数( file 文件 、处理完成的 callBack 回调、 params {绘制 canvas 需要的参数、图片压缩的大小、返回文件的格式} )
    3. 因为需要使用到 FileReader 方法 所以在处理之前先判断浏览器是否支持 不支持就直接返回文件并打印错误信息,如果浏览器支持,那就使用 FileReader 读取传入的文件
    4. 在 FileReader.onload 方法中进行 img 创建,
    5. 监听 img.onload 事件 在图片加载完成后进行 canvas 绘制
    6. 绘制完之后我们就需要根据 orientation 值判断图片是否需要旋转
    7. 在将图片旋转正常后,使用递归将突破压缩至设置的大小
    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);
            }
          }
        }
    
  4. 示例代码,复制至页面可以直接查看效果

    <!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>
    
    
  5. 效果图

    效果图片

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值