imuncle / imuncle.github.io

大叔的个人小站
https://imuncle.github.io/
78 stars 17 forks source link

用 canvas 的 getImageData 做点有趣的事 #80

Open imuncle opened 4 years ago

imuncle commented 4 years ago

版权声明:本文为CSDN博主「FEWY」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/FE_dev/article/details/82586698

说明

canvas元素标签强大之处在于可以直接在HTML上进行图形操作,具有极大的应用价值。

canvas 可以实现对图像的像素操作,这就要说到 getImageData() 方法了。

解释

CanvasRenderingContext2D.getImageData() 返回一个 ImageData 对象,用来描述 canvas 区域隐含的像素数据,这个区域通过矩形表示,起始点为(sx, sy)、宽为sw、高为sh。

语法

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

参数

sx:将要被提取的图像数据矩形区域的左上角 x 坐标。

sy:将要被提取的图像数据矩形区域的左上角 y 坐标。

sw:将要被提取的图像数据矩形区域的宽度。

sh:将要被提取的图像数据矩形区域的高度。

返回值

一个 ImageData 对象,包含 canvas 给定的矩形图像数据。

ImageData 对象会有三个属性,heightwidthdata

ImageData.height 使用像素描述 ImageData 的实际高度,这个值其实等于 getImageData() 方法中的参数 sh

ImageData.width 使用像素描述 ImageData 的实际宽度。这个值其实等于 getImageData() 方法中的参数 sw

ImageData.data 一个一维数组,包含以 RGBA 顺序的数据,数据使用 0 至 255(包含)的整数表示。

image

注意

如果高度(sh)或者宽度(sw)变量为0,则会抛出错误。

示例

以上是需要知道的基本知识,下来看几个例子吧!

颜色选择器

效果图

image

实现思路

  1. 先把图片画到 canvas 上,
  2. 获取鼠标移动时的坐标并通过 getImageData() 方法,获取这一个点的像素,
  3. 得到像素信息后,拼接出 rgba 的字符串,再设置下面的小正方形的背景是这个颜色。

代码请点这里

图片灰度

效果图

image

实现思路

  1. 先在 canvas 上画出图片,
  2. 通过 getImageData() 方法,获取整个 canvas 上的像素信息,
  3. 点击按钮时,遍历获取到的像素信息的每个像素的 红色值、绿色值 和 蓝色值,相加求出平均值,再把平均值赋值给红色、绿色和蓝色,这么做是为了求出每个像素的灰度,
  4. 然后把改变后的像素信息,通过 putImageData() 方法重新添加到 canvas中,就实现了图片灰度的效果。

代码请点这里

除了图片可以实现灰度的效果外,因为 canvas 中也可以放视频,对于视频也可以实现灰度的效果,原理都是一样的,操作其实也一样,其实视频做灰度效果,可以理解为给很多张图片做灰度效果。

效果图

image

代码请点这里

文字粒子

效果图

image

实现思路

实现思路

我们先不说,怎么实现最后的动画效果,我们先来想怎么获取文字所有像素在 canvas 上的坐标,

1、先获取文字在canvas上的像素信息

我们知道文字的像素信息,知道每个像素的坐标,就能实现各种效果,像示例中的效果,仅仅只是改变一个 x 坐标的值而已。

2、遍历保存文字像素的数组(dotList),每个像素(Dot)对象还有一个 nowX 属性,初始值是0,每次画最后的圆点的时候,都是用这个属性作为圆的 x 坐标,nowX 属性不断的增加,直到最后等于像素(Dot)对象的 x 属性值就停止。

代码请点这里

这种效果我也是在 Canvas基础-粒子动画Part3 这篇文章中看到的,作者写的很好,推荐看看。

在github上也有一个不错的项目 shape-shifter,可以了解一下。

计算图片中的图形个数和面积

问题

平面上有若干个不特定的形状,如下图所示。请写程序求出物体的个数,以及每个不同物体的面积。

image

效果图

image

实现思路

  1. 创建存储图片像素点的二维数组(coordinates ),图中只有两种颜色,空白区域一种颜色,形状区域一种颜色,空白区域的像素标记为0,形状区域标记为1,类似下面这样
    0,0,0,0,0,0,0,0,0,0,0,0
    0,0,1,1,1,0,0,0,0,0,0,0
    0,1,1,1,1,0,0,0,0,0,0,0
    0,1,1,1,0,0,0,1,1,1,1,0
    0,0,0,0,0,0,1,1,1,0,0,0
    0,0,0,0,0,0,1,1,1,0,0,0
    0,0,0,0,0,0,0,0,0,0,0,0

2、计算有多少个形状,就是看二维数组中有多少个连续为1的块,计算形状的面积,就是算一个连续为1的块,有多少个1。

这就要通过 递归回溯算法 来计算了。

回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。 回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法。

代码请点这里

这里有一个示例,展示下用回溯法怎么找到这些形状的。

image

这个例子出自 前端面试的一道算法题(使用canvas解答)这篇文章,文章讲的也很好,推荐看看

总结

getImageData() 方法 要考虑两个问题

1、跨域问题 getImageData() 方法不允许操作非此域名外的图片资源,所以如果想本地试试文章中的例子,直接写图片路径就会报错

image

不过可以将图片转换成base64编码,直接把base64编码赋值给图片的src属性就可以简单的解决这个问题了。这种方式仅仅适用于图片,如果是视频的话,还是需要服务器端的配合。

具体可以看看这篇文章 解决canvas图片getImageData,toDataURL跨域问题

2、性能问题 getImageData() 方法一般获取的像素信息是很多的,所以这就要考虑性能的问题,至于怎么优化也是要看具体场景了,比如在文中的文字粒子效果的示例中,可以先用 measureText() 方法计算文字宽度,结合 fontSize 就能知道 文字在哪块区域,只通过这个块区域获取文字的像素就好了。

getImageData() 方法,一句话总结就是获取 canvas 上的像素信息,文中实现的各种效果不管是简单的还是复杂的,都是在操作像素。

通过getImageData() 方法,能做的事情还有很多,远不止文章中提到的这些,比如实现其他的滤镜效果,文中只是说了一个灰度,我们还可以去实现反相,模糊,浮雕等,文中实现了文字粒子效果,其实图片也可以实现粒子效果,除此之外还有很多好玩的事情,但是本人才疏学浅,好多我也不知道。 最后,如果文中有不足或者错误的地方,还请小伙伴们指出,万分感谢。

参考