前端图片二进制

即:读《玩转前端二进制》测试及学习

先上图,是关于图片在前端变化的流程

在这里插入图片描述

一. 选择本地图片 -> 图片预览

由流程图中可知,图片预览通常有两种方式,第一种是从本地获取图片然后预览,第二种是从网络中下载图片预览。

  1. FileReader API

在支持FileReader API 的浏览器中,我们也可以利用改API方便实现 图片本地预览功能。

在这里插入图片描述

由上图可知,该API 兼容性较好,我们可以正常使用。上测试代码

  <div>
    <h3>选择本地图片 -> 图片预览</h3>
    <input type="file" accept="image/*" onchange="loadFile(event)">
    <img id="previewLocalImg" />
  </div>
  <script>
    const loadFile = function(event) {
      const reader = new FileReader()
      reader.onload = function() {
        const output = document.querySelector('#previewLocalImg')
        output.src = reader.result
      }
      console.log(event.target.files)
      reader.readAsDataURL(event.target.files[0])
    }
  </script>

在以上示例中,我们创建了一个FileReader对象并为该对象绑定onload对应的事件处理函数,然后调用FileReader对象的readAsDataURL()方法,把本地图片对应的File对象转换成Data URL。

当文件读取完成后,就会触发绑定的onload事件处理函数,在该处理函数内部会把获取到的Data URL 数据赋值给 img的src属性,从何实现图片本地预览。

在这里插入图片描述
在红色的框内,我们可以看到 src 属性值是 base64 编码之后的字符串。

而 这个属性值其实被称为 Data URL, 它由四个部分组成:前缀(data:)、指示数据类型的 MIME 类型、如果非文本则为可选的 base64 标记、数据本身:

data:[<mediatype>][;base64],<data>

mediatype 是个 MIME 类型的字符串,比如 “image/png” 表示 PNG 图像文件。如果被省略,则默认值为 text/plain;charset=US-ASCII。如果数据是文本类型,你可以直接将文本嵌入(根据文档类型,使用合适的实体字符或转义字符)。如果是二进制数据,你可以将数据进行 base64 编码之后再进行嵌入。

MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型,是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。

常见的 MIME 类型有:超文本标记语言文本 .html text/html、PNG 图像 .png image/png、普通文本 .txt text/plain 等。

在 Web 项目开发过程中,为了减少 HTTP 请求的数量,对应一些较小的图标,我们通常会考虑使用 Data URL 的形式内嵌到 HTML 或 CSS 文件中。「但需要注意的是:如果图片较大,图片的色彩层次比较丰富,则不适合使用这种方式,因为该图片经过 base64 编码后的字符串非常大,会明显增大 HTML 页面的大小,从而影响加载速度。」

需要注意的是 base64 只是一种数据编码方式,目的是为了保障数据的安全传输。但标准的 base64 编码无需额外的信息,即可以进行解码,是完全可逆的。因此在涉及传输私密数据时,并不能直接使用 base64 编码,而是要使用专门的对称或非对称加密算法。

二. 网络下载图片 -> 图片预览

除了可以从本地获取图片之外,我们也可以使用 fetch API 从网络上获取图片,然后在进行图片预览。当然对于网络上可正常访问的图片地址,我们可以直接把地址赋给 img 元素,并不需要通过 fetch API 绕一大圈。若在显示图片时,你需要对图片进行特殊处理,比如解密图片数据时,你就可以考虑在 Web Worker 中使用 fetch API 获取图片数据并进行解密操作。

简单起见,我们不考虑特殊的场景。首先我们先来看一下 fetch API 的兼容性:
在这里插入图片描述

然后我们使用 fetch API 从 网络 上获取图片,具体代码如下所示:

<div>
    <h3>获取远程图片预览示例</h3>
    <img id="previewInternetImg" />
  </div>
  <script>
    const image = document.querySelector("#previewInternetImg");
    fetch("https://wx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTLZpzZqvib4bzlMqE3wibILYcSn23OFBaMDs4fNNpib6BxV3dyZDzbtLJLq5BPQfCoTv4zicXeHMuxNnw/132")
      .then((response) => response.blob())
      .then((blob) => {
        const objectURL = URL.createObjectURL(blob);
        image.src = objectURL;
      });
  </script>

在以上示例中,我们通过 fetch API 从 网络 上获取图片,当请求成功后,把响应对象(Response)转换为 Blob 对象,然后使用 URL.createObjectURL 方法,创建 Object URL 并把它赋给 img 元素的 src 属性,从而实现图片的显示。

在这里插入图片描述
在图中可以看到,src 的属性值又是一串字符串,这个到底是什么呢??

blob:null/ea2557df-67ce-4cfb-9cf2-4de8b4527633

以上的特殊字符串,我们称之为 「Object URL」,相比前面介绍的 Data URL,它简洁很多。

Object URL 是一种伪协议,也被称为 Blob URL。它允许 Blob 或 File 对象用作图像,下载二进制数据链接等的 URL 源。在浏览器中,我们使用 URL.createObjectURL 方法来创建 Blob URL,该方法接收一个 Blob 对象,并为其创建一个唯一的 URL,其形式为 blob:<origin>/<uuid>

浏览器内部为每个通过 URL.createObjectURL 生成的 URL 存储了一个 「URL → Blob」 映射。因此,此类 URL 较短,但可以访问 Blob。生成的 URL 仅在当前文档打开的状态下才有效。但如果你访问的 Blob URL 不再存在,则会从浏览器中收到 404 错误。

上述的 Blob URL 看似很不错,但实际上它也有副作用。虽然存储了 URL → Blob 的映射,但 Blob 本身仍驻留在内存中,浏览器无法释放它。映射在文档卸载时自动清除,因此 Blob 对象随后被释放。但是,如果应用程序寿命很长,那不会很快发生。因此,如果我们创建一个 Blob URL,即使不再需要该 Blob,它也会存在内存中。

针对这个问题,我们可以调用 URL.revokeObjectURL(url) 方法,从内部映射中删除引用,从而允许删除 Blob(如果没有其他引用),并释放内存。

三. 图片灰度化

要对图片进行灰度化处理,我们就需要操作图片像素数据。

1.getImageData 方法

我们可以利用 CanvasRenderingContext2D 提供的 getImageData 来获取图片像素数据,其中 getImageData() 返回一个 ImageData 对象,用来描述 canvas 区域隐含的像素数据,这个区域通过矩形表示,起始点为(sx, sy)、宽为 sw、高为 sh。其中 getImageData 方法的语法如下:

ctx.getImageData(sx, sy, sw, sh);

相应的参数说明如下:

sx:将要被提取的图像数据矩形区域的左上角 x 坐标。
sy:将要被提取的图像数据矩形区域的左上角 y 坐标。
sw:将要被提取的图像数据矩形区域的宽度。
sh:将要被提取的图像数据矩形区域的高度。

2.putImageData 方法

在获取到图片的像素数据之后,我们就可以对获取的像素数据进行处理,比如进行灰度化或反色处理。当完成处理后,若要在页面上显示处理效果,则我们需要利用 CanvasRenderingContext2D 提供的另一个 API —— putImageData。

该 API 是 Canvas 2D API 将数据从已有的 ImageData 对象绘制到位图的方法。如果提供了一个绘制过的矩形,则只绘制该矩形的像素。此方法不受画布转换矩阵的影响。putImageData 方法的语法如下:

void ctx.putImageData(imagedata, dx, dy);
void ctx.putImageData(imagedata, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight);

相应的参数说明如下:

imageData:ImageData ,包含像素值的数组对象。
dx:源图像数据在目标画布中的位置偏移量(x 轴方向的偏移量)。
dy:源图像数据在目标画布中的位置偏移量(y 轴方向的偏移量)。
dirtyX(可选):在源图像数据中,矩形区域左上角的位置。默认是整个图像数据的左上角(x 坐标)。
dirtyY(可选):在源图像数据中,矩形区域左上角的位置。默认是整个图像数据的左上角(y 坐标)。
dirtyWidth(可选):在源图像数据中,矩形区域的宽度。默认是图像数据的宽度。
dirtyHeight(可选):在源图像数据中,矩形区域的高度。默认是图像数据的高度。

3.图片灰度化处理

<div>
    <h3>图片灰度化处理</h3>
    <div>
      <button id="grayscalebtn">灰度化</button>
      <div style="display: flex;">
        <div style="flex: 50%;">
          <p>预览容器</p>
          <img id="previewGreyImg" width="230" height="230" style="border: 2px dashed blue;" />
        </div>
        <div style="flex: 50%;">
          <p>Canvas容器</p>
          <canvas id="canvas" width="230" height="230" style="border: 2px dashed grey;"></canvas>
        </div>
      </div>
    </div>
  </div>
  <script>
    const image = document.querySelector("#previewGreyImg");
    const canvas = document.querySelector("#canvas");

    fetch("https://wx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTLZpzZqvib4bzlMqE3wibILYcSn23OFBaMDs4fNNpib6BxV3dyZDzbtLJLq5BPQfCoTv4zicXeHMuxNnw/132")
      .then((response) => response.blob())
      .then((blob) => {
        const objectURL = URL.createObjectURL(blob);
        image.src = objectURL;
        image.onload = () => {
          draw();
        };
      });

    function draw() {
      const ctx = canvas.getContext("2d");
      ctx.drawImage(image, 0, 0, 230, 230);
      const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
      const data = imageData.data;

      const grayscale = function () {
        for (let i = 0; i < data.length; i += 4) {
          const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
          data[i] = avg; // red
          data[i + 1] = avg; // green
          data[i + 2] = avg; // blue
        }
        ctx.putImageData(imageData, 0, 0);
      };
      const grayscalebtn = document.querySelector("#grayscalebtn");
      grayscalebtn.addEventListener("click", grayscale);
    }
  </script>

在以上示例中,我们先从 网上下载图片,然后先进行本地预览,接着调用 draw() 方法把获取的图像绘制到页面的 Canvas 容器中,同时为灰度化按钮绑定监听事件。

当用户点击灰度化按钮时,将会触发灰度化处理函数,在该函数内部会对通过 ctx.getImageData() 方法获取的图片像素进行灰度化处理,处理完成后再通过 ctx.putImageData() 方法把处理过的像素数据更新到 Canvas 上。

以上代码成功运行后,最终的灰度化效果如下图所示:
在这里插入图片描述

四. 图片压缩

在一些场合中,我们希望在上传本地图片时,先对图片进行一定的压缩,然后再提交到服务器,从而减少传输的数据量。在前端要实现图片压缩,我们可以利用 Canvas 对象提供的 toDataURL() 方法,该方法接收 type 和 encoderOptions 两个可选参数。

其中 type 表示图片格式,默认为 image/png。而 encoderOptions 用于表示图片的质量,在指定图片格式为 image/jpeg 或 image/webp 的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92,其他参数会被忽略。

下面我们来看一下如何对前面已进行灰度化处理的图片进行压缩。

<h3>图片灰度化处理</h3>
    <button id="grayscalebtn">灰度化</button>
    <button id="compressbtn">图片压缩</button>
    <div style="display: flex;">
      <div style="flex: 33.3%;">
        <p>预览容器</p>
        <img id="previewGreyImg" width="230" height="230" style="border: 2px dashed blue;" />
      </div>
      <div style="flex: 33.3%;">
        <p>Canvas容器</p>
        <canvas id="canvas" width="230" height="230" style="border: 2px dashed grey;">
        </canvas>
      </div>
      <div style="flex: 33.3%;">
        <p>压缩预览容器</p>
        <img id="compressPrevContainer" width="230" height="230" style="border: 2px dashed green;" />
      </div>
    </div>

    <script>
      const image = document.querySelector("#previewGreyImg");
      const canvas = document.querySelector("#canvas");

      fetch("https://wx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTLZpzZqvib4bzlMqE3wibILYcSn23OFBaMDs4fNNpib6BxV3dyZDzbtLJLq5BPQfCoTv4zicXeHMuxNnw/132")
        .then((response) => response.blob())
        .then((blob) => {
          const objectURL = URL.createObjectURL(blob);
          image.src = objectURL;
          image.onload = () => {
            draw();
          };
        });

      function draw() {
        const ctx = canvas.getContext("2d");
        ctx.drawImage(image, 0, 0, 230, 230);
        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const data = imageData.data;

        const grayscale = function () {
          for (let i = 0; i < data.length; i += 4) {
            const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
            data[i] = avg; // red
            data[i + 1] = avg; // green
            data[i + 2] = avg; // blue
          }
          ctx.putImageData(imageData, 0, 0);
        };
        const grayscalebtn = document.querySelector("#grayscalebtn");
        grayscalebtn.addEventListener("click", grayscale);
      }
      const compressbtn = document.querySelector("#compressbtn");
      const compressImage = document.querySelector("#compressPrevContainer");
      compressbtn.addEventListener("click", compress);

      function compress(quality = 80, mimeType = "image/webp") {
        const imageDataURL = canvas.toDataURL(mimeType, quality / 100);
        compressImage.src = imageDataURL;
      }
    </script>

在这里插入图片描述

其实 Canvas 对象除了提供 toDataURL() 方法之外,它还提供了一个 toBlob() 方法,该方法的语法如下:

canvas.toBlob(callback, mimeType, qualityArgument)

和 toDataURL() 方法相比,toBlob() 方法是异步的,因此多了个 callback 参数,这个 callback 回调方法默认的第一个参数就是转换好的 blob文件信息。

五. 图片上传

在获取压缩后图片对应的 Data URL 数据之后,可以把该数据直接提交到服务器。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值