1.路径、描边与填充
迄今为止,在本章之中我们所绘制的唯一图形,就是通过在Canvas的绘图环境对象上调用strokeRect()方法 所画的矩形。我们也通过调用fillRect()方法对其进行了填充。这两个方法都是立即生效的。实际上,它们是Canvas绘图环境中仅有的两个可以用 来立即绘制图形的方法(strokeText()与fillText()方法也是进行立即绘制的,但文本不算是图形)。绘图环境对象中还有一些方法,用于 绘制诸如贝塞尔曲线(bézier curve)这样更为复杂的图形,这些方法都是基于路径(path)的。
大多数绘制系统,例如Scalable Vector Graphics(可缩放向量图形,简称SVG)、Apple的Cocoa框架,以及Adobe Illustrator等,都是基于路径的。使用这些绘制系统时,你需要先定义一个路径,然后再对其进行描边(也就是绘制路径的轮廓线)或填充,也可以在 描边的同时进行填充。图2-13演示了这三种绘制方式。
图 2-13 图形的描边与填充效果
该应用程序创建了9个不同的路径,对左边一列的路径进行了描边操作,对中间一列的路径进行了填充,并对右边一列的路径同时进行描边与填充。
第一行的矩形路径与最后一行的圆弧路径都是封闭路径(closed path),而中间一行的弧形路径则是开放路径(open path)。请注意,不论一个路径是开放或是封闭,你都可以对其进行填充。当填充某个开放路径时,浏览器会把它当成封闭路径来填充。图中右边一列的中间那 个图形,就是这种效果。
程序清单2-9列出了图2-13中那个应用程序的代码。
程序清单2-9 文本、矩形与圆弧的描边及填充
var context=document.getElementById("drawingCanvas").getContext("2d"); //Functions... function drawGrid(context,color,stepx,stepy){ //Listing omitted for brevity.See Example 2.13 //for a complete listing. } //Initialization... drawGrid(context,"lightgray",10,10); //Drawing attributes... context.font="48pt Helvetica"; context.strokeStyle="blue"; context.fillStyle="red"; context.lineWidth="2"; //Line width set to 2 for text //Text... context.strokeText("Stroke",60,110); context.fillText("Fill",440,110); context.strokeText("Stroke&Fill",650,110); context.fillText("Stroke&Fill",650,110); //Rectangles... context.lineWidth="5"; //Line width set to 5 for shapes context.beginPath(); context.rect(80,150,150,100); context.stroke(); context.beginPath(); context.rect(400,150,150,100); context.fill(); context.beginPath(); context.rect(750,150,150,100); context.stroke(); context.fill(); //Open arcs... context.beginPath(); context.arc(150,370,60,0,Math.PI*3/2); context.stroke(); context.beginPath(); context.arc(475,370,60,0,Math.PI*3/2); context.fill(); context.beginPath(); context.arc(820,370,60,0,Math.PI*3/2); context.stroke(); context.fill(); //Closed arcs... context.beginPath(); context.arc(150,550,60,0,Math.PI*3/2); context.closePath(); context.stroke(); context.beginPath(); context.arc(475,550,60,0,Math.PI*3/2); context.closePath(); context.fill(); context.beginPath(); context.arc(820,550,60,0,Math.PI*3/2); context.closePath(); context.stroke(); context.fill();首先调用beginPath()方法来开始一段新的路径,rect()与arc()方法分别用于创建矩形及弧形路径。然后,应用程序在绘图环境对象上调用stroke()与fill()方法,对刚才那些路径进行描边或填充。
描边与填充操作的效果取决于当前的绘图属性,这些属性包括了lineWidth、strokeStyle、 fillStyle以及阴影属性等。比如,程序清单2-9中的这个应用程序,将lineWidth属性值设置为2,然后对文本进行描边,其后又将其重置为 5,再对路径进行描边。
由rect()方法所创建的路径是封闭的,然而,arc()方法创建的圆弧路径则不封闭,除非你用它创建的是个圆形路径。要封闭某段路径,必须像程序清单2-9中那样,调用closePath()方法才行。
表2-5总结了本应用程序中与路径相关的方法。
提示:路径与隐形墨水
有一个很恰当的比喻,可以用来说明“创建路径随后对其进行描边或填充”这个操作。我们可以将该操作比作“使用隐形墨水来绘图”。
你用隐形墨水所绘制的内容并不会立刻显示出来,必须进行一些后续操作,像是加热、涂抹化学药品、照射红外线等,才可以 将你所画的内容显示出来。如果读者关注这个话题,可以在读到所有 关于隐形墨水的知识。
使用rect()与arc()这样的方法来创建路径,就好比使用隐形墨水来进行绘制一样。这些方法会创建一条不可见的路径,稍后可以调用stroke()或fill()令其可见。
2. 路径与子路径在某一时刻,canvas之中只能有一条路径存在,Canvas规范将其称为“当前路径”(current path)。然而,这条路径却可以包含许多子路径(subpath)。而子路径,又是由两个或更多的点组成的。比方说,可以像这样绘制出两个矩形来:
context.beginPath(); //Clear all subpaths from //the current path context.rect(10,10,100,100);//Add a subpath with four points context.stroke(); //Stroke the subpath containing //four points context.beginPath(); //Clear all subpaths from the //current path context.rect(50,50,100,100);//Add a subpath with four points context.stroke(); //Stroke the subpath containing //four point以上这段代码通过调用beginPath()来开始一段新的路径,该方法会将当 前路径中的所有子路径都清除掉。然后,这段代码调用了rect()方法,此方法向当前路径中增加了一个含有4个点的子路径。最后,调用stroke()方 法,将当前路径的轮廓线描绘出来,使得这个矩形出现在canvas之中。
接下来,这段代码又一次调用了beginPath()方法,该方法清除了上一次调用rect()方法时所创建的子路径。 然后,再一次调用rect()方法,这次还是会向当前路径中增加一段含有4个点的子路径。最后,对该路径进行描边,使得第二个矩形也出现在了canvas 之中。