来自 奥门威尼斯网址 2019-12-06 23:47 的文章
当前位置: 威尼斯国际官方网站 > 奥门威尼斯网址 > 正文

奥门威尼斯网址:纯前端完结图片背景透明化,

前言

不论是做一些2d的小游戏,或者制作小图标,或者抠图都需要用到这个功能,对图片的背景进行透明化,是我们经常需要用到的一个功能。

通常情况下我们都会去下载PS或者美图秀秀这样的软件去制作。

但是我真的不想仅仅为了做个透明图像就去下载这些软件,这些软件不仅体积大,要下载个半天,放在电脑上也占空间。

最重要的是每次我做这个事情,都需要去临时百度一下制作透明图片的方法。

这些软件固然强大,但是功能的众多或者需要一些基础知识,往往造成了一些门槛。

简单点说,虽然瑞士军刀很6,但是我现在只需要一把起子,我不想知道什么蒙版图层,不想在一堆什么美颜什么各种滤镜之中找半天,我就想上传个图片点两下就好了。

那么能不能在线对图片进行背景透明化呢?

当然是有的,下面是网址

你以为我是来推荐网站的?当然不是。

我之所以提到这个网站,是因为我以前就是用这个做一些处理的,但是真的不是很给力啊。

我并不知道它的原理,也没有看过它的代码,但是它的缺陷很明显:

  1. 不能对指定颜色进行透明化
  2. 当需要对色差很大的多种颜色进行透明化时,无能为力
  3. 对一些图片的透明化处理不够完美,会出现锯齿,但是又无法进行进一步处理
  4. 对复杂图片完全无能为力

有问题就解决问题呗,于是就有了今天的小玩意。

ImageData对象

ImageData对象包含了一个区域内的canvas的像素信息。它包含以下可读属性:

width
canvas的宽度,单位是像素。
奥门威尼斯网址 ,height
canvas的高度,单位是像素。
data
一个Uint8ClampedArray的一维数组,包含了每个像素的RGBA值。

什么是Uint8ClampedArray?这个数组里的数值是8位的整型,而且值得范围在0和255之间。任何不在[0, 255]之间的数都会变成[0, 255]之间最接近的那个整型数。
0到255之间,那记录的自然是RGBA颜色数值啦。这个data数组是这样排列的,data[0]是第一排第一列的像素R通道的数值,data[1]第一排第一列的像素G通道的数值,data[3]是第一排第一列的像素的Alpha通道的数值。而data[4]是第一排第二列的像素的R通道数值,以此类推。
比如说,第50排第200列的像素的蓝色通道的值:

blueComponent = imageData.data[((50 * (imageData.width * 4)) + (200 * 4)) + 2];

另外,data也有length属性,就是data数组的长度。

作品

与之前的作品一样,直接将功能写在这篇博客里了,所以可以直接在博客园中使用。

使用方法:

  1. 上传图片
  2. 点击图片,将以鼠标点击处的颜色为标准,对色差20之内的颜色进行透明化处理。(如果想调整色差标准,可以在控制台下设置transparentConfig.colorDiff)
  3. 对出现透明化处理有有误的地方,可以开启恢复模式,再次移动鼠标到图片上,此时鼠标会变成红色小方框,小方框区域内会显示原始图像。点击后会将红色小方框区域内的图像恢复为原始图像。(如果想调整小方框尺寸,可以在控制台下通过transparentConfig.setRecoverSize(30)的方式进行修改)
  4. 下载图片,搞定。

当然按照本懒人的惯例,还是只在chrome浏览器下实现,所以如果您用其它浏览器的话可能无法正常操作。

不过本应用的核心功能与以往一样都是可以在现代浏览器中实现的,只是需要您调一下兼容性。

如果您有闲情逸致,想研究一下的话,这是本项目的 GitHub地址,为了能方便复制进博客园,所以代码是直接写在html中的。

少说废话,以下为应用:

选择背景图 开启恢复模式

 

创建ImageData对象

有两种方法创建ImageData:

var myImageData = ctx.createImageData(width, height);

var myImageData = ctx.createImageData(anotherImageData);

注意啦,方法二是不会把data属性复制过去的,仅仅是复制了宽度和高度,data数组里的像素信息都是透明黑的,也就是都是0。

技术点

本应用依然只使用纯前端实现,涉及到的技术点如下:

  1. 获取图片文件
  2. 将文件转换为图片,并放入canvas中
  3. 点击canvas获取点击处的颜色信息
  4. 根据指定颜色,对图像中在色差范围内的颜色进行透明化处理
  5. 自定义鼠标,在鼠标上显示指定区域内原始图像
  6. 对图像上指定区域,进行图像还原操作
  7. 下载图片

其中技术点1,2,7在之前的一篇博客中有涉及到,所以这里就不再赘述,不了解的可以去看一下我之前写的那篇博客:[在博客园里给图片加水印(canvas

  • drag)]()

那么,接下来就让我们看一下具体的实现吧。

获取像素信息

可以用getImageData()方法获取像素信息。

var myImageData = ctx.getImageData(left, top, width, height);

top和left就是所截取的画布部分的左上角坐标。

Tip:
超过画布区域返回的像素信息都是透明黑,也就是都是0。

点击图片获取点击处的颜色信息

通过type为file类型的input获取到文件,然后通过FileReader读取文件信息后放入到canvas中。

这是前两步所做的工作,现在我们需要做的是点击图像(实际上是canvas)获取到点击处的颜色信息。

首先我们需要获取到原始图像的像素信息,并保存下来,这一步在图片加载时实现,部分代码如下:

var ctx = document.getElementById('target_canvas').getContext('2d');
imgDataArr = ctx.getImageData(0, 0, imgWidth, imgHeight).data;

 小课堂开始:

canvas的getImageData会获取canvas中指定区域内的图像信息,返回一个ImageData对象。

ImageData对象的data属性的值是一个Uint8ClampedArray对象,而这个对象就是图像的像素信息。

Uint8ClampedArray看名字可以了解到,它是一个定型数组,里面的值都是0-255范围之内的值。

假如我们有一个图片只有四个像素,长2px,宽2px。左上角的像素和右下角的像素为黑色,而右上角和左下角的像素为白色。

如下图:

奥门威尼斯网址 1

那么这张图片会以怎样的形式存储在Uint8ClampedArray数组中呢?

首先我们了解到白色的RGBA值为rgba(255,255,255,255),黑色的RGBA值为rgba(0,0,0,255)。

那么这张图片的分解为rgba值,分别为

rgba(0,0,0,255)           rgba(255,255,255,255)
rgba(255,255,255,255)     rgba(0,0,0,255)

那么颜色值将会以从左到右,从上至下的方式存储到Uint8ClampedArray数组中,如下:

[0,0,0,255,255,255,255,255,255,255,255,255,0,0,0,255]

小课堂讲解完毕,回到正题。

现在我们已经拿到了原始图像的像素信息了,并存放在了imgDataArr这个Uint8ClampedArray数组中。

如何获取鼠标点击处的像素信息呢?代码如下:

   /**
      * 获取图像数据中指定偏移处的颜色信息
      */
    function getColorInfo(imgDataArr, offsetX, offsetY) {
      var pos = canvasInfo.width * 4 * offsetY + offsetX * 4;
      return {
        rValue: imgDataArr[pos],
        gValue: imgDataArr[pos + 1],
        bValue: imgDataArr[pos + 2],
        aValue: imgDataArr[pos + 3]
      }
    }

    /**
     * 非恢复模式下,点击canvas,以点击处颜色为标准,去掉颜色色差在指定色差范围内的颜色
     */
    function transparetModeCanvasClick(e) {
      if (imgDataArr.length === 0) {
        return;
      }
      if (resultImgDataArr.length === 0) {
        resultImgDataArr = imgDataArr.slice(0)
      }
      var clickColorInfo = getColorInfo(resultImgDataArr, e.offsetX, e.offsetY)
      ...
    }

我们会给canvas绑定回调函数为transparetModeCanvasClick的click事件,那么,在鼠标点击canvas后,我们就可以获取到鼠标相对于canvas左上角的点击位置。

imgDataArr里面保存的是原始的图像像素信息,之后还会用到,所以这里不做处理。

那么就copy数据到当前像素信息数组resultImgDataArr中。

然后获取像素信息时需要计算像素在一维数组中的位置:

var pos = canvasInfo.width * 4 * offsetY + offsetX * 4;

根据上面的表达式得到点击的那个像素在一维数组中的位置,如果有仔细阅读之前Uint8ClampedArray存储像素信息的方式,这个表达式应该不难理解。

例子

var img = new Image();
img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
img.onload = function() {
  ctx.drawImage(img, 0, 0);
  img.style.display = 'none';
};
var color = document.getElementById('color');
function pick(event) {
  var x = event.layerX;
  var y = event.layerY;
  var pixel = ctx.getImageData(x, y, 1, 1);
  var data = pixel.data;
  var rgba = 'rgba(' + data[0] + ', ' + data[1] +
             ', ' + data[2] + ', ' + (data[3] / 255) + ')';
  color.style.background =  rgba;
  color.textContent = rgba;
}
canvas.addEventListener('mousemove', pick);

判断颜色与指定颜色的色差,并做透明化处理

在获取到点击像素的颜色信息后,我们需要去遍历整个canvas的像素信息,对于色差小于指定范围的颜色做透明化处理。代码如下:

        /**
         * 获取图像数据指定位置颜色与指定颜色的色差
         */
        function getColorDiff(imgDataArr, pos, colorInfo) {
          var value = Math.pow(imgDataArr[pos] - colorInfo.rValue, 2) +
            Math.pow(imgDataArr[pos + 1] - colorInfo.gValue, 2) +
            Math.pow(imgDataArr[pos + 2] - colorInfo.bValue, 2);

          return Math.pow(value, 0.5);
        }
        /**
         * 设置图像数据指定位置为透明色
         */
        function setTransparent(imgDataArr, pos) {
          imgDataArr[pos] = 0;
          imgDataArr[pos + 1] = 0;
          imgDataArr[pos + 2] = 0;
          imgDataArr[pos + 3] = 0;
        }
        /**
         * 非恢复模式下,点击canvas,以点击处颜色为标准,去掉颜色色差在指定色差范围内的颜色
         */
        function transparetModeCanvasClick(e) {
          if (imgDataArr.length === 0) {
            return;
          }
          if (resultImgDataArr.length === 0) {
            resultImgDataArr = imgDataArr.slice(0)
          }
          var clickColorInfo = getColorInfo(resultImgDataArr, e.offsetX, e.offsetY)

          // 如果是透明颜色则不做处理
          if (clickColorInfo.aValue === 0) {
            return;
          }

          var ctx = document.getElementById('target_canvas').getContext('2d');
          for (var pos = 0, len = canvasInfo.width * canvasInfo.height * 4; pos < len; pos = pos + 4) {
            if (getColorDiff(resultImgDataArr, pos, clickColorInfo) < transparentConfig.colorDiff) {
              setTransparent(resultImgDataArr, pos);
            }
          }
          ctx.putImageData(new ImageData(resultImgDataArr, canvasInfo.width, canvasInfo.height), 0, 0);
          setCanvasImgToDownloadLink();
        }

 色差计算公式为将rgb三个值的颜色相减,将他们的平方和进行开方即可。

这里其实存在一个优化点:

1、通常我们设为透明色的颜色,每次实际上都比较单一,那么在这里可以设一个临时数组存放已经比较过色差的颜色。再次要对一个颜色比较色差前,可以查看颜色是否在这个数组中,从而避免再次计算色差,因此对于大图像而言,存在很多像素点,这种大量计算能尽量减少的话可以让操作更快。

2、这里的循环也可以进行优化。倒序循环  + Duff's Device可以进行优化。

之所以在这里强调这一点,是因为在这里对大图进行消除单一颜色为透明色时,确实需要消耗更多时间。(不过我还能接受,所以暂时不理了)

至于设置图片为透明色,实际上只需要将

imgDataArr[pos + 3] = 0;

即可。
但是为了和一般声明的透明颜色保持一致,还是将其他几个值都设为0。

将数据设为透明色的数据之后,需要调用canvas的putImageData方法将整个图像的数据设置到canvas中。

至此,我们完成了对图像进行透明化处理的整个过程。
但是仍然不够,因为对于复杂图像而言,这种方式处理起来太过粗暴,无法做到精细化的处理。
所以接下来我们要实现恢复模式,对于处理的不好的地方进行恢复原始图像的操作。

结果

奥门威尼斯网址 2
鼠标滑过就会显示rgba值。

本文由威尼斯国际官方网站发布于奥门威尼斯网址,转载请注明出处:奥门威尼斯网址:纯前端完结图片背景透明化,

关键词: