1.Canvas概述 Canvas API(畫布)用於在網頁實時生成圖像,並且可以操作圖像內容,基本上它是一個可以用JavaScript操作的點陣圖(bitmap)。 要使用HTML5在瀏覽器視窗中繪製圖形,首先需要在HTML文檔中新建一個canvas網頁元素。一般方法如下: <canvas id=" ...
1.Canvas概述
Canvas API(畫布)用於在網頁實時生成圖像,並且可以操作圖像內容,基本上它是一個可以用JavaScript操作的點陣圖(bitmap)。
要使用HTML5在瀏覽器視窗中繪製圖形,首先需要在HTML文檔中新建一個canvas網頁元素。一般方法如下:
<canvas id="myCanvas" width="400" height="300">
您的瀏覽器不支持canvas!
</canvas>
上面這段代碼,表示建立了一個名為“myCanvas”的canvas網頁元素,它就是一塊畫布,該畫布的寬為400,高為300。有了這塊畫布,我們就可以使用JavaScript編寫程式,利用Canvas API在這塊畫布上繪製圖形。如果所用瀏覽器不支持Canvas API,則就會顯示canvas標簽中間的文字——“您的瀏覽器不支持canvas!”。
每個canvas網頁元素都有一個對應的context對象(上下文對象),Canvas API定義在這個context對象上面。為了在canvas上繪製圖形,必須先得到一個畫布上下文對象的引用。為此,使用JavaScript編寫程式段如下:
var canvas = document.getElementById('myCanvas'); // 取得網頁中的畫布對象
var ctx = canvas.getContext('2d'); // 得到畫布上下文對象ctx
上面代碼中,getContext方法指定參數2d,表示該canvas對象用於生成2D圖案(即平面圖案)。如果參數是3d,就表示用於生成3D圖像(即立體圖案)。
當使用一個canvas元素的getContext(“2d”)方法時,返回的是CanvasRenderingContext2D對象,其內部表現為笛卡爾平面坐標。這就是Canvas畫布提供的一個用來作圖的平面空間,該空間的每個點都有自己的坐標,x表示橫坐標,y表示縱坐標。原點(0, 0)位於畫布左上角,x軸的正向是原點向右,y軸的正向是原點向下。
每一個canvas元素僅有一個上下文對象。得到了這個上下文對象,就可以利用這個對象的屬性和方法進行圖形繪製了。
2.繪圖方法
2.1 繪製路徑
在Canvas API中,上下文CanvasRenderingContext2D對象提供了一系列與圖形繪製相關的屬性和方法。其中,與路徑繪製相關的方法如下:
void beginPath(); // 開始繪製路徑
void closePath(); // 結束路徑繪製
void moveTo(in float x, in float y); // 設置線段的起點
void lineTo(in float x, in float y); // 設置線段的終點
void bezierCurveTo(in float cp1x, in float cp1y, in float cp2x, in float cp2y, in float x, in float y); // 繪製一條三次貝塞爾曲線
void quadraticCurveTo(in float cpx, in float cpy, in float x, infloat y); // 繪製一條二次貝塞爾曲線
void stroke(); // 給透明的線段著色,從而完成線段繪製
void fill(); // 給閉合路徑填充顏色,填充色由fillStyle屬性指定
與繪製路徑相關的屬性有:
attribute float lineWidth; // 線的寬度,預設為1
attribute any strokeStyle; // 著色的顏色,預設為black(黑色)
attribute any fillStyle; // 填充顏色,預設為black(黑色)
attribute DOMString lineCap; // 線段的箭頭樣式,僅有三個選項:butt(預設值)、round、square,其他值忽略
下麵通過幾個例子來說明繪製路徑的方法和屬性的使用。
例1 繪製一條從(20,20)到(200,20)的一條紅色橫線。
<!DOCTYPE html>
<head>
<title>繪圖方法的使用</title>
<script type="text/javascript">
function draw(id)
{
var canvas=document.getElementById(id);
if (canvas==null)
return false;
var ctx=canvas.getContext('2d');
ctx.beginPath(); // 開始路徑繪製
ctx.moveTo(20, 20); // 設置路徑起點,坐標為(20,20)
ctx.lineTo(200, 20); // 繪製一條到(200,20)的直線
ctx.lineWidth = 1.0; // 設置線寬
ctx.strokeStyle = "#FF0000"; // 設置線條顏色為紅色
ctx.stroke(); // 進行線的著色,這時整條線才變得可見
}
</script>
</head>
<body onload="draw('myCanvas');">
<canvas id="myCanvas" width="400" height="300" style="border:3px double #996633;">
</canvas>
</body>
</html>
將上述HTML代碼保存到一個html文本文件中,再在瀏覽器中打開包含這段HTML代碼的html文件,可以看到在畫布中繪製出一條長為180的紅色橫線。
下麵的例子中我們不再給出完整的HTML文件內容,只給出JavaScript編寫的與圖形繪製直接相關的代碼。例如例1源文件中加了註釋的6條語句。讀者需要自己試一試時,只需把下列各例給出的代碼去覆蓋例1中的6條註釋語句,其餘部分保持不變即可。
在繪製路徑時,moveto和lineto方法可以多次使用。最後,還可以使用closePath方法,自動繪製一條當前點到起點的線段,形成一個封閉圖形,省卻使用一次lineto方法。
例2 在畫布中繪製一個紅色邊框的直角三角形和一個藍色邊框的等腰三角形。
ctx.beginPath();
ctx.moveTo(20,20);
ctx.lineTo(20,100); // 垂直直角邊
ctx.lineTo(70,100); // 水平直角邊
ctx.lineTo(20,20); // 斜邊
ctx.strokeStyle="red";
ctx.stroke(); // 進行著色,使得線段可見
ctx.beginPath();
ctx.moveTo(40,120);
ctx.lineTo(20,180); // 左邊的腰
ctx.lineTo(60,180); // 底邊
ctx.closePath(); // 右邊的腰是通過自動封閉繪製得到
ctx.strokeStyle="blue";
ctx.stroke();
例3 線段箭頭的三種樣式的比較。
ctx.lineWidth=10;
ctx.strokeStyle="red";
ctx.beginPath();
ctx.lineCap='butt';
ctx.moveTo(100,50);
ctx.lineTo(250,50);
ctx.stroke();
ctx.beginPath();
ctx.lineCap='round';
ctx.moveTo(100,80);
ctx.lineTo(250,80);
ctx.stroke();
ctx.beginPath();
ctx.lineCap='square';
ctx.moveTo(100,110);
ctx.lineTo(250,110);
ctx.stroke();
在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖1所示的三條線段。其中,第1根線段箭頭樣式為“butt”(為預設值),線段的頭和尾都是長方形,也就是不做任何的處理;第2根線段箭頭樣式為“round”,線段的頭和尾都增加一個半圓形的箭頭;第3根線段的樣式為“square”,線段的頭和尾都增加一個長方形,長度為線寬一半,高度為線寬。
圖1 繪製的3根紅色線段
例4 繪製紅綠藍3個實心三角形。
ctx.beginPath();
ctx.moveTo(20,20);
ctx.lineTo(20,100);
ctx.lineTo(70,100);
ctx.lineTo(20,20);
ctx.fillStyle="red";
ctx.fill(); // 填充紅色三角形
ctx.beginPath();
ctx.moveTo(40,120);
ctx.lineTo(20,180);
ctx.lineTo(60,180);
ctx.closePath();
ctx.fillStyle="green";
ctx.fill(); // 填充綠色三角形
ctx.beginPath();
ctx.moveTo(70,20);
ctx.lineTo(120,180);
ctx.lineTo(140,150);
ctx.fillStyle="blue";
ctx.fill(); // 填充藍色三角形
在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖2所示的三個實心三角形。
圖2 三個實心三角形
通過上面的示例,我們可以知道,填充的形狀應該是封閉的路徑。如果路徑未關閉,那麼 fill() 方法會從路徑結束點到開始點之間添加一條線,以關閉該路徑,然後填充該路徑。例如,圖2中藍色三角形構成的路徑並未關閉,調用fill()時,會自動添加直線關閉。
貝賽爾曲線(Bezier curve)是電腦圖形學中相當重要的參數曲線。Canvas API中提供了兩個繪製貝塞爾曲線的方法。其中:
quadraticCurveTo() 方法用於繪製一條二次貝塞爾曲線。
二次貝塞爾曲線需要兩個點。第一個點(cpx,cpy)是用於二次貝塞爾計算中的控制點,第二個點(x,y)是曲線的結束點。曲線的開始點是當前路徑中最後一個點。如果路徑不存在,需要使用 beginPath() 和 moveTo() 方法來定義開始點。
bezierCurveTo() 方法用於繪製一條三次貝塞爾曲線。
三次貝塞爾曲線需要三個點。前兩個點(cp1x,cp1y)和(cp2x,cp2y)是用於三次貝塞爾計算中的控制點,第三個點(x,y)是曲線的結束點。曲線的開始點是當前路徑中最後一個點。如果路徑不存在,同樣需要使用 beginPath() 和 moveTo() 方法來定義開始點。
例5 繪製一條二次貝塞爾曲線和一條三次貝塞爾曲線。
ctx.beginPath();
ctx.moveTo(20,20);
ctx.quadraticCurveTo(20,100,200,20);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(20,120);
ctx.bezierCurveTo(20,220,200,180,200,120);
ctx.stroke();
在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖3所示的兩條貝塞爾曲線。
圖3 兩條貝塞爾曲線
例6 使用多個貝塞爾曲線來繪製一個對話氣泡。
ctx.beginPath();
ctx.moveTo(75,25);
ctx.quadraticCurveTo(25,25,25,62.5);
ctx.quadraticCurveTo(25,100,50,100);
ctx.quadraticCurveTo(50,120,30,125);
ctx.quadraticCurveTo(60,120,65,100);
ctx.quadraticCurveTo(125,100,125,62.5);
ctx.quadraticCurveTo(125,25,75,25);
ctx.stroke();
在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖4所示的對話氣泡。
圖4 對話氣泡
例7 使用多個貝塞爾曲線來繪製一個紅心。
ctx.fillStyle="red";
ctx.beginPath();
ctx.moveTo(75,40);
ctx.bezierCurveTo(75,37,70,25,50,25);
ctx.bezierCurveTo(20,25,20,62.5,20,62.5);
ctx.bezierCurveTo(20,80,40,102,75,120);
ctx.bezierCurveTo(110,102,130,80,130,62.5);
ctx.bezierCurveTo(130,62.5,130,25,100,25);
ctx.bezierCurveTo(85,25,75,37,75,40);
ctx.fill();
在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖5所示的紅心圖案。
圖5 紅心
例8 通過迴圈繪製一個9行9列的棋盤。
ctx.strokeStyle="red";
ctx.lineWidth=3;
ctx.beginPath();
for (i=50;i<=450;i+=50)
{
ctx.moveTo(i,50);
ctx.lineTo(i,450);
ctx.moveTo(50,i);
ctx.lineTo(450,i);
}
ctx.stroke();
為顯示完整的棋盤,請將畫布的寬和高均設置為500。在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖6所示的棋盤。
圖6 棋盤
2.2 繪製矩形
在Canvas API中,上下文CanvasRenderingContext2D對象提供的與矩形繪製相關的方法如下:
void rect(in float x, in float y, in float w, in float h); // 建立一個矩形路徑
void clearRect(in float x, in float y, in float w, in float h); // 清除給定矩形區域的內容
void fillRect(in float x, in float y, in float w, in float h); // 填充給定的矩形區域
void strokeRect(in float x, in float y, in float w, in float h); // 繪製給定的矩形邊框
這個幾個方法中給定的四個參數分別為矩形左上角頂點的x坐標、y坐標,以及矩形的寬w和高h。
例9 繪製紅綠藍三個矩形邊框。
ctx.beginPath();
ctx.lineWidth="8";
ctx.strokeStyle="red";
ctx.rect(15,15,150,150);
ctx.stroke(); // 繪製紅色矩形
ctx.beginPath();
ctx.lineWidth="3";
ctx.strokeStyle="#00FF00";
ctx.strokeRect(30,30,40,50); // 繪製綠色矩形
ctx.beginPath();
ctx.lineWidth="8";
ctx.strokeStyle="blue";
ctx.moveTo(80,50);
ctx.lineTo(80,120);
ctx.lineTo(140,120);
ctx.lineTo(140,50);
ctx.closePath();
ctx.stroke(); // // 繪製藍色矩形
在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖7所示的三個矩形。
圖7 三個矩形邊框
例10 繪製一個邊長為100的正方形,邊框採用藍色,內部用紅色填充。
ctx.fillStyle="red";
ctx.strokeStyle="blue";
ctx.lineWidth=2;
ctx.fillRect(50,50,100,100);
ctx.strokeRect(50,50,100,100);
在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖8所示的正方形。
圖8 正方形
在沒有進行坐標旋轉的情況下,採用rect()和strokeRect()方法繪製的矩形一定是兩條邊與x軸平行,兩條邊與y軸平行。若要繪製與坐標軸不平行的矩形,可以採用繪製4條線的方法完成。
例11 繪製矩形中的矩形。要求矩形里的矩形其頂點在外面矩形的中點上。
var x = [50,50,250,250];
var y = [50,250,250,50];
ctx.strokeStyle="red";
ctx.lineWidth=3;
for (i=1;i<=4;i++)
{
ctx.beginPath();
ctx.moveTo(x[0],y[0]);
for (k=1;k<=3;k++)
ctx.lineTo(x[k],y[k]);
ctx.closePath();
ctx.stroke();
var tx=x[0];
var ty=y[0];
for (k=0;k<3;k++)
{
x[k]=(x[k]+x[k+1])/2;
y[k]=(y[k]+y[k+1])/2;
}
x[3]=(tx+x[3])/2;
y[3]=(ty+y[3])/2;
}
在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖9所示的正方形。
圖9 正方形中的正方形
例12 繪製國際象棋棋盤。
for (i=0;i<8;i++)
{
for (j=0;j<8;j++)
{
if ((i+j)%2==0)
ctx.fillStyle = 'black';
else
ctx.fillStyle= 'white';
ctx.fillRect(j*50,i*50,50,50);
}
}
為顯示完整的棋盤,請將畫布的寬和高均設置為400。在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖10所示的國際象棋棋盤。
圖10 國際象棋棋盤
例13 根據給定數據繪製柱狀圖。
var data = [100, 50, 20, 30, 100];
var colors = [ "red","orange", "yellow","green", "blue"];
ctx.fillStyle = "white";
ctx.fillRect(0,0,canvas.width,canvas.height);
for(var i=0; i<data.length; i++)
{
var dp = data[i];
ctx.fillStyle = colors[i];
ctx.fillRect(25+i*50, 280-dp*2, 50, dp*2);
}
在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖11所示的柱狀圖。
圖11 柱狀圖
2.3 繪製圓形和扇形
在Canvas API中,上下文CanvasRenderingContext2D對象提供的與圓形和弧等繪製相關的方法如下:
void arc(in float x, in float y, in float radius, in float startAngle, in float endAngle, in boolean anticlockwise);
void arcTo(in float x1, in float y1, in float x2, in float y2, in float radius);
其中,arc方法用來繪製扇形。參數x和y是圓心坐標,radius是半徑,startAngle和endAngle則是扇形的起始角度和終止角度(以弧度表示),anticlockwise表示作圖時應該逆時針畫(true)還是順時針畫(false)。
arcTo() 方法用於在畫布上創建介於兩個切線之間的弧/曲線。繪製出子路徑最後一個點(x0,y0)和(x1,y1)以及(x1,y1)和(x2,y2)構成的兩條直線間半徑為radius的最短弧線,並用直線連接(x0,y0)
例14 繪製圓弧。
ctx.strokeStyle="red";
ctx.fillStyle="orange";
for(var i=0;i<3;i++)
{
for(var j=0;j<4;j++)
{
ctx.beginPath();
x = 50+j*100;
y = 50+i*100;
radius = 45;
startAngle = 0;
endAngle = Math.PI/2+(Math.PI*j)/2;
anticlockwise = i%2==0 ? false : true;
ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
if (i>1)
ctx.fill();
else
ctx.stroke();
}
}
在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖12所示的圓弧。
圖12 圓弧及填充
通過這個例子還可以加深理解:當調用fill()函數時,所有沒有閉合的形狀都會自動閉合,因此可以不調用closePath()函數。但是調用stroke()時不會自動閉合。
例15 繪製9個大小不一的圓。
ctx.fillStyle="red";
ctx.lineWidth=1;
for (var i=1;i<10;i++)
{
ctx.beginPath();
ctx.arc(i*20,i*20,i*10,0,Math.PI*2,true);
ctx.closePath();
ctx.fillStyle='rgba(255,0,0,0.25)';
ctx.fill();
}
在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖13所示的圖形。
圖13 逐漸放大的圓
例16 根據給定數據繪製餅圖。
var data = [100, 50, 20, 30, 100];
ctx.fillStyle = "white";
ctx.fillRect(0,0,canvas.width,canvas.height);
var colors = [ "red","orange", "yellow","green", "blue"];
var total = 0;
for(var i=0; i<data.length; i++)
total += data[i];
var prevAngle = 0;
for(var i=0; i<data.length; i++)
{
var fraction = data[i]/total;
var angle = prevAngle + fraction*Math.PI*2;
ctx.fillStyle = colors[i];
ctx.beginPath();
ctx.moveTo(150,150);
ctx.arc(150,150, 100, prevAngle, angle, false);
ctx.lineTo(150,150);
ctx.fill();
ctx.strokeStyle = "black";
ctx.stroke();
prevAngle = angle;
}
在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖14所示的餅圖。
圖14 餅圖
2.4 繪製文本
在Canvas API中,上下文CanvasRenderingContext2D對象提供的與文本繪製相關的方法如下:
void fillText(in DOMString text, in float x, in float y, optionalin float maxWidth);
void strokeText(in DOMString text, in float x, in float y, optionalin float maxWidth);
這兩個方法用來繪製文本,它的前三個參數分別為文本內容、起點的x坐標、y坐標。其中:fillText方法為繪製填充的文字;strokeText方法為對文字進行描邊,不填充內部區域,通常用來添加空心字。
與文本相關的屬性有:
attribute DOMString font; // 設置字體,預設為10px sans-serif
attribute DOMString textAlign; // 設置對齊方式,有"start", "end", "left", "right", "center"等,預設為"start"
attribute DOMString textBaseline; //設置文字對齊基線,有"top", "hanging", "middle", "alphabetic", "ideographic", "bottom" 等取值,預設為”alphabetic"
例17 在畫布上添加兩行文字。
ctx.font = "Bold 50px 隸書";
ctx.fillStyle = "Black";
ctx.fillText("我們是中國人", 10, 50);
ctx.strokeText("我們熱愛我們的祖國", 10, 150);
在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖15所示的文本。
圖15 文本繪製
註意:fillText方法不支持文本斷行,即所有文本出現在一行內。所以,如果要生成多行文本,只有調用多次fillText方法。
例18 繪製包含數據說明的柱狀圖。
var data = [100, 50, 20, 30, 100];
var colors = [ "red","orange", "yellow","green", "blue"];
ctx.fillStyle = "white";
ctx.fillRect(0,0,canvas.width,canvas.height);
for(var i=0; i<data.length; i++)
{
var dp = data[i];
ctx.fillStyle = colors[i];
ctx.fillRect(25+i*50, 280-dp*2, 50, dp*2);
}
ctx.fillStyle = "black";
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(25,10);
ctx.lineTo(25,280);
ctx.lineTo(290,280);
ctx.stroke();
ctx.fillStyle = "black";
for(var i=0; i<6; i++)
{
ctx.fillText((5-i)*20 + "",4, i*40+80);
ctx.beginPath();
ctx.moveTo(25,i*40+80);
ctx.lineTo(30,i*40+80);
ctx.stroke();
}
var labels = ["JAN","FEB","MAR","APR","MAY"];
for(var i=0; i<5; i++)
ctx.fillText(labels[i], 40+ i*50, 290);
在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖16所示的包含數據說明的柱狀圖。
圖16 包含數據說明的柱狀圖
還可以為文本等設置陰影。
例19 為文本設置陰影。
ctx.shadowOffsetX = 3; // 設置水平位移
ctx.shadowOffsetY = 3; // 設置垂直位移
ctx.shadowBlur = 2; // 設置模糊度
ctx.shadowColor = "rgba(0, 0, 0, 0.5)"; // 設置陰影顏色
ctx.font = "50px 宋體";
ctx.fillStyle = "Black";
ctx.fillText("我們是中國人", 10, 50);
在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖17所示的文字陰影效果。
圖17 文字陰影
2.5 圖形剪切
在Canvas API中,上下文CanvasRenderingContext2D對象提供了一個用於圖形剪切的方法。
void clip();
剪切(clip)路徑和普通的 canvas 圖形差不多,不同的是它的作用是遮罩,用來隱藏沒有遮罩的部分,如圖18所示。紅邊五角星就是裁切路徑,所有在路徑以外的部分都不會在 canvas 上繪製出來。預設情況下,canvas 有一個與它自身一樣大的剪切路徑(也就是沒有剪切效果)。
圖18 剪切示意圖
例20 一個簡單的剪切示例。
ctx.fillStyle = 'red';
ctx.fillRect(0,0,400,300);
ctx.beginPath();
ctx.moveTo(200,50);
ctx.lineTo(100,250);
ctx.lineTo(300,250);
ctx.closePath();
ctx.lineWidth = 10;
ctx.stroke();
ctx.clip();
ctx.fillStyle = 'yellow';
ctx.fillRect(0,0,400,150);
在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖19所示的三角形剪切。
圖19 三角形剪切
在這個例子中,先畫了一個與 canvas 一樣大小(寬400,高300)的紅色方形作為背景,然後用 clip方法創建一個三角形的剪切路徑。剪切路徑創建之後,所有出現在它裡面的東西才會畫出來。這樣在其後繪製高度為畫布一半的矩形填充時,只有三角形剪切路徑裡面的內容才會繪製出來。
設定了剪切區域之後,無論在Canvas上繪製什麼,只有落在剪切區域內的那部分才能得以顯示,其餘都會被遮蔽掉。
例21 cilp方法的進一步理解。
// 繪製第一個圓
ctx.beginPath();
ctx.fillStyle = 'red';
ctx.arc(200, 100, 100, 0, Math.PI * 2, false);
ctx.fill();
// 繪製第二個圓
ctx.beginPath();
ctx.fillStyle = 'blue';
ctx.arc(100, 150, 100, 0, Math.PI * 2, false);
ctx.fill();
// 繪製第三個圓
ctx.beginPath();
ctx.fillStyle = 'green';
ctx.arc(300, 150, 100, 0, Math.PI * 2, false);
ctx.fill();
// 繪製第四個圓
ctx.beginPath();
ctx.fillStyle = 'brown';
ctx.arc(200, 200,100, 0, Math.PI * 2, false);
ctx.fill();
ctx.lineWidth = 10;
ctx.strokeStyle='black';
ctx.stroke();
在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖20所示的4個圓。
圖20 沒有使用clip方法的4個圓
若在第3個圓繪製後插入一條語句“ctx.clip();”,則在畫布中繪製出如圖21所示的圖形。從圖21可以看出第4個圓(棕色的),只有落在第3個圓中的部分被繪製出來。
圖21 在第3個圓之後使用clip()方法
若clip()方法往上移,放到第2個圓的後面,則在畫布中繪製出如圖22所示的圖形。從圖22可以看出第3個圓(藍色的)完全沒有被繪製出來,因為第3個圓與第2個圓相切,沒有交集;第4個圓(棕色的)只有落在第2個圓中的部分被繪製出來。
圖22 在第2個圓之後使用clip()方法
若再把clip()方法往上移,放到第1個圓的後面,則在畫布中繪製出如圖23所示的圖形。從圖23可以看出,第1個圓為剪切區域,第2、3、4個圓只有落在第1個圓中的部分才被繪製出來。
圖23 在第1個圓之後使用clip()方法
當使用剪切函數clip()進行繪圖後,可能需要取消該剪切區域或者重新定義剪切區域。在Canvas中,可以通過save()函數和restore()函數來實現。在構建剪切區域之前保存狀態,完成剪切區域內的繪圖之後進行狀態讀取。
例如,在例21的程式中,在第2個圓繪製後插入語句“ctx.save();”和“ctx.clip();”,在第3個圓繪製後插入語句“ctx.restore();”,則在畫布中繪製出如圖24所示的圖形。從圖24可以看出,第3個圓(藍色的)完全沒有被繪製出來,因為第3個圓與第2個圓相切,沒有交集;第4個圓(棕色的)全部被繪製出來,此時取消了剪切區域。
圖24 在第3個圓繪製後取消剪切區域
例22 採用clip實現簡單的探照燈效果。
<!DOCTYPE html>
<head>
<title>簡單探照燈</title>
</head>
<body>
<canvas id="myCanvas" width="400" height="400" style="border:3px double #996633;">
</canvas>
<script type="text/javascript">
var rot=10;
var canvas=document.getElementById('myCanvas');
var ctx=canvas.getContext('2d');
setInterval("draw()",100);
function draw()
{
ctx.clearRect(0,0,400,400);
ctx.save();
ctx.fillStyle="black";
ctx.fillRect(0,0,400,400);
ctx.beginPath();
ctx.arc(rot,200,40,0,Math.PI*2,true);
ctx.closePath();
ctx.fillStyle="white";
ctx.fill();
ctx.clip();
ctx.font="bold 45px 隸書";
ctx.textAlign="center";
ctx.textBaseline="middle";
ctx.fillStyle="#FF0000";
ctx.fillText("中國北京歡迎您!",200,200);
ctx.restore();
rot=rot+10;
if (rot>400) rot=10;
}
</script>
</body>
</html>
在瀏覽器中打開包含這段HTML代碼的html文件,可以看到在畫布中呈現出如圖25所示的簡單探照燈效果。
圖25 簡單的探照燈
3.圖形變換
在圖形學中,可以對圖形進行平移、縮放和旋轉等變換操作。
在Canvas API中,上下文CanvasRenderingContext2D對象提供的與圖形變換相關的方法如下:
void translate(in float x, in float y); // 平移Canvas的原點到指定的坐標點(x,y)
void rotate(in float angle); // 按給定的弧度angle順時針旋轉
void scale(in float x, in float y); // 按給定的縮放倍率進行縮放
void setTransform(in float m11, in float m12, in float m21, infloat m22, in float dx, in float dy); // 將當前轉換重置為單位矩陣
void transform(in float m11, in float m12, in float m21, in floatm22, in float dx, in float dy); // 按矩陣進行變換
在進行圖形變換前先保存上下文環境(狀態)是一個良好的習慣。大多數情況下,調用 restore()方法比手動恢複原先的狀態要簡單得多。例如,在一個迴圈中做平移操作但沒有保存和恢復canvas 的狀態,很可能到最後會發現有些東西不見了,那是因為它很可能已經超出 canvas 範圍以外了。
3.1 save和restore方法
save方法用於保存上下文環境,restore方法用於恢復到上一次保存的上下文環境。
在Canvas中,每個上下文對象都包含一個繪圖狀態的堆,繪圖狀態包含下列內容:
(1)當前的變換矩陣;
(2)當前的剪切區域(clip);
(3)當前的屬性值:fillStyle、font、globalAlpha、globalCompositeOperation、lineCap、 lineJoin、lineWidth、miterLimit、shadowBlur、shadowColor、shadowOffsetX、shadowOffsetY、 strokeStyle、textAlign、textBaseline等。
例23 save方法和restore方法的簡單應用示例。
ctx.fillStyle = "red";
ctx.fillRect(10,10,80,80);
ctx.save();
ctx.shadowOffsetX = 10;
ctx.shadowOffsetY = 10;
ctx.shadowBlur = 5;
ctx.shadowColor = "rgba(0,0,0,0.5)";
ctx.fillStyle = "blue";
ctx.fillRect(100,10,80,80);
ctx.restore();
ctx.fillRect(200,10,80,80);
ctx.fillStyle = "orange";
ctx.fillRect(300,10,80,80);
在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖26所示的4個矩形。
圖26 4個矩形
這4個矩形中,第1個矩形填充紅色,之後保存狀態(填充色為紅色),第2個矩形是一個有黑色陰影的填充色為藍色的矩形;接著,使用restore方法,恢復了保存前的設置,繪製了一個沒有陰影的填充色為紅色的第3個矩形,第4個矩形是一個填充色為橙色的矩形,也沒有陰影。
若去掉代碼中的“ctx.restore();”語句,不恢復狀態,則繪製的4個矩形如圖27所示。體會圖27與圖26的區別。
圖27 不執行“ctx.restore();”繪製的4個矩形
3.2 translate、scale和rotate方法
translate() 方法實現坐標平移,例如,進行ctx.translate(dx,dy);後,坐標原點移到(dx,dy)處,這樣程式繪圖時給出的坐標值(x,y),相對於canvas預設的坐標原點(0,0),應該為(x+dx,y+dy)。
例24 translate簡單應用示例。
ctx.fillStyle = "red";
ctx.fillRect(10,10,80,80);
ctx.fillStyle = "blue";
ctx.translate(80,80);
ctx.fillRect(10,10,80,80);
在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖26所示的2個矩形。從圖中看出,在坐標位置 (10,10) 處繪製一個紅色填充矩形後,將坐標原點平移到(80,80),這樣再次繪製填充藍色的矩形從位置 (90,90) 處開始繪製。
圖28 平移後的藍色矩形
例25 連續坐標平移的示例。
ctx.fillStyle = 'rgba(255,0,0,0.5)';
for (i = 0; i<5; i++)
{
ctx.translate(50,50);
ctx.fillRect(0,0,100,100);
}
在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖29所示的5個矩形。
圖29 坐標平移後的5個矩形
scale() 方法實現圖形的縮放。需要註意的是使用scale方法對繪圖進行縮放後,所有之後的繪圖也會被縮放,包括坐標定位也會被縮放。例如,執行ctx.scale(2,2)後,繪圖將定位於距離畫布左上角兩倍遠的位置。
例26 scale簡單應用示例。
ctx.fillStyle = "red";
ctx.fillRect(10,10,80,80);
ctx.save();
ctx.fillStyle = "blue";
ctx.scale(2,2);
ctx.fillRect(50,10,80,80);
ctx.restore();
ctx.fillStyle = "green";
ctx.scale(0.5,0.5);
ctx.fillRect(10,200,80,80);
在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖30所示的3個矩形。從圖中看出,在坐標位置 (10,10) 處繪製一個紅色填充矩形後,ctx.scale(2,2);將繪圖放大兩倍,這樣繪製填充藍色的矩形從位置 (100,20) 處開始繪製,其寬和高均是紅色填充矩形的2倍;恢覆上下文環境後,ctx.scale(0.5,0.5);將繪圖縮小1倍,這樣繪製填充綠色的矩形從位置 (5,100) 處開始繪製,其寬和高均是紅色填充矩形的一半。
圖30 矩形的縮放
若去掉代碼中的“ctx.restore();”語句,不恢復狀態,則繪製的3個矩形如圖31所示。體會圖31與圖30的區別。綠色矩形的大小之所以與紅色矩形一樣,是因為一個東西放大2倍後再縮小1倍,正好恢複原樣。
通過這個示例一定得明白,圖形變換的設置一定是在前一個狀態的基礎上進行的。因此在進行圖形變換時,根據需要通過save()方法保存狀態,restore()方法恢復狀態是非常重要的。
圖31 不執行“ctx.restore();”繪製的3個矩形
例27 連續圖形放大的示例。
ctx.fillStyle = 'rgba(255,0,0,0.5)';
for (i = 0; i<4; i++)
{
ctx.scale(2,2);
ctx.fillRect(5,5,10,10);
}
在瀏覽器中打開包含這段JavaScript代碼的html文件,可以看到在畫布中繪製出如圖32所示的4個矩形。
圖32 依次放大後的4個矩形
rotate()方法實現圖形的旋轉,其中參數給出的旋轉角度angle以弧度計。如需