原文请见个人github博客>>html
接触canvas应该是在去年半次元作制品计划吧,想一想也很久了,不过,那会儿天天累得和狗同样,周末还要上课。经验总结也基本都记录在OneNote,思惟跳跃性的记录也就不适合做为博客发布。
如今来了新公司,一段时间忙,一段时间闲成狗,因此就会再从新总结,写写博客。c++
引用自MDN:
<canvas> 是 HTML5 新增的元素,可用于经过使用JavaScript中的脚原本绘制图形。例如,它能够用于绘制图形,制做照片,建立动画,甚至能够进行实时视频处理或渲染。git
我认为canvas最好的教程就是MDN的,canvas基础补充请戳这里>>github
咱们能够将已经加载好的图片画到canvas上。
绘制图片的api接口:drawImage(image, x, y, width, height)
其中 image 是 image 或者 canvas 对象,x 和 y 是其在目标 canvas 里的起始坐标。width 和 height,这两个参数用来控制 当像canvas画入时应该缩放的大小。
drawImage其实还有四个额外参数,通常用来作截图,这里由于文章不涉及就不赘述了。有兴趣的小伙伴能够戳这里>>canvas
getImageData是canvas提供的一个很是强大的接口,它能够获取canvas的全部的像素点的值。不过,值的展示形式和通常的rgba或rgb等属性不一样,全部的值会被记录在一个Uint8ClampedArray
的一维数组里面。api
The Uint8ClampedArray typed array represents an array of 8-bit unsigned integers clamped to 0-255; if you specified a value that is out of the range of [0,255], 0 or 255 will be set instead; if you specify a non-integer, the nearest integer will be set. The contents are initialized to 0.数组
翻译一下:dom
Uint8ClampedArray类型数组表示一个8-bit无符号整数,即0-255区间;若是你设了一个的值超出了[0, 255]的范围,他们会被0或者255代替(小于0代替为0,大于255替代为255);若是你设了一个非整数,会被替代为这个小数最接近的整数。全部的初始值为0;ide
见图:
若是,canvas将每一个像素点的值按照rgba这样的顺序一个一个的存入Unit8ClampedArray里面。
所以,数组的长度为length = canvas.width canvas.height 4。动画
知道了这种关系,咱们不妨把这个一维数组想象成二维数组,想象它是一个平面图,如图:
一个格子表明一个像素
w = 图像宽度
h = 图像高度
这样,咱们能够很容易获得点(x, y)在一维数组中对应的位置。咱们想想,点(1, 1)坐标对应的是数组下标为0,点(2, 1)对应的是数组下标4,假设图像宽度为2*2,那么点(1,2)对应下标就是index=((2 - 1)*w + (1 - 1))*4 = 8
。
推导出公式:index = [(y - 1) w + (x - 1) h] * 4
咱们既然已经可以拿到图像的每个像素点,那么咱们就能够随心所欲啦!
不过客官别急,咱们还有点小知识要补充,避免代码实现的过程陷入迷茫~
The CanvasRenderingContext2.createImageData() method of the Canvas 2D API creates a new, blank ImageData object with the specified dimensions. All of the pixels in the new object are transparent black.
翻译(非直译):
createImageData是在canvas在取渲染上下文为2D(即
canvas.getContext('2d')
)的时候提供的接口。做用是建立一个新的、空的、特定尺寸的ImageData对象。其中全部的像素点初始都为黑色透明。
咱们会用到ctx.createImageData(width, height)
这个接口,width和height是新ImageData对象的初始长宽。
ImageData是一个对象,其实咱们在canvas.getImageData拿到的对象就是ImageData,它内部由width,height,Uint8ClampedArray组成,
如:{data: Uint8ClampedArray(958400), width: 400, height: 599}
The CanvasRenderingContext2D.putImageData() method of the Canvas 2D API paints data from the given ImageData object onto the bitmap. If a dirty rectangle is provided, only the pixels from that rectangle are painted. This method is not affected by the canvas transformation matrix.
翻译:
CanvasRenderingContext2D.putImageData() 方法做为canvas 2D API 以给定的ImageData对象绘制数据进位图。若是提供了脏矩形,将只有矩形的像素会被绘制。这个方法不会影响canvas的形变矩阵。
看上去有点迷糊,矩阵都出来了。不过不用担忧,咱们只关注第一句就好,忽略“若是“以后的文字。
咱们将会用到ctx.putImageData(imagedata, dx, dy)
接口,imageData就是用户提供的ImageData对象,dx和dy分别是canvas坐标系的x点和y点,将从这个(dx,dy)开始输入数据。
终于迎来了最后的阶段!
直接上代码:
html:
<div class="box"> <img id="img" src="index.png"> </div> <canvas id="canvas"></canvas>
js
draw() function draw() { const canvas = document.getElementById('canvas') const ctx = canvas.getContext('2d') const img = document.getElementById('img') // 等图片加载完之后才能获取图片信息 img.onload = function() { const style = window.getComputedStyle(img) const w = style.width const h = style.height const ws = w.replace(/px/, '') const hs = h.replace(/px/, '') canvas.width = ws canvas.height = hs ctx.drawImage(img, 0, 0, canvas.width, canvas.height) // 修改颜色准备 const originColor = ctx.getImageData(0, 0, ws, hs) // 保存ImageData里的Uint8ClampedArray数据 const originColorData = originColor.data // 建立一个空的图像,这时canvas里其实已经没原来的图像了 const output = ctx.createImageData(ws, hs) const outputData = output.data // 诡异画风按钮绑定 const weirdBtn = document.getElementById('weird') weirdBtn.addEventListener('click', function() { // 诡异画风数据处理(咱们能够用各类处理方法处理图像数据,达到想要的效果) weird(originColorData, outputData, ws, hs) ctx.putImageData(output, 0, 0) }) } } // 诡异 function weird(originColorData, outputData, ws, hs) { let random let randomData let index; let r, g, b; // 逐行扫描 for (let y = 1; y <= hs; y++) { // 逐列扫描 for (let x = 1; x <= ws; x++) { // rgb处理 for (let c = 0; c < 3; c++) { random = Math.random(0, 255) * 100 randomData = Math.abs(random - originColorData[index]) index = ((y-1) * ws + (x-1)) * 4 + c outputData[index] = randomData } // alpha处理,咱们就让透明度一直未1就行了 outputData[index + 3] = 255; } } }
经过对imageData的处理,咱们能够控制每一个像素点,而后你想处理出不一样的效果,只须要改写weird方法就能够了。我写了5种滤镜效果,效果以下gif图: