canvas元素是HTML5中新增的一个重要元素,专门用来绘制图形,在页面上放置一个canvas元素,就相当于在页面上放置一块“画布”,可以在其中进行图形的描绘。

canvas元素只是一块无色透明的区域。需要利用JavaScript编写在其中进行绘画的脚本。从这个角度来说,您可以理解为类似于其它开发语言中的canvas画布。

canvas元素要求至少设置width和height特性,以指定要创建的绘图区域大小。任何在起始和结束标签之间的内容是候选内容,它们当浏览器不支持<canvas>元素的时候便会显示。例如:

<canvas id="drawing" width="200" height="200">A drawing of something.</canvas>

和其它元素一样,width和height他特性也可以作为DOM元素对象的属性使用,可以在任何时刻更改。整个元素也可以使用CSS定义样式。

开始在画布上绘图之前,要先取得绘图的环境。canvas元素正式支持一个2D绘图环境。绘图环境的引用可以使用getContext()方法获取,要传入一个“2D”参数,如下:

var drawing = document.getElementById("drawing");

//确保支持<canvas> 

if (drawing.getContext) {     

var context = drawing.getContext("2d");     

//其它代码 

}

当使用canvas元素时,一定要测试getContext()方法是否存在。有些浏览器会为非正式的HTML元素创建默认的HTML元素对象。这种情况下getContext()方法就不可用了,会造成脚本执行错误。

canvas绘制长方形

2D绘图环境的原点(0,0)在元素的左上角,坐标值都是相对于该点计算的。默认情况下,width和height即宽和高,表示了在各方向上有多少个像素。

唯一的一个可以直接在2D绘图环境中绘制的图形就是长方形。长方形有三个方法:fillRect()、strokeRect()和clearRect()。三个方法都接受四个参数:长方形的x坐标、y坐标、宽和高。参数的单位是像素。

fillrect填充色彩是使用fillStyle属性指定的,它一开始等于黑色(“#000000”)。可以将该属性设置为以六位十六进制数指定的任意色彩,或者使用CSSrgb()或者rgba()格式。如下例:

var drawing = document.getElementById("drawing"); 

//确保完全支持<canvas> 

if (drawing.getContext) { 

var context = drawing.getContext("2d"); 

context.fillStyle = "#EEEEFF"; 

context.fillRect(0, 0, 200, 200); 

//绘制一个红色的长方形 

context.fillStyle = "#ff0000"; 

context.fillRect(10, 10, 50, 50); 

//绘制一个半透明的蓝色长方形 

context.fillStyle = "rgba(0, 0, 255, 0.5)"; 

context.fillRect(30, 30, 50, 50); 

}

该代码首先将fillStyle设置为红色,然后在(10,10)绘制了一个50像素高和宽的长方形。然后,又使用rgba()格式将fillStyle设为一个半透明的蓝色,并画了另一个长方形覆盖了第一个。结果可以看到红色和蓝色长方形中间接在一起。

strokeRect()方法使用由strokeStyle属性指定的颜色绘制了一个长方形外框。和fillStyle属性一样,strokeStyle默认为“#000000”,并可以使用十六进制值、rgb()或者rgba()设置。如下例:

var drawing = document.getElementById("drawing"); 

//确保完全支持<canvas> 

if (drawing.getContext) { 

var context = drawing.getContext("2d"); 

//填充画布 

context.fillStyle = "#EEEEFF"; 

context.fillRect(0, 0, 200, 200); 

//绘制一个红色的长方形边框 

context.strokeStyle = "#ff0000"; 

context.strokeRect(10, 10, 50, 50); 

//绘制一个半透明的蓝色长方形边框 

context.strokeStyle = "rgba(0, 0, 255, 0.5)"; 

context.strokeRect(20, 30, 50, 50); 

}

这段代码也绘制了两个重叠的长方形;不过都只是边框而不是填充的长方形。

画笔的大小是由lineWidth属性控制的,可以设置为任意整数。类似还有,lineCap属性描述了在线段终点所使用的形状(“butt”、“round”或者“square”),lineJoin表示线段如何连接(“round”、“bevel”或“miter”)。

可以使用clearRect()方法清除画布的一块区域。该方法用于让一块绘图环境变透明。通过绘制图形然后清除指定区域,就可以创建有趣的效果,比如切掉另一个形状的一部分。如下例:

var drawing = document.getElementById("drawing"); 

//确保完全支持<canvas> 

if (drawing.getContext) { 

var context = drawing.getContext("2d"); 

//填充画布 context.fillStyle = "#EEEEFF"; 

context.fillRect(0, 0, 200, 200); 

//绘制一个红色的长方形 context.fillStyle = "#ff0000"; 

context.fillRect(10, 10, 50, 50); 

//绘制一个半透明的蓝色长方形 

context.fillStyle = "raba(0, 0, 255, 0.5)"; 

context.fillRect(30, 30, 50, 50); 

//创建一个覆盖前面长方形的长方形 

context.clearRect(40, 40, 10, 10); 

}

这里有两个填充的相互覆盖的长方形,然后另一个小长方形清除了中间的覆盖区域。

canvas绘制路径

2D绘图环境支持一些在画布上绘制路径的方法,可以创建复杂的形状和线条。要开始创建路径,必须首先调用beginPath()表示新路经开始。然后,可以调用以下方法创建路径。

arc(x,y,radius,startAngle,endAngleanticlockwise)——绘图中心点在(x,y)的弧,半径为radius,角度在startAngle和endAngle(单位是弧度)之间。最后一个参数是一个布尔值,表示startAngle和endAngle是逆时针方向计算还是顺时针方向计算。

arcTo(x1,y1,x2,y2,radius)——绘制从上一个点到(x2,y2)的弧,经过(x1,y1),半径为radius。

bezierCurveTo(c1x,c1y,c2x,c2y,x,y)——使用控制点(c1x,c1y)和(c2x,c2y)从最后一点到点(x,y)绘制一条曲线。

lineTo(x,y)——从最后一点到点(x,y)绘制一条直线。
moveTo(x,y)——将光标移动到点(x,y)而不绘制线条。
quadraticCurveTo(xx,cy,x,y)——使用控制点(cx,cy)从最后一点绘制一条二次曲线到点(x,y)。
rect(x,y,width,height)——在点(x,y)绘制一个长width和宽height的矩形。与strokeRect()和fillRect()不一样的是,这个函数创建一个路径而不是单独的形状。

一旦创建好路径之后,还可以调用fill()方法用fillStyle填充色。另外一个选项调用stroke()方法对路径描边,使用strokeStyle()。最后一个选项是调用clip(),根据路径创建一个裁剪区域。

请看下面绘制一个钟(不带数字)的例子:

var drawing = document.getElementById("drawing"); 

//确保完全支持(canvas) 

if (drawing.getContext) {

     var context = drawing.getContext("2d");

     //填充画布

     context.fillStyle = "#EEEEFF";

     context.fillRect(0, 0, 200, 200);

     //路径开始

     context.beginPath();

     //绘制外圆

     context.arc(100, 100, 99, 0, 2 * Math.PI, false);

     //绘制内圆

     context.moveTo(194, 100);

     context.arc(100, 100, 94, 0, 2 * Math.PI, false);

     //绘制分针

     context.moveTo(100, 100);

     context.lineTo(100, 15);

     //绘制时针

     context.moveTo(100, 100);

     context.lineTo(35, 100);

     //路径描边

     context.stroke(); 

}

这个例子使用arc()绘制了两个圆:内外各一,用于创建钟的边框。外圆半径为99像素,中心在(100,100),也就是画布的中心。要绘制一个完整的圆,必须起始于0度角,画整一圈至2π个弧度(使用Math.PI进行计算)。在绘制内圆之前,路径必须移动到一个圆所在的新点,以面绘制额外的线条。第二次调用arc()使用了缩小的半径以到达边框效果。之后,组合使用moveTo()和lineTo()绘制了时针和分针。最后一步是调用stroke(),让图形显示。

路径是2D绘图环境的主要绘制机制,因为它们让开发人员能更好地控制要画什么。由于路径使用频繁,还有个方法叫做isPointInPath(),接受一个x坐标和y坐标作为参数。该方法可以在路径关闭之前任意时间点调用,来判断某个点是否在于路径之上,如下所示:

if (context.isPointInPath(100, 100)) {

     alert("Point(100, 100) is in the path."); 

}

2D绘图环境的路径API十分强健,足够使用多种填充样式、画笔样式和其它样式创建复杂的图形。

cnavas绘制文本

由于常常需要混合文本和图形,2D绘图环境提供了绘制文本的方法。有两个绘制文本的方法:fillText()和strokeText(),两者都接受4个参数:要绘制的字符串、X坐标、Y坐标和可选的最大像素度。两者都根据一下3个属性进行绘制。

font——表示字体样式、大小和族,格式和CSS中一样,如“10pxArial”。
textAlign——表示文本应如何对其。可能的值有:“start”、“end”、“left”、“right”和“center”。
textBaseline——表示文本的基线位置。可能的值有:“top”、“hanging”、“middle”、“alphabetic”、“ideographic”和“bottom”。

各个属性都有一个默认的值,所以并非每次绘制文本都要对它进行设置。fillText()方法使用fillStyle()属性绘制文本,而strokeText()方法使用strokeStyle属性。大多数时候可能是使用fillText(),因为它能在网页上模仿普通的文本呈现。例如,在上一篇文章实例中为闹钟添加文字“12”:

var drawing = document.getElementById("drawing"); 

//确保完全支持<canvas> 

if (drawing.getContext) {

     var context = drawing.getContext("2d");

     //填充画布

     context.fillStyle = "#EEEEFF";

     context.fillRect(0, 0, 200, 200);

     //路径开始

     context.beginPath();

     //绘制外圆

     context.arc(100, 100, 99, 0, 2 * Math.PI, false);

     //绘制内圆

     context.moveTo(194, 100);

     context.arc(100, 100, 94, 0, 2 * Math.PI, false);

     //绘制分针

     context.moveTo(100, 100);

     context.lineTo(100, 15);

     //绘制时针

     context.moveTo(100, 100);

     context.lineTo(35, 100);

     //路径描边

     context.stroke();

     //绘制文本

     context.font = "bold 10px Arial";

     context.textAlign = "center";

     context.textBaseline = "midddle";

     context.fillText("12", 100, 18); 

}

文本绘制是比较复杂的一个绘图操作,所以,尚未在支持<canvas>元素的主要浏览器中实现。

canvas变换

环境变换允许对绘制在画布上的图像执行操作。2D绘图环境支持所有基本的绘图变换。当创建了绘图环境,,变形矩阵便已经初始化了默认值,可以让所有的绘图操作直接按照所描述的那样直接呈现。对绘图环境进行变换,可以让绘图操作应用一个不同的变换矩阵并产生一个不同的结果。

变换矩阵可以使用以下方法扩张。

rotate(angle)——图像绕原点旋转一定的弧度。
scale(scaleX,scaleY)——缩放图像,在x轴放大scaleX,Y轴放大scaleY。scaleX和scaleY的默认值都是1.0。
translate(x,y)——将原点移动到点(x,y)。该该操作之后,坐标(0,0)将位于点(x,y)。
transform(m1_1,m2_1,m2_1,m2_2,dx,dy)——将变换矩阵乘上以下矩阵:
setTransform(m1_1,m1_2,m2_1,m2_2,dx,dy)——重置变换矩阵到默认状态,然后调用transform()。

变换可以根据需要简单或复杂。比如,如果能将原点变换到钟的中心,那么绘制钟的指针就更加容易了。如下:

var drawing = document.getElementById("drawing"); 

//确保完全支持<canvas>

if (drawing.getContext) {

     var context = drawing.getContext("2d");

     //填充画布

     context.fillStyle = "#EEEEFF";

     context.fillRect(0, 0, 200, 200);

     //路径开始

     context.beginPath();

     //绘制外圆

     context.arc(100, 100, 99, 0, 2 * Math.PI, false);

     //绘制内圆

     context.moveTo(194, 100);

     context.arc(100, 100, 94, 0, 2 * Math.PI, false);

     //转换到中心

     context.translate(100, 100);

     //绘制分针

     context.moveTo(0, 0);

     context.lineTo(0, -85);

     //绘制时针

     context.moveTo(0, 0);

     context.lineTo(-65, 0);

     //路径描边

     context.stroke(); 

}

在转换原点到钟的中心(100,100)之后,绘制同一个方向上的直线就是简单的算数问题了。现在的计算都是基于(0,0)而不是(100,100)。更进一步,可以如下是使用rotate()方法旋转钟的指针:

var drawing = document.getElementById("drawing");

//确保完全支持<canvas> 

if (drawing.getContext) {

     var context = drawing.getContext("2d");

     //路径开始

     context.beginPath();

     //绘制外圆

     context.arc(100, 100, 99, 0, 2 * Math.PI, false);

     //绘制内圆

     context.moveTo(194, 100);

     context.arc(100, 100, 94, 0, 2 * Math.PI, false);

     //转换到中心

     context.translate(100, 100);

     //旋转指针

     context.rotate(1);

     //绘制分针

     context.moveTo(0, 0);

     context.lineTo(0, -85);

     //绘制时针

     context.moveTo(0, 0);

     context.lineTo(-65, 0);

     //路径描边

     context.stroke(); 

}

由于原点已经转移到了钟的中心,所以旋转是根据点进行的。也就是说,两个指针都以中心为锚并顺时针旋转。

所有这些变换(包括像fillStyle和strokeStyle之类的属性)都保持环境设置直到显式更改。虽然没有明确的重置它们的方式,但有两个方法可以帮助追踪变化。一旦想要能回退到特定的属性和变换集合,可以调用save()方法。调用这个方法之后,当前所有的设置都会推到一个栈中保存,然后可以继续对环境进行其他更改。当你想回到之前的设置,可以调用restore(),它会从设置栈上弹出设置并恢复它。你可以不断调用save()存储更多的设置,然后系统地调用restore()回退。例子:

var drawing = document.getElementById("drawing"); 

//确保完全支持<canvas> 

if (drawing.getContext) {

     var context = drawing.getContext("2d");

     //填充画布

     context.fillStyle = "#EEEEFF";

     context.fillRect(0, 0, 200, 200);

     context.fillStyle = "#ff0000";

     context.save();

     context.fillStyle = "#00ff00";

     context.translate(100, 100);

     context.save();

     context.fillStyle = "#0000ff";

     context.fillRect(0, 0, 100, 200); //在(100,100)绘制一个蓝色的矩形

     context.restore();

     context.fillRect(10, 10, 100, 200); //在(100, 100)处绘制一个绿色的矩形

     context.restore();

     context.fillRect(0, 0, 100, 200); //在(0,0)处绘制一个红色的矩形 

}

这段代码中,fillStyle设成了红,然后调用了save()。接下来,fillStyle变成了绿色,然后坐标有移动到了(100,100)。然后再次调用save()来保存这些设置。

然后fillStyle属性有被设置成了蓝色并绘制了一个矩形。因为坐标移动了,实际上最终矩形是画在(100,100)。当调用了restore()之后,fillStyle()有设回了绿色,所以接下来画的矩形是绿色的。矩形实际是画在(110,110)处,因为坐标转换仍然有效。当再次调用了restore()之后,取消了坐标转换并且fillStyle设回了红色。最后的矩形是画在(0,0)处的。

注意save()只保存应用于绘图环境上的设置的变换,不包括绘图环境的内容。

canvas使用图片

2D绘图环境对图片操作有一些内置的支持。如果以一个现成的图片要画在画布上,可以调用drawImage()方法。根据所需结果,可以用三套参数调用这个方法。最简单的是传入一个HTML<img>元素,以及目标x坐标和y坐标,可以在指定位置绘制图像。如下:

var images = document.getElementsByTagName("image"); 

var image = document.images[0]; 

context.drawImage(image, 10 ,10);

这段代码取得了文档中的第一个图像,并显示在环境中的位置(10,10)处,还可以通过增加两个参数改变绘制图像的方式:目标宽和高。它能缩放图像而不会影响变换矩阵。例如:

context.drawImge(image, 50, 10, 20, 30);

当执行这段代码时,图像缩放成了20像素和30像素高。

还可以值选择图像的一部分显示在环境中。这要给drawImage()提供九个参数:要绘制的图像、源图像的x坐标、源图像的y坐标、源图像的宽、源图像的高、目标图像的x坐标、目标图像的y坐标、目标图像的宽、目标图像的高。使用drawImage()这个重载可以获得最多的控制权。看这个例子:

context.drawImage(image, 0, 10, 50, 50, 0, 100, 40, 60);

这里,图像只有一部分被显示在了画布上。这部分图像起始于(10,10),50像素宽50像素高。图像在环境(10,10)处缩放为40×60显示。