大家都知道canvas很好很强大,入门资料可以看mozilla developer center的Canvas教程,在这里就不多说了。
这里只是希望利用canvas的图像处理功能来创建ios图标风格的图片,就像下面这样:
接下来就开始吧:
- 准备原始图片
将它绘制到canvas里的方法也很简单,首先在html中得有一个canvas标签:
<canvas id="mycanvas" width="437" height="400"></canvas>
然后是javascript代码:
var context = document.getElementById("mycanvas").getContext("2d");
var img = new Image();
img.onload = function(){
context.drawImage(img, 0, 0);
};
img.src='https://www.lofter.com/blog/shellchine/new/text/chihuo.png'; - 为图片裁剪圆边,简单的思路就是在canvas中画个圆角矩形clip一下,但问题是canvas没有给我们提供原生的圆角矩形函数,只好自己动手,丰衣足食了,先是定义一个圆角矩形绘制方法,然后用它来对canvas裁剪一下边界:
CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r) {
if (w < 2 * r) r = w / 2;
if (h < 2 * r) r = h / 2;
this.beginPath();
this.moveTo(x+r, y);
this.arcTo(x+w, y, x+w, y+h, r);
this.arcTo(x+w, y+h, x, y+h, r);
this.arcTo(x, y+h, x, y, r);
this.arcTo(x, y, x+w, y, r);
this.closePath();
return this;
};
context.roundRect(0, 0, 437, 400, 40).clip();
得到的效果如下: - 接下来为图片添加高亮效果,基本思路是使用蒙板图片叠加。说起来很简单,但这个图片叠加里是有很多讲究的,用不好的话就是差之毫厘,谬以千里了。
首先我们得了解canvas为我们提供了哪些图片叠加方法,以下表格列出了目前canvas中可用的globalCompositeOperation,可将你的canvas context对象设置globalCompositeOperation属性为以下的任何一个值(默认为source-over)。图中蓝色矩形表示原有内容(destination),红色圆形表示新图形(source):
source-over: 默认设置,新图形会覆盖在原有内容之上
destination-over: 在原有内容之下绘制新图形
source-in: 新图形会仅仅出现与原有内容重叠的部分。其它区域都变成透明的
destination-in: 原有内容中与新图形重叠的部分会被保留,其它区域都变成透明的
source-out: 只有新图形中与原有内容不重叠的部分会被绘制出来
destination-out: 原有内容中与新图形不重叠的部分会被保留
source-atop: 新图形中与原有内容重叠的部分会被绘制,并覆盖于原有内容之上
destination-atop: 原有内容中与新内容重叠的部分会被保留,并会在原有内容之下绘制新图形
lighter: 两图形中重叠部分作加色处理
darker: 两图形中重叠的部分作减色处理
(注:新的canvas标准已无此定义).xor: 重叠的部分会变成透明
copy: 只有新图形会被保留,其它都被清除掉
现在我们再拿出要叠加到原始图片的蒙板图,是个半透明的灰度图(这张图是用imagemagick生成的,ps当然也可以,有闲再写下怎么用canvas来画),如下:
对照前面的列表看来,在canvas默认提供的图片叠加方法,可能符合我们要求的也只有默认的source-over了,虽然觉得不是想要的,还是硬着头皮试试吧: - 试完了,果然发现,这真的不是我们想要的~~~
看来canvas默认提供的图片叠加效果是没戏了。幸好canvas同时还提供了图片像素级操作的接口,我们可以用它来实现更复杂的图片叠加算法,如Multiply, Screen, Bumpmap, Divide, Plus, Minus, ModulusAdd, ModulusSubtract, Difference, Exclusion, Lighten, Darken 等等。在这里,我们只需要实现Screen算法。
实现算法前,首先看看我们要用到像素级的操作方法,包括:
- createImageData(w,h):可以新建一个w*h尺寸的新的ImageData,不过也可以使用参数(anotherImageData)来创建【firefox5开始支持】。
- getImageData(x,y,w,h):表示起点x,y,尺寸w,h。可以获取canvas.context中在参数范围内的ImageData。
- putImageData(ImageData, dx, dy [, DirtyX] [, DirtyX] [, DirtyWidth] [, DirtyHeight]):imageData包含了图像的width,height, 还有一个CanvasPixelArray。dx, dy表示绘图起始位置。相对于canvas区域左上角。后面四个可选参数:表示可见区范围。相对于起绘点,即上面的参数dx,dy表示的点。缺省为0,0,ImageData.width,ImageData.height。
实现函数如下:
function imageData(url, fn){
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.onload = function(){
var ht = img.height;
var width = img.width;
canvas.width = width;
canvas.height = ht;
ctx.drawImage(img, 0, 0);
if(typeof fn == 'function'){
fn(ctx.getImageData(0,0,width, ht));
}
};
img.src = url;
}
imageData('chihuo_r.png', function(data1){
imageData('ios-over.png', function(data2){
var pix1 = data1.data;
var pix2 = data2.data;
for (var i = 0,n = pix1.length; i < n; i+=4) {
var alpha = pix2[i+3]/255;
for(var j = i; j < i+3; j++){
pix1[j] = 255*(1-(1-pix1[j]/255)*(1-pix2[j]*alpha/255));
}
}
var context = document.getElementById("mycanvas").getContext("2d");
context.putImageData(data1,0,0);
});
});
这次得到的效果正是文章开头所示的样子了。
这个例子只是canvas图像处理功能的牛刀小试,事实上,利用canvas提供的像素级操作方法,我们便可以封装出大量的图像处理滤镜,实现photoshop的许多酷炫的特效,这是后话,暂且不表。