canvas学习之路--记录(二)
接着上面...
一、图形组合
当我们在canvas中绘制的图形重合在一起的时候,能看到哪个图形则完全取决于图形的绘制顺序(后绘制的图形会覆盖掉之前绘制的图形,当然,如果后面绘制的图形颜色透明度不为1,则会受之前绘制图形的颜色影响).
图形上下文对象的globalCompositeOperation属性能让我们自己决定图形的组合方式,如context.globalCompositeOperation = type;
其中type的值必须是下面几种字符串之一:
type值 | type所代表的的含义 |
source-over(默认值) | 为默认值,表示新图形覆盖在原有图形之上 |
destination-over | 在原有图形之下绘制新图形 |
source-in | 只显示新图形中与原有图形相重叠的部分,其他部分则变成透明 |
destination-in | 只显示原有图形中与新图形相重叠的部分,新图形与原有图形的其他部分都透明 |
source-out | 只显示新图形中与原有图形不重叠的部分,新图形与原有图形的其他部分变成透明 |
destination-out | 只显示原有图形中与新图形不重叠的部分,新图形与原有图形的其他部分变成透明 |
source-atop | 只绘制新图形中与原有图形重叠的部分与未被重叠覆盖的原有图形,新图形的其他部分变成透明 |
destination-atop | 只绘制原有图形中被新图形覆盖的部分与新图形的其他部分,原有图形的其他部分变成透明 |
lighter | 原有图形与新图形均绘制,重叠部分做加色处理 |
xor | 只绘制新图形中与原有图形不重叠的部分,重叠部分变成透明 |
copy | 只绘制新图形,原有图形中未与新图形重叠的部分变成透明 |
可在绘制路径之前,提前设置context.globalCompositeOperation = [[type]]-->11中类型,如果,未填写正确类型,则会是默认类型
二、给图形绘制阴影
使用图形上下文对象的几个关于阴影绘制的属性即可添加阴影效果
shadowOffsetX-->阴影的横向偏移量
shadowOffsetY-->阴影的纵向偏移量
shadowColor-->阴影的颜色
shadowBlur-->阴影的模糊范围,属性可选,它表示阴影边缘的模糊范围,如果不希望阴影的边缘太清晰,需要将阴影的边缘模糊化时可以使用该属性,设定的属性值为比0大的数字,否则会被忽略,一般设定0到10之间
注:使用图形上下文对象的绘制阴影属性,这几个属性与路径无关,只要设定一次,全部的图形都会具有阴影效果,如果想让某一个图形没有阴影效果,只需要重新将shadowColor设定为rgba(0,0,0,0)就可以了.
function draw (obj) { var $myCanvas = document.getElementById(obj); if ($myCanvas == null) return false; var $ctx = $myCanvas.getContext('2d'); $ctx.fillStyle = '#eef'; $ctx.fillRect(0,0,400,300); $ctx.shadowOffsetX = 10; $ctx.shadowOffsetY = 10; $ctx.shadowColor = 'rgba(100,100,100,0.5)'; $ctx.shadowBlur = 7.5; $ctx.translate(200,50); for (var i = 0; i < 50; i++) { $ctx.translate(25,25); $ctx.scale(0.95,0.95); $ctx.rotate(Math.PI/10); create5Star($ctx); $ctx.fill(); } } function create5Star (context) { var n = 0, dx = 100, dy = 0, s = 50; context.beginPath(); context.fillStyle = 'rgba(255,0,0,0.5)'; var x = Math.sin(0), y = Math.cos(0), dig = Math.PI/5*4; for (var i = 0; i < 5; i++) { var x = Math.sin(i*dig), y = Math.cos(i*dig); context.lineTo(dx+x*s,dy+y*s); } context.closePath(); }
三、使用图像
接下来是我个人认为比较重要的几个用途.
1.绘制图像
在canvas中可以读取图像文件,然后将之绘制在画布当中,绘制图像的方法:drawImage
context.drawImage(image,x,y); ===> x,y-->绘制时该图像在画布中的起始坐标
context.drawImage(image,x,y,w,h); ===> 前三个参数与上面一样,w、h则是指绘制时的图像(画布区域出现的图像的宽高)的宽度与高度,第一种方法中省略了这两个参数,所以绘制出来的图像与原图大小相同,
context.drawImage(image,sx,sy,sw,sh,dx,dy,dw,dh); ===> 第三种方法则是将画布中已经绘制好的图像的全部或则局部区域复制到画布中的另一个位置上,除了image仍然代表被复制的图像文件,前面四个参数是原图像的被复制区域,后面四个参数是画布中绘制出图像的区域.
上面三种形式中,image都代表一个图片对象,用该对象来装载图片文件.
绘制图像时需要使用不带参数的new方法创建Image对象,然后设定该Image对象的src属性为需要绘制的图像文件的路径,如下
var image = new Image(); image.src = 'image1.jpg'; //设置图像路径
然后就可以使用drawImage方法绘制该图像文件了
事实上,即时设定好Image对象的src属性后,也不一定立刻就能把图形绘制完毕,譬如,有时该图像文件是一个来源于网络的比较大的图像文件,这时候用户就得耐心等待图像全部加载完毕才能看见该图像了.
这种情况下,可以使用
image.onload = function(){} //绘制图像的函数
在Image对象的onload事件中同步执行绘制图像的函数,就可以一边装载一边绘制了.
function draw (obj) { var $myCanvas = document.getElementById(obj); if ($myCanvas == null) return false; var $ctx = $myCanvas.getContext('2d'); var img = new Image(); img.src = 'http://img17.3lian.com/d/file/201702/22/1005a2e0825ffe290b3f697404ee8038.jpg'; img.onload = function () { $ctx.drawImage(img,0,0,400,300); } }
2.图像平铺
使用图像上下文对象的createPattern方法就可以实现图像平铺效果: context.createPattern(image,type);
其中image为要平铺的对象,type的值则有下面几种类型(注意,type值为字符串类型)
no-repeat:不平铺
repeat-x:横方向平铺
repeat-y:纵方向平铺
repeat:全方向平铺
function draw (obj) { var $myCanvas = document.getElementById(obj); if ($myCanvas == null) return false; var $ctx = $myCanvas.getContext('2d'); var img = new Image(); img.src = 'http://img17.3lian.com/d/file/201702/22/1005a2e0825ffe290b3f697404ee8038.jpg'; img.onload = function () { var ptrn = $ctx.createPattern(img,'repeat'); $ctx.fillStyle = ptrn; $ctx.fillRect(0,0,400,300); } }
3.图片裁剪
使用canvas绘制图像的时候,我们经常会想要保留图像的一部分,可以使用canvas API自带的图像裁剪功能来实现.
使用图形上下文对象的不带参数的clip方法来实现canvas元素的图像裁剪功能.该方法使用路径来对canvas画布设置一个裁剪区域,因此必须先创建好路径,等路径创建完成后,再调用clip方法设置裁剪区域.
function draw (obj) { var $myCanvas = document.getElementById(obj); if ($myCanvas == null) return false; var $ctx = $myCanvas.getContext('2d'); var gr = $ctx.createLinearGradient(0,400,300,0); gr.addColorStop(0,'rgb(255,255,0)'); gr.addColorStop(1,'rgb(0,255,255)'); $ctx.fillStyle = gr; $ctx.fillRect(0,0,400,300); var img = new Image(); img.onload = function () { drawImg($ctx,img); } img.src = 'img/aaa.jpg'; } function drawImg (context,image) { create5StarClip(context); context.drawImage(image,-50,-150,300,300); } function create5StarClip (context) { var n = 0, dx = 100, dy = 0, s = 150; context.beginPath(); context.translate(100,150); var x = Math.sin(0), y = Math.cos(0), dig = Math.PI/5*4; for (var i = 0; i < 5; i++) { var x = Math.sin(i*dig), y = Math.cos(i*dig); context.lineTo(dx+x*s,dy+y*s); } context.clip(); }
4.像素处理(本人觉得这个东西实在是太牛逼了)
使用canvas API能够获取图像中的每一个像素,然后得到该像素颜色的rgb值或rgba值.使用图形上下文对象的getImageData方法来获取图像中的像素,如: var imagedata = context.getImageData(sx,sy,sw,sh);
其中:sx,sy--->分别表示所获取区域的起点横坐标和纵坐标,sw,sh--->表示所获区域的宽度和高度.
imagedata变量是一个CanvasPixelArray对象(画布像素数组对象),具有height,width,data等属性.其中data属性是一个保存像素数据的数组,内容类似"[r1,g1,b1,a1,r2,g2,b2,a2,......]",其中r1,g1,b1,a1为第一个像素的红色值,绿色值,蓝色值,透明度值;r2,g2,b2,a2分别为第二个像素的红色值,绿色值,蓝色值,透明度值,以此类推,data.length/4为所取得像素的数量
function draw (obj) { var $myCanvas = document.getElementById(obj); if ($myCanvas == null) return false; var $ctx = $myCanvas.getContext('2d'); $ctx.fillStyle = '#eef'; $ctx.fillRect(0,0,400,300); var img = new Image(); img.src = 'img/aaa.jpg'; img.onload = function () { $ctx.drawImage(img,0,0,400,300); } var imagedata = $ctx.getImageData(0,0,400,300); console.log(imagedata); console.log(imagedata.data.length); }
取得这些像素之后,就可以对这些像素进行处理了,其中可以用Canvas API将图像进行反显操作.
首先将数组中每个像素的颜色进行反显操作,然后保存回像素组,最后使用图形上下文对象的putImageData方法将反显操作后的图像重新绘制在画布上.
如:context.putImageData(imagedata,dx,dy[,dirtyX,dirtyY,dirtyWidth,dirtyHeight]);
该方法的函数,其中imagedata为前面所述的像素数组,dx,dy分别表示重绘图像的起点横坐标,起点纵坐标,后面的dirtyX,dirtyY,dirtyWidth,dirtyHeight则是可选参数,是一个矩形的起点横坐标,起点纵坐标,宽度与高度,加上这四个参数,则只绘制像素数组中这个矩形范围内的图像!
注:Canvas API的像素操作页只有部分浏览器支持.
function draw (obj) { var $myCanvas = document.getElementById(obj); if ($myCanvas == null) return false; var $ctx = $myCanvas.getContext('2d'); var img = new Image(); img.src = 'img/aaa.jpg'; img.onload = function () { $ctx.drawImage(img,0,0,400,300); var imagedata = $ctx.getImageData(0,0,400,300); for (var i = 0; i < imagedata.data.length; i+=4) { imagedata.data[i+0] = 255 - imagedata.data[i+0]; imagedata.data[i+1] = 255 - imagedata.data[i+1]; imagedata.data[i+2] = 255 - imagedata.data[i+2]; } $ctx.putImageData(imagedata,0,0); } }
四、绘制文字
在HTML5中,可以在canvas画布中进行文字的绘制,同时也可以指定绘制文字的字体,大小,对齐方式等,还可以进行文字的纹理填充等.
绘制文字的方法:fillText或strokeText
fillText(text,x,y[,maxWidth]) 该方法用填充方式绘制字符串,text-->所要绘制的文字 x,y-->绘制文字的起点横纵坐标,第四个参数maxWidth为可选参数,表示显示文字时的最大宽度,可以防止文字溢出
strokeText(text,x,y[,maxWidth]) 同上
在对文字进行绘制之前,可以对该对象的有关文字绘制的属性进行设置:
font : 设置文字字体
textAlign : 设置文字水平对齐方式 属性值:start(默认值),end,left,right,center
textBaseline : 设置文字垂直对齐方式 属性值:top,hanging,middle,alpabetic(默认值),ideographic,bottom
function draw (obj) { var $myCanvas = document.getElementById(obj); if ($myCanvas == null) return false; var $ctx = $myCanvas.getContext('2d'); $ctx.fillStyle = '#00f'; $ctx.font = 'italic 30px sans-serif'; $ctx.textBaseline = 'top'; $ctx.fillText('上山打松鼠',0,0); $ctx.font = 'bold 30px sans-serif'; $ctx.strokeText('上山打松鼠',0,50); }
补充 : context.measureText(text)能够得到文字的宽度
五、最后的补充
1.保存与恢复状态
在之前介绍的图像裁剪当中,还有一个遗留问题,当我们裁剪过图像之后,想要取消裁剪范围继续绘制其他图形,这就需要使用Canvas API中的save和restore两个方法,这两个方法都不带参数,分别用来保存与恢复图形上下文的当前回话状态.这里的绘画状态指前面所讲的坐标原点,变形时的变换矩阵,以及图形上下文对象的当前属性值等很多内容.
在需要保存与恢复当前状态时,首先调用save方法将当前状态保存在栈中,做完想做的工作后,再调用restore从栈中取出之前保存的图形上下文的状态进行恢复.
context.save(); //保存绘画状态,之后可以进行想要的操作 // 新的操作 context.restore(); //恢复状态
当出现下面几种情况下,可以应用:
图像或图形变形
图像裁剪
改变图形上下文的以下属性时:fillStyle, font, globalAlpha, globalCompositeOperation, lineCap, lineJoin, lineWidth, miterLimit, shadowBlur, shadowColor, shadowOffsetX, shadowOffsetY, strokeStyle, textAlign, textBaseline.
2.保存文件
Canvas API保存文件的原理实际上是把当前绘画状态输出到一个data URL地址所指向的数据中的过程(其实就是图片转成的base64编码,之前的博客中有介绍);
其方法为canvas.toDataURL(type) (注:此方法为canvas画布对象的方法)
$myCanvas.toDataURL('image/jpeg');
3.动画在canvas中实际上就是不断的擦除(context.clearRect),重绘的过程,当然比较复杂的情况下,也会插入当前绘制状态的保存与恢复...
好了,写到这儿,canvas中基础的知识点就大致过了一遍,本文参考《HTML5与CSS3权威指南》一书中的canvas篇,以后会带来更多canvas实际当中的应用