到目前为止,我们使用了图像以及路径。这是相当高级的。然而,canvas 同样能够处理像素。 你能够从整个画布或一部分中获取像素,接着处理像素,在将处理完毕的像素设置回canvas画布里。 这能够让你做出很多有趣的效果。
生成纹理
假设我们想生成西洋跳棋盘的纹理。纹理的尺寸是300 * 200 像素。
//create a new 300 x 300 pixel buffer
var data = c.createImageData(300,200);
// 遍历每一个像素
for(var x=0; x<data.width; x++) {
for(var y=0; y<data.height; y++) {
var val = 0;
var horz = (Math.floor(x/4) % 2 == 0); // 每四个像素循环一次
var vert = (Math.floor(y/4) % 2 == 0); // 每四个像素循环一次
if( (horz && !vert) || (!horz && vert)) {
val = 255;
} else {
val = 0;
}
var index = (y*data.width+x)*4; // 计算下标
data.data[index] = val; // 红色通道
data.data[index+1] = val; // 绿色通道
data.data[index+2] = val; // 蓝色通道
data.data[index+3] = 255; // 设置alpha透明度为1
}
}
// 将处理完的数据设置回画布上
c.putImageData(data,0,0);
相当简单。我们创建了一个新的缓存区,遍历每一个元素根据x,y坐标设置颜色,再将缓存数据设置到canvas画布上。 现在你注意到我们处理的是二维图像,但缓冲区只是一维数组。我们需要独自计算像素坐标的下标。
画布缓存区是非常简单的由每个像素的整数值组成的一维长数组。每个像素由红、绿、蓝以及alpha透明度部分组成,因此,
计算特定像素的红色通道值索引下标需要通过计算下面的等式:(y * width + x) * 4。对于宽度为20像素位图中的像素点(8,10)代入公式就是
(10*20 + 8) * 4
。* 4
乘4是因为每一个像素包括四个颜色通道(红绿蓝以及透明度)。
数据data对象中包括了图像的宽度,所以代码就是 (10*data.width + 8)*4
。
一旦找到了红色通道,那么剩下的通道也能通过增加下标找到,正如上面的绿、蓝以及透明通道代码所示。
这是上面代码的运行结果。
添加噪音
现在我们做些许修改让棋盘看上去有些陈旧。 通过随机生成一些稍微不同颜色的像素,添加一些干扰像素。
if(val == 0) {
val = Math.random()*100;
} else {
val = 255-Math.random()*100;
}
上面,棋盘就做旧了一下。
图像反色
上面是通过像素缓冲区生成图像。 同样可以处理在canvas画布上的像素数据。 这就是说几乎所有的 PS 的滤镜及修改都能使用 canvas 完成。 例如,想将一条图片做反色处理。反色处理的程序很简单。 一个像素是由 RGBA 四个通道的值组成,每个值的范围是0到255。 转换每个像素值只需要使用255减去当前的通道值。如下代码:
var img = new Image();
img.onload = function() {
// 绘制图形到画布上
c.drawImage(img,0,0);
// 获取画布上的通道数据
var data = c.getImageData(0,0,canvas.width,canvas.height);
// 反转红绿蓝像素通道的值
for(n=0; n<data.width*data.height; n++) {
var index = n*4;
data.data[index] = 255-data.data[index];
data.data[index+1] = 255-data.data[index+1];
data.data[index+2] = 255-data.data[index+2];
// alpah 通道不需要处理
}
// 将处理完毕的数据设置回canvas画布上
c.putImageData(data,0,0);
}
img.src = "baby_original.png";
上面只修改了RGB通道值。Alpha通道不修改。处理后的图像如下:
灰度
Here's another example. It's essentially the same code, just a different equation. This one will turn a color image into black and white.
这里有另一个例子。代码也很类似,只是算法不同。这个例子将彩色图像处理成黑白效果。
for(n=0; n<data.width*data.height; n++) {
var index = n*4;
var r = data.data[index];
var g = data.data[index+1];
var b = data.data[index+2];
var v = r*0.21+g*0.71+b*0.07; // 平均权重
data.data[index] = v;
data.data[index+1] = v;
data.data[index+2] = v;
// alpha 通道不处理
}
我们没有简单地通过平均通道值选择灰度值。我发现我们的眼睛对某些颜色比其他颜色更加敏感。 所以上面代码的计算中加大了绿色通道的权重。以下是最终结果。
使用图像缓冲区,你能够任意绘制或操作图像,唯一的限制是速度。 虽然处理二进制数据不是Javascript的强项,然而浏览器运行得越来越快,因此一些类PS的图像处理在今天也是可能的。 在后面的工具章节中哦个,我会给你展示一些处理更加简单及快速的类库。
混合模式
Canvas 也支持混合模式。你会发现一些跟PhotoShop相类似的模式。
每次你绘制一个路径,每一个像素都会跟原来存在的像素做比较,并会根据算法计算最后的像素。
通过我们使用的模式是SrcOver
,就是源像素(当前正在绘制的)会绘制在目标像素的上面over
。
如果源像素是部分透明的,那么源像素与目标元素的透明部分就会混合在一起。
然而SrcOver
只是众多模式中的一种。下面的例子是使用 lighter
模式绘制的重叠的圆。
lighter
会将两部分的像素相加,最大值为白色。
c.globalCompositeOperation = "lighter"; //set the blend mode
c.fillStyle = "#ff6699"; //fill with a pink
// 随机绘制50个圆
for(var i=0; i<50; i++) {
c.beginPath();
c.arc(
Math.random()*400, // 随机 x
Math.random()*400, // 随机 y
40, // 半径
0,Math.PI*2); // 整圆
c.closePath();
c.fill();
}
阴影效果
Canvas 支持阴影,与 CSS 类似。能够设置阴影颜色,偏移量和模糊半径来模拟不同的效果。 下面的例子是模拟绿色文本外发白光。
c.fillStyle = "black";
c.fillRect(0,0,canvas.width,canvas.height);
c.shadowColor = "white";
c.shadowOffsetX = 0;
c.shadowOffsetY = 0;
c.shadowBlur = 30;
c.font = 'bold 80pt Arial';
c.fillStyle = "#55cc55";
c.fillText("ALIEN",30,200);