DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> HTML基礎知識 >> HTML5詳解 >> Canvas繪圖中的路徑描邊與填充
Canvas繪圖中的路徑描邊與填充
編輯:HTML5詳解     

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總結了本應用程序中與路徑相關的方法。

 

提示:路徑與隱形墨水

有一個很恰當的比喻,可以用來說明“創建路徑隨後對其進行描邊或填充”這個操作。我們可以將該操作比作“使用隱形墨水來繪圖”。

你用隱形墨水所繪制的內容並不會立刻顯示出來,必須進行一些後續操作,像是加熱、塗抹化學藥品、照射紅外線等,才可以 將你所畫的內容顯示出來。如果讀者關注這個話題,可以在http://en.wikipedia.org/wiki/Invisible_ink讀到所有 關於隱形墨水的知識。

使用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 之中。

現在考慮一下,如果將第二個beginPath()調用去掉,會怎麼樣呢?像是這樣:

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.rect(50,50,100,100);//Add a second subpath with
//four points
context.stroke();
//Stroke both subpaths

面這段代碼在一開始與剛才那段是一樣的:先調用beginPath()來清除當前路徑中的所有子路徑,然後調用rect()來創建一條包含矩形4個點的子路徑,再調用stroke()方法使得這個矩形出現在canvas之上。

接下來,這段代碼再次調用了rect()方法,不過這一次,由於沒有調用beginPath()方法來清除原有的子路 徑,所以第二次對rect()方法的調用,會向當前路徑中增加一條子路徑。最後,該段代碼再一次調用stroke()方法,這次對stroke()方法的 調用,將會使得當前路徑中的兩條子路徑都被描邊,這意味著它會重繪第一個矩形。

填充路徑時所使用的“非零環繞規則”

如果當前路徑是循環的,或是包含多個相交的子路徑,那麼Canvas的繪圖環境變量就必須要判斷,當fill()方法被 調用時,應該如何對當前路徑進行填充。Canvas在填充那種互相有交叉的路徑時,使用“非零環繞規則”(nonzero winding rule)來進行判斷。圖2-14演示了該規則的運用。

“非零環繞規則”是這麼來判斷有自我交叉情況的路徑的:對於路徑中的任意給定區域,從該區域內部畫一條足夠長的線段,使此線段的終點完全落在路徑范圍之外。圖2-14中的那三個箭頭所描述的就是上面這個步驟。

圖 2-14 在填充路徑時運用“非零環繞規則”來進行判斷

接下來,將計數器初始化為0,然後,每當這條線段與路徑上的直線或曲線相交時,就改變計數器的值。如果是與路徑的順時針 部分相交,則加1,如果是與路徑的逆時針部分相交,則減1。若計數器的最終值不是0,那麼此區域就在路徑裡面,在調用fill()方法時,浏覽器就會對其 進行填充。如果最終值是0,那麼此區域就不在路徑內部,浏覽器也就不會對其進行填充了。

可以從圖2-14中看出“非零環繞規則”是如何運用的。左邊的那個帶箭頭的線段,先穿過了路徑的逆時針部分,然後又穿過 了路徑的順時針部分。這意味著其計數值是0,所以該線段起點所在的那個區域就不在范圍內,在調用fill()方法時,浏覽器也就不會對其進行填充。然而, 其余兩條帶箭頭的線段,其計數值都不是0,所以它們的起點所在的區域就會被浏覽器填充。

3. 剪紙效果

我們來運用一下所學到的路徑、陰影以及非零環繞原則等知識,實現如圖2-15所示的剪紙(cutout)效果。

圖 2-15 用兩個圓形做出的剪紙效果

圖2-15所示的應用程序,其JavaScript代碼列在了程序清單2-10之中。

這段JavaScript代碼創建了一條路徑,它由兩個圓形所組成,其中一個圓形在另一個的內部。通過設定arc()方法的最後一個參數值,該應用程序以順時針方向繪制了內部的圓形,並且以逆時針方向繪制了外圍的圓形。繪制效果如圖2-16上方所示。

圖 2-16 運用“非零環繞規則”來實現剪紙效果

在創建好路徑之後,圖2-15中的那個應用程序對該路徑進行了填充。浏覽器運用“非零環繞規則”,對外圍圓形的內部進行了填充,不過填充的范圍並不包括裡面的圓,這就產生了一種剪紙圖案的效果。你也可以利用此技術來剪出任意想要的形狀來。

程序清單2-10 圖2-15所示應用程序的JavaScript代碼

var context=document.getElementById("canvas").getContext("2d");
//Functions...
function drawGrid(color,stepx,stepy){
//Listing omitted for brevity.See Example 2.13
//for a complete listing.
function drawTwoArcs(){
context.beginPath();
context.arc(300,190,150,0,Math.PI*2,false);//Outer:CCW
context.arc(300,190,100,0,Math.PI*2,true);//Inner:CW
context.fill();
context.shadowColor=undefined;
context.shadowOffsetX=0;
context.shadowOffsetY=0;
context.stroke();
function draw(){
context.clearRect(0,0,context.canvas.width,
context.canvas.height);
}
}
drawGrid("lightgray",10,10);
context.save();
context.shadowColor="rgba(0,0,0,0.8)";
context.shadowOffsetX=12;
context.shadowOffsetY=12;
context.shadowBlur=15;
drawTwoArcs();
context.restore();
}
//Initialization...
context.fillStyle="rgba(100,140,230,0.5)";
context.strokeStyle=context.fillStyle;
draw();
XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved