Canvas API(畫(huà)布)用于在網(wǎng)頁(yè)實(shí)時(shí)生成圖像,并且可以操作圖像內容,基本上它是一個(gè)可以用JavaScript操作的位圖(bitmap)。
要使用HTML5在瀏覽器窗口中繪制圖形,首先需要在HTML文檔中新建一個(gè)canvas網(wǎng)頁(yè)元素。一般方法如下:
<canvas id="myCanvas" width="400" height="300">
您的瀏覽器不支持canvas!
</canvas>
上面這段代碼,表示建立了一個(gè)名為“myCanvas”的canvas網(wǎng)頁(yè)元素,它就是一塊畫(huà)布,該畫(huà)布的寬為400,高為300。有了這塊畫(huà)布,我們就可以使用JavaScript編寫(xiě)程序,利用Canvas API在這塊畫(huà)布上繪制圖形。如果所用瀏覽器不支持Canvas API,則就會(huì )顯示canvas標簽中間的文字——“您的瀏覽器不支持canvas!”。
每個(gè)canvas網(wǎng)頁(yè)元素都有一個(gè)對應的context對象(上下文對象),Canvas API定義在這個(gè)context對象上面。為了在canvas上繪制圖形,必須先得到一個(gè)畫(huà)布上下文對象的引用。為此,使用JavaScript編寫(xiě)程序段如下:
var canvas = document.getElementById('myCanvas'); // 取得網(wǎng)頁(yè)中的畫(huà)布對象
var ctx = canvas.getContext('2d'); // 得到畫(huà)布上下文對象ctx
上面代碼中,getContext方法指定參數2d,表示該canvas對象用于生成2D圖案(即平面圖案)。如果參數是3d,就表示用于生成3D圖像(即立體圖案)。
當使用一個(gè)canvas元素的getContext(“2d”)方法時(shí),返回的是CanvasRenderingContext2D對象,其內部表現為笛卡爾平面坐標。這就是Canvas畫(huà)布提供的一個(gè)用來(lái)作圖的平面空間,該空間的每個(gè)點(diǎn)都有自己的坐標,x表示橫坐標,y表示縱坐標。原點(diǎn)(0, 0)位于畫(huà)布左上角,x軸的正向是原點(diǎn)向右,y軸的正向是原點(diǎn)向下。
每一個(gè)canvas元素僅有一個(gè)上下文對象。得到了這個(gè)上下文對象,就可以利用這個(gè)對象的屬性和方法進(jìn)行圖形繪制了。
在Canvas API中,上下文CanvasRenderingContext2D對象提供了一系列與圖形繪制相關(guān)的屬性和方法。其中,與路徑繪制相關(guān)的方法如下:
void beginPath(); // 開(kāi)始繪制路徑
void closePath(); // 結束路徑繪制
void moveTo(in float x, in float y); // 設置線(xiàn)段的起點(diǎn)
void lineTo(in float x, in float y); // 設置線(xiàn)段的終點(diǎn)
void bezierCurveTo(in float cp1x, in float cp1y, in float cp2x, in float cp2y, in float x, in float y); // 繪制一條三次貝塞爾曲線(xiàn)
void quadraticCurveTo(in float cpx, in float cpy, in float x, infloat y); // 繪制一條二次貝塞爾曲線(xiàn)
void stroke(); // 給透明的線(xiàn)段著(zhù)色,從而完成線(xiàn)段繪制
void fill(); // 給閉合路徑填充顏色,填充色由fillStyle屬性指定
與繪制路徑相關(guān)的屬性有:
attribute float lineWidth; // 線(xiàn)的寬度,默認為1
attribute any strokeStyle; // 著(zhù)色的顏色,默認為black(黑色)
attribute any fillStyle; // 填充顏色,默認為black(黑色)
attribute DOMString lineCap; // 線(xiàn)段的箭頭樣式,僅有三個(gè)選項:butt(默認值)、round、square,其他值忽略
下面通過(guò)幾個(gè)例子來(lái)說(shuō)明繪制路徑的方法和屬性的使用。
例1 繪制一條從(20,20)到(200,20)的一條紅色橫線(xiàn)。
<!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(); // 開(kāi)始路徑繪制
ctx.moveTo(20, 20); // 設置路徑起點(diǎn),坐標為(20,20)
ctx.lineTo(200, 20); // 繪制一條到(200,20)的直線(xiàn)
ctx.lineWidth = 1.0; // 設置線(xiàn)寬
ctx.strokeStyle = "#FF0000"; // 設置線(xiàn)條顏色為紅色
ctx.stroke(); // 進(jìn)行線(xiàn)的著(zhù)色,這時(shí)整條線(xiàn)才變得可見(jiàn)
}
</script>
</head>
<body onload="draw('myCanvas');">
<canvas id="myCanvas" width="400" height="300" style="border:3px double #996633;">
</canvas>
</body>
</html>
將上述HTML代碼保存到一個(gè)html文本文件中,再在瀏覽器中打開(kāi)包含這段HTML代碼的html文件,可以看到在畫(huà)布中繪制出一條長(cháng)為180的紅色橫線(xiàn)。
下面的例子中我們不再給出完整的HTML文件內容,只給出JavaScript編寫(xiě)的與圖形繪制直接相關(guān)的代碼。例如例1源文件中加了注釋的6條語(yǔ)句。讀者需要自己試一試時(shí),只需把下列各例給出的代碼去覆蓋例1中的6條注釋語(yǔ)句,其余部分保持不變即可。
在繪制路徑時(shí),moveto和lineto方法可以多次使用。最后,還可以使用closePath方法,自動(dòng)繪制一條當前點(diǎn)到起點(diǎn)的線(xiàn)段,形成一個(gè)封閉圖形,省卻使用一次lineto方法。
例2 在畫(huà)布中繪制一個(gè)紅色邊框的直角三角形和一個(gè)藍色邊框的等腰三角形。
ctx.beginPath();
ctx.moveTo(20,20);
ctx.lineTo(20,100); // 垂直直角邊
ctx.lineTo(70,100); // 水平直角邊
ctx.lineTo(20,20); // 斜邊
ctx.strokeStyle="red";
ctx.stroke(); // 進(jìn)行著(zhù)色,使得線(xiàn)段可見(jiàn)
ctx.beginPath();
ctx.moveTo(40,120);
ctx.lineTo(20,180); // 左邊的腰
ctx.lineTo(60,180); // 底邊
ctx.closePath(); // 右邊的腰是通過(guò)自動(dòng)封閉繪制得到
ctx.strokeStyle="blue";
ctx.stroke();
例3 線(xiàn)段箭頭的三種樣式的比較。
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();
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖1所示的三條線(xiàn)段。其中,第1根線(xiàn)段箭頭樣式為“butt”(為默認值),線(xiàn)段的頭和尾都是長(cháng)方形,也就是不做任何的處理;第2根線(xiàn)段箭頭樣式為“round”,線(xiàn)段的頭和尾都增加一個(gè)半圓形的箭頭;第3根線(xiàn)段的樣式為“square”,線(xiàn)段的頭和尾都增加一個(gè)長(cháng)方形,長(cháng)度為線(xiàn)寬一半,高度為線(xiàn)寬。

圖1 繪制的3根紅色線(xiàn)段
例4 繪制紅綠藍3個(gè)實(shí)心三角形。
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(); // 填充藍色三角形
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖2所示的三個(gè)實(shí)心三角形。

圖2 三個(gè)實(shí)心三角形
通過(guò)上面的示例,我們可以知道,填充的形狀應該是封閉的路徑。如果路徑未關(guān)閉,那么 fill() 方法會(huì )從路徑結束點(diǎn)到開(kāi)始點(diǎn)之間添加一條線(xiàn),以關(guān)閉該路徑,然后填充該路徑。例如,圖2中藍色三角形構成的路徑并未關(guān)閉,調用fill()時(shí),會(huì )自動(dòng)添加直線(xiàn)關(guān)閉。
貝賽爾曲線(xiàn)(Bezier curve)是計算機圖形學(xué)中相當重要的參數曲線(xiàn)。Canvas API中提供了兩個(gè)繪制貝塞爾曲線(xiàn)的方法。其中:
quadraticCurveTo() 方法用于繪制一條二次貝塞爾曲線(xiàn)。
二次貝塞爾曲線(xiàn)需要兩個(gè)點(diǎn)。第一個(gè)點(diǎn)(cpx,cpy)是用于二次貝塞爾計算中的控制點(diǎn),第二個(gè)點(diǎn)(x,y)是曲線(xiàn)的結束點(diǎn)。曲線(xiàn)的開(kāi)始點(diǎn)是當前路徑中最后一個(gè)點(diǎn)。如果路徑不存在,需要使用 beginPath() 和 moveTo() 方法來(lái)定義開(kāi)始點(diǎn)。
bezierCurveTo() 方法用于繪制一條三次貝塞爾曲線(xiàn)。
三次貝塞爾曲線(xiàn)需要三個(gè)點(diǎn)。前兩個(gè)點(diǎn)(cp1x,cp1y)和(cp2x,cp2y)是用于三次貝塞爾計算中的控制點(diǎn),第三個(gè)點(diǎn)(x,y)是曲線(xiàn)的結束點(diǎn)。曲線(xiàn)的開(kāi)始點(diǎn)是當前路徑中最后一個(gè)點(diǎn)。如果路徑不存在,同樣需要使用 beginPath() 和 moveTo() 方法來(lái)定義開(kāi)始點(diǎn)。
例5 繪制一條二次貝塞爾曲線(xiàn)和一條三次貝塞爾曲線(xiàn)。
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();
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖3所示的兩條貝塞爾曲線(xiàn)。

圖3 兩條貝塞爾曲線(xiàn)
例6 使用多個(gè)貝塞爾曲線(xiàn)來(lái)繪制一個(gè)對話(huà)氣泡。
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();
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖4所示的對話(huà)氣泡。

圖4 對話(huà)氣泡
例7 使用多個(gè)貝塞爾曲線(xiàn)來(lái)繪制一個(gè)紅心。
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();
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖5所示的紅心圖案。

圖5 紅心
例8 通過(guò)循環(huán)繪制一個(gè)9行9列的棋盤(pán)。
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();
為顯示完整的棋盤(pán),請將畫(huà)布的寬和高均設置為500。在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖6所示的棋盤(pán)。

圖6 棋盤(pán)
在Canvas API中,上下文CanvasRenderingContext2D對象提供的與矩形繪制相關(guān)的方法如下:
void rect(in float x, in float y, in float w, in float h); // 建立一個(gè)矩形路徑
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); // 繪制給定的矩形邊框
這個(gè)幾個(gè)方法中給定的四個(gè)參數分別為矩形左上角頂點(diǎn)的x坐標、y坐標,以及矩形的寬w和高h。
例9 繪制紅綠藍三個(gè)矩形邊框。
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(); // // 繪制藍色矩形
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖7所示的三個(gè)矩形。

圖7 三個(gè)矩形邊框
例10 繪制一個(gè)邊長(cháng)為100的正方形,邊框采用藍色,內部用紅色填充。
ctx.fillStyle="red";
ctx.strokeStyle="blue";
ctx.lineWidth=2;
ctx.fillRect(50,50,100,100);
ctx.strokeRect(50,50,100,100);
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖8所示的正方形。

圖8 正方形
在沒(méi)有進(jìn)行坐標旋轉的情況下,采用rect()和strokeRect()方法繪制的矩形一定是兩條邊與x軸平行,兩條邊與y軸平行。若要繪制與坐標軸不平行的矩形,可以采用繪制4條線(xiàn)的方法完成。
例11 繪制矩形中的矩形。要求矩形里的矩形其頂點(diǎn)在外面矩形的中點(diǎn)上。
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;
}
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖9所示的正方形。

圖9 正方形中的正方形
例12 繪制國際象棋棋盤(pán)。
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);
}
}
為顯示完整的棋盤(pán),請將畫(huà)布的寬和高均設置為400。在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖10所示的國際象棋棋盤(pán)。

圖10 國際象棋棋盤(pán)
例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);
}
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖11所示的柱狀圖。

圖11 柱狀圖
在Canvas API中,上下文CanvasRenderingContext2D對象提供的與圓形和弧等繪制相關(guān)的方法如下:
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方法用來(lái)繪制扇形。參數x和y是圓心坐標,radius是半徑,startAngle和endAngle則是扇形的起始角度和終止角度(以弧度表示),anticlockwise表示作圖時(shí)應該逆時(shí)針畫(huà)(true)還是順時(shí)針畫(huà)(false)。
arcTo() 方法用于在畫(huà)布上創(chuàng )建介于兩個(gè)切線(xiàn)之間的弧/曲線(xiàn)。繪制出子路徑最后一個(gè)點(diǎn)(x0,y0)和(x1,y1)以及(x1,y1)和(x2,y2)構成的兩條直線(xiàn)間半徑為radius的最短弧線(xiàn),并用直線(xiàn)連接(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();
}
}
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖12所示的圓弧。

圖12 圓弧及填充
通過(guò)這個(gè)例子還可以加深理解:當調用fill()函數時(shí),所有沒(méi)有閉合的形狀都會(huì )自動(dòng)閉合,因此可以不調用closePath()函數。但是調用stroke()時(shí)不會(huì )自動(dòng)閉合。
例15 繪制9個(gè)大小不一的圓。
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();
}
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖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;
}
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖14所示的餅圖。

圖14 餅圖
在Canvas API中,上下文CanvasRenderingContext2D對象提供的與文本繪制相關(guān)的方法如下:
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);
這兩個(gè)方法用來(lái)繪制文本,它的前三個(gè)參數分別為文本內容、起點(diǎn)的x坐標、y坐標。其中:fillText方法為繪制填充的文字;strokeText方法為對文字進(jìn)行描邊,不填充內部區域,通常用來(lái)添加空心字。
與文本相關(guān)的屬性有:
attribute DOMString font; // 設置字體,默認為10px sans-serif
attribute DOMString textAlign; // 設置對齊方式,有"start", "end", "left", "right", "center"等,默認為"start"
attribute DOMString textBaseline; //設置文字對齊基線(xiàn),有"top", "hanging", "middle", "alphabetic", "ideographic", "bottom" 等取值,默認為”alphabetic"
例17 在畫(huà)布上添加兩行文字。
ctx.font = "Bold 50px 隸書(shū)";
ctx.fillStyle = "Black";
ctx.fillText("我們是中國人", 10, 50);
ctx.strokeText("我們熱愛(ài)我們的祖國", 10, 150);
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖15所示的文本。

圖15 文本繪制
注意:fillText方法不支持文本斷行,即所有文本出現在一行內。所以,如果要生成多行文本,只有調用多次fillText方法。
例18 繪制包含數據說(shuō)明的柱狀圖。
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);
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖16所示的包含數據說(shuō)明的柱狀圖。

圖16 包含數據說(shuō)明的柱狀圖
還可以為文本等設置陰影。
例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);
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖17所示的文字陰影效果。

圖17 文字陰影
在Canvas API中,上下文CanvasRenderingContext2D對象提供了一個(gè)用于圖形剪切的方法。
void clip();
剪切(clip)路徑和普通的 canvas 圖形差不多,不同的是它的作用是遮罩,用來(lái)隱藏沒(méi)有遮罩的部分,如圖18所示。紅邊五角星就是裁切路徑,所有在路徑以外的部分都不會(huì )在 canvas 上繪制出來(lái)。默認情況下,canvas 有一個(gè)與它自身一樣大的剪切路徑(也就是沒(méi)有剪切效果)。

圖18 剪切示意圖
例20 一個(gè)簡(jiǎn)單的剪切示例。
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);
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖19所示的三角形剪切。

圖19 三角形剪切
在這個(gè)例子中,先畫(huà)了一個(gè)與 canvas 一樣大?。▽?00,高300)的紅色方形作為背景,然后用 clip方法創(chuàng )建一個(gè)三角形的剪切路徑。剪切路徑創(chuàng )建之后,所有出現在它里面的東西才會(huì )畫(huà)出來(lái)。這樣在其后繪制高度為畫(huà)布一半的矩形填充時(shí),只有三角形剪切路徑里面的內容才會(huì )繪制出來(lái)。
設定了剪切區域之后,無(wú)論在Canvas上繪制什么,只有落在剪切區域內的那部分才能得以顯示,其余都會(huì )被遮蔽掉。
例21 cilp方法的進(jìn)一步理解。
// 繪制第一個(gè)圓
ctx.beginPath();
ctx.fillStyle = 'red';
ctx.arc(200, 100, 100, 0, Math.PI * 2, false);
ctx.fill();
// 繪制第二個(gè)圓
ctx.beginPath();
ctx.fillStyle = 'blue';
ctx.arc(100, 150, 100, 0, Math.PI * 2, false);
ctx.fill();
// 繪制第三個(gè)圓
ctx.beginPath();
ctx.fillStyle = 'green';
ctx.arc(300, 150, 100, 0, Math.PI * 2, false);
ctx.fill();
// 繪制第四個(gè)圓
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();
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖20所示的4個(gè)圓。

圖20 沒(méi)有使用clip方法的4個(gè)圓
若在第3個(gè)圓繪制后插入一條語(yǔ)句“ctx.clip();”,則在畫(huà)布中繪制出如圖21所示的圖形。從圖21可以看出第4個(gè)圓(棕色的),只有落在第3個(gè)圓中的部分被繪制出來(lái)。

圖21 在第3個(gè)圓之后使用clip()方法
若clip()方法往上移,放到第2個(gè)圓的后面,則在畫(huà)布中繪制出如圖22所示的圖形。從圖22可以看出第3個(gè)圓(藍色的)完全沒(méi)有被繪制出來(lái),因為第3個(gè)圓與第2個(gè)圓相切,沒(méi)有交集;第4個(gè)圓(棕色的)只有落在第2個(gè)圓中的部分被繪制出來(lái)。

圖22 在第2個(gè)圓之后使用clip()方法
若再把clip()方法往上移,放到第1個(gè)圓的后面,則在畫(huà)布中繪制出如圖23所示的圖形。從圖23可以看出,第1個(gè)圓為剪切區域,第2、3、4個(gè)圓只有落在第1個(gè)圓中的部分才被繪制出來(lái)。

圖23 在第1個(gè)圓之后使用clip()方法
當使用剪切函數clip()進(jìn)行繪圖后,可能需要取消該剪切區域或者重新定義剪切區域。在Canvas中,可以通過(guò)save()函數和restore()函數來(lái)實(shí)現。在構建剪切區域之前保存狀態(tài),完成剪切區域內的繪圖之后進(jìn)行狀態(tài)讀取。
例如,在例21的程序中,在第2個(gè)圓繪制后插入語(yǔ)句“ctx.save();”和“ctx.clip();”,在第3個(gè)圓繪制后插入語(yǔ)句“ctx.restore();”,則在畫(huà)布中繪制出如圖24所示的圖形。從圖24可以看出,第3個(gè)圓(藍色的)完全沒(méi)有被繪制出來(lái),因為第3個(gè)圓與第2個(gè)圓相切,沒(méi)有交集;第4個(gè)圓(棕色的)全部被繪制出來(lái),此時(shí)取消了剪切區域。

圖24 在第3個(gè)圓繪制后取消剪切區域
例22 采用clip實(shí)現簡(jiǎn)單的探照燈效果。
<!DOCTYPE html>
<head>
<title>簡(jiǎn)單探照燈</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 隸書(shū)";
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>
在瀏覽器中打開(kāi)包含這段HTML代碼的html文件,可以看到在畫(huà)布中呈現出如圖25所示的簡(jiǎn)單探照燈效果。

圖25 簡(jiǎn)單的探照燈
在圖形學(xué)中,可以對圖形進(jìn)行平移、縮放和旋轉等變換操作。
在Canvas API中,上下文CanvasRenderingContext2D對象提供的與圖形變換相關(guān)的方法如下:
void translate(in float x, in float y); // 平移Canvas的原點(diǎn)到指定的坐標點(diǎn)(x,y)
void rotate(in float angle); // 按給定的弧度angle順時(shí)針旋轉
void scale(in float x, in float y); // 按給定的縮放倍率進(jìn)行縮放
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); // 按矩陣進(jìn)行變換
在進(jìn)行圖形變換前先保存上下文環(huán)境(狀態(tài))是一個(gè)良好的習慣。大多數情況下,調用 restore()方法比手動(dòng)恢復原先的狀態(tài)要簡(jiǎn)單得多。例如,在一個(gè)循環(huán)中做平移操作但沒(méi)有保存和恢復canvas 的狀態(tài),很可能到最后會(huì )發(fā)現有些東西不見(jiàn)了,那是因為它很可能已經(jīng)超出 canvas 范圍以外了。
save方法用于保存上下文環(huán)境,restore方法用于恢復到上一次保存的上下文環(huán)境。
在Canvas中,每個(gè)上下文對象都包含一個(gè)繪圖狀態(tài)的堆,繪圖狀態(tài)包含下列內容:
(1)當前的變換矩陣;
(2)當前的剪切區域(clip);
(3)當前的屬性值:fillStyle、font、globalAlpha、globalCompositeOperation、lineCap、 lineJoin、lineWidth、miterLimit、shadowBlur、shadowColor、shadowOffsetX、shadowOffsetY、 strokeStyle、textAlign、textBaseline等。
例23 save方法和restore方法的簡(jiǎn)單應用示例。
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);
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖26所示的4個(gè)矩形。

圖26 4個(gè)矩形
這4個(gè)矩形中,第1個(gè)矩形填充紅色,之后保存狀態(tài)(填充色為紅色),第2個(gè)矩形是一個(gè)有黑色陰影的填充色為藍色的矩形;接著(zhù),使用restore方法,恢復了保存前的設置,繪制了一個(gè)沒(méi)有陰影的填充色為紅色的第3個(gè)矩形,第4個(gè)矩形是一個(gè)填充色為橙色的矩形,也沒(méi)有陰影。
若去掉代碼中的“ctx.restore();”語(yǔ)句,不恢復狀態(tài),則繪制的4個(gè)矩形如圖27所示。體會(huì )圖27與圖26的區別。

圖27 不執行“ctx.restore();”繪制的4個(gè)矩形
translate() 方法實(shí)現坐標平移,例如,進(jìn)行ctx.translate(dx,dy);后,坐標原點(diǎn)移到(dx,dy)處,這樣程序繪圖時(shí)給出的坐標值(x,y),相對于canvas默認的坐標原點(diǎn)(0,0),應該為(x+dx,y+dy)。
例24 translate簡(jiǎn)單應用示例。
ctx.fillStyle = "red";
ctx.fillRect(10,10,80,80);
ctx.fillStyle = "blue";
ctx.translate(80,80);
ctx.fillRect(10,10,80,80);
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖26所示的2個(gè)矩形。從圖中看出,在坐標位置 (10,10) 處繪制一個(gè)紅色填充矩形后,將坐標原點(diǎn)平移到(80,80),這樣再次繪制填充藍色的矩形從位置 (90,90) 處開(kāi)始繪制。

圖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);
}
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖29所示的5個(gè)矩形。

圖29 坐標平移后的5個(gè)矩形
scale() 方法實(shí)現圖形的縮放。需要注意的是使用scale方法對繪圖進(jìn)行縮放后,所有之后的繪圖也會(huì )被縮放,包括坐標定位也會(huì )被縮放。例如,執行ctx.scale(2,2)后,繪圖將定位于距離畫(huà)布左上角兩倍遠的位置。
例26 scale簡(jiǎn)單應用示例。
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);
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖30所示的3個(gè)矩形。從圖中看出,在坐標位置 (10,10) 處繪制一個(gè)紅色填充矩形后,ctx.scale(2,2);將繪圖放大兩倍,這樣繪制填充藍色的矩形從位置 (100,20) 處開(kāi)始繪制,其寬和高均是紅色填充矩形的2倍;恢復上下文環(huán)境后,ctx.scale(0.5,0.5);將繪圖縮小1倍,這樣繪制填充綠色的矩形從位置 (5,100) 處開(kāi)始繪制,其寬和高均是紅色填充矩形的一半。

圖30 矩形的縮放
若去掉代碼中的“ctx.restore();”語(yǔ)句,不恢復狀態(tài),則繪制的3個(gè)矩形如圖31所示。體會(huì )圖31與圖30的區別。綠色矩形的大小之所以與紅色矩形一樣,是因為一個(gè)東西放大2倍后再縮小1倍,正好恢復原樣。
通過(guò)這個(gè)示例一定得明白,圖形變換的設置一定是在前一個(gè)狀態(tài)的基礎上進(jìn)行的。因此在進(jìn)行圖形變換時(shí),根據需要通過(guò)save()方法保存狀態(tài),restore()方法恢復狀態(tài)是非常重要的。

圖31 不執行“ctx.restore();”繪制的3個(gè)矩形
例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);
}
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖32所示的4個(gè)矩形。

圖32 依次放大后的4個(gè)矩形
rotate()方法實(shí)現圖形的旋轉,其中參數給出的旋轉角度angle以弧度計。如需將角度degress轉換為弧度,可以使用 degrees*Math.PI/180 公式進(jìn)行計算。
例28 將矩形旋轉45°。
ctx.fillStyle = "red";
ctx.fillRect(150,50,80,80);
ctx.fillStyle = "blue";
ctx.rotate(45*Math.PI/180);
ctx.fillRect(150,50,80,80);
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖33所示的2個(gè)矩形,其中藍色矩形順時(shí)針旋轉了45°。

圖33 順時(shí)針旋轉了45°的藍色矩形
例29 連續旋轉的矩形。
ctx.translate(200,200);
ctx.fillStyle = 'rgba(255,0,0,0.5)';
for (i = 0; i<6; i++)
{
ctx.rotate(Math.PI/3);
ctx.fillRect(0,0,100,50);
}
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖34所示的6個(gè)矩形。

圖34 旋轉的矩形
transform() 方法是通過(guò)變換矩陣實(shí)現變換。例如,語(yǔ)句ctx.transform(a,b,c,d,e,f);中包含6個(gè)參數,其中:
a 水平縮放繪圖
b 水平傾斜繪圖
c 垂直傾斜繪圖
d 垂直縮放繪圖
e 水平移動(dòng)繪圖
f 垂直移動(dòng)繪圖
通過(guò)transform() 方法可以縮放、旋轉、移動(dòng)并傾斜當前的繪圖環(huán)境。
例如,ctx.translate(dx,dy)可以用
context.transform(0,1,1,0,dx,dy);
或 context.transform(1,0,0,1,dx,dy); 來(lái)替代。
又例如, ctx.transform(0.95,0,0,0.95,30,30);
可以替代 ctx.translate(30,30);
ctx.scale(0.95.0.95); 同時(shí)縮放和平移。
例30 transform() 方法簡(jiǎn)單應用示例。
ctx.fillStyle="red";
ctx.fillRect(0,0,150,50)
ctx.transform(1,0.5,0,1,30,10);
ctx.fillStyle="blue";
ctx.fillRect(0,0,150,50);
ctx.transform(1,0.5,-0.5,1,30,10);
ctx.fillStyle="green";
ctx.fillRect(0,0,150,50);
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖35所示的3個(gè)矩形。由圖可以看出,藍色矩形水平方向傾斜了,綠色矩形在水平方向和垂直方向上都傾斜了。

圖35 應用transform() 方法后繪制的矩形
setTransform()方法也是通過(guò)變換矩陣實(shí)現變換,它與transform()的使用方法類(lèi)同,區別在于:每次調用 setTransform() 時(shí),都會(huì )重置前一個(gè)變換矩陣然后構建新的矩陣;二每次調用 transform() 時(shí),它都會(huì )在前一個(gè)變換矩陣基礎上構建變換矩陣。
例如,將例30中的第2個(gè)transform 方法“ctx.transform(1,0.5,-0.5,1,30,10);”改寫(xiě)為“ctx.setTransform(1,0.5,-0.5,1,30,10);”,則在瀏覽器窗口中繪制出如圖36所示的圖形。由圖可知,藍色矩形基本被綠色矩形覆蓋,只露出一個(gè)小角。這是因為繪制藍色矩形之前,使用的是setTransform() 方法,會(huì )重置變換矩陣,而不是在繪制藍色矩形的變換矩陣基礎上進(jìn)行,因此藍色矩形與綠色矩形水平傾斜一致,水平與垂直方向平移也一致,只是綠色矩形多進(jìn)行了垂直傾斜,因此將藍色矩形露出一小角。

圖36 應用setTransform() 方法后繪制的矩形
例31 通過(guò)變換繪制螺旋。
ctx.translate(200,50);
for(var i=0; i<50; i++)
{
ctx.save();
ctx.transform(0.95,0,0,0.95,30,30);
ctx.rotate(Math.PI/12);
ctx.beginPath();
ctx.fillStyle = 'rgba(255,0,0,0.5)';
ctx.arc(0,0,50,0,Math.PI*2,true);
ctx.closePath();
ctx.fill();
}
在瀏覽器中打開(kāi)包含這段JavaScript代碼的html文件,可以看到在畫(huà)布中繪制出如圖37所示的螺旋圖形。

圖37 螺旋圖
canvas更有意思的一項特性就是圖像操作能力??梢杂糜趧?dòng)態(tài)的圖像合成或者作為圖形的背景,以及游戲界面等等。瀏覽器支持的任意格式的外部圖片都可以使用,比如PNG、GIF或者JPEG。
drawImage() 方法可以向畫(huà)布上繪制圖像、畫(huà)布或視頻。
要在畫(huà)布上繪制圖片,可以使用drawImage方法。該方法有三種不同的調用形式:
(1)ctx.drawImage(img,x,y); // 在畫(huà)布上定位圖像
(2)ctx.drawImage(img,x,y,width,height);
// 在畫(huà)布上定位圖像,并規定圖像的寬度和高度
(3)ctx.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
// 剪切圖像,并在畫(huà)布上定位被剪切的部分。
其中,各參數的說(shuō)明如下:
img 規定要使用的圖像、畫(huà)布或視頻。
sx 可選。開(kāi)始剪切的 x 坐標位置。
sy 可選。開(kāi)始剪切的 y 坐標位置。
swidth 可選。被剪切圖像的寬度。
sheight 可選。被剪切圖像的高度。
x 在畫(huà)布上放置圖像的 x 坐標位置。
y 在畫(huà)布上放置圖像的 y 坐標位置。
width 可選。要使用的圖像的寬度。(伸展或縮小圖像)
height 可選。要使用的圖像的高度。(伸展或縮小圖像)
在使用drawImage()方法將圖像繪制到畫(huà)布上之前,要獲得需要繪制的圖像。canvas的API可以使用圖像源的類(lèi)型有:
(1)HTMLImageElement。
這些圖片或者由Image()函數構造出來(lái)的,或者頁(yè)面中的某一個(gè)<img>元素。
(2)HTMLVideoElement。
用一個(gè)HTML的 <video>元素作為圖片源,可以從視頻中抓取當前幀作為一個(gè)圖像。
(3)HTMLCanvasElement。
可以使用另一個(gè) <canvas> 元素作為圖片源。這種類(lèi)型的一個(gè)常用的應用就是將第二個(gè)canvas作為另一個(gè)大的 canvas 的縮略圖。
獲取到需要在canvas上繪制的圖片的方式有以下幾種:
(1)使用相同頁(yè)面內的圖片或其它 canvas 元素。
通過(guò)document.images 集合、document.getElementsByTagName 方法或者 document.getElementById 方法來(lái)獲取頁(yè)面內的圖片(如果已知圖片元素的 ID)。
使用其它 canvas 元素和引用頁(yè)面內的圖片類(lèi)似地,用 document.getElementsByTagName 或 document.getElementById 方法來(lái)獲取其它 canvas 元素。
例32 在畫(huà)布中繪制頁(yè)面上的圖片。
<!DOCTYPE html>
<head>
<title>在畫(huà)布中繪制頁(yè)面上的圖片</title>
<script type="text/javascript">
function draw(id)
{
var canvas=document.getElementById(id);
if (canvas==null)
return false;
var ctx=canvas.getContext('2d');
var img=document.getElementById("myImg");
ctx.drawImage(img,10,10);
}
</script>
</head>
<body onload="draw('myCanvas');">
<img src="aaa.jpg" id="myImg">
<canvas id="myCanvas" width="400" height="400" style="border:3px double #996633;">
</canvas>
</body>
</html>
在瀏覽器中打開(kāi)保存這段HTML代碼的html文件,可以看到在畫(huà)布中出現如圖38所示的頁(yè)面。頁(yè)面中,左邊的圖片是頁(yè)面中的img元素,右邊是在畫(huà)布中繪制的左邊的圖片。

圖38 圖片繪制(一)
若將上面程序中的“ctx.drawImage(img,10,10);”改寫(xiě)為“ctx.drawImage(img,50,50,200,200,20,20,250,150);”,則在瀏覽器中顯示如圖39所示的內容。右邊canvas中繪制的圖片是左邊原圖的部分剪切,并進(jìn)行了水平拉伸。

圖39 圖片繪制(二)
(2)創(chuàng )建圖像。
用腳本創(chuàng )建一個(gè)新的 HTMLImageElement 對象。要實(shí)現這個(gè)方法,可以使用Image()構造函數。
var img = new Image(); // 創(chuàng )建一個(gè)img元素
img.src = 'myImage.png'; // 設置圖片源地址
當腳本執行后,圖片開(kāi)始裝載。
由于圖像的載入需要時(shí)間,drawImage方法只能在圖像完全載入后才能調用,因此上面的代碼需要改寫(xiě)??捎胠oad時(shí)間來(lái)保證不會(huì )在加載完畢之前使用這個(gè)圖片。
var img = new Image(); // 創(chuàng )建img元素
img.onload = function(){
// 執行drawImage語(yǔ)句
}
img.src = 'myImage.png'; // 設置圖片源地址
例33 編寫(xiě)如下的HTML文件內容。
<!DOCTYPE html>
<head>
<title>創(chuàng )建圖像對象使用圖片進(jìn)行繪制</title>
<script type="text/javascript">
function draw(id)
{
var canvas=document.getElementById(id);
if (canvas==null)
return false;
var ctx=canvas.getContext('2d');
var image = new Image();
image.src = 'aaa.jpg';
image.onload=function(){
ctx.drawImage(image,10,10);
}
}
</script>
</head>
<body onload="draw('myCanvas');">
<canvas id="myCanvas" width="400" height="400" style="border:3px double #996633;">
</canvas>
</body>
</html>
在瀏覽器中打開(kāi)保存這段HTML代碼的html文件,可以看到在畫(huà)布中出現如圖40所示的頁(yè)面。

圖40 創(chuàng )建圖像對象使用圖片進(jìn)行繪制
(3)通過(guò) data: url 方式嵌入圖像。
Data urls 允許用一串 Base64 編碼的字符串的方式來(lái)定義一個(gè)圖片。其優(yōu)點(diǎn)就是圖片內容即時(shí)可用,無(wú)須再到服務(wù)器兜一圈。缺點(diǎn)就是圖像沒(méi)法緩存,圖片大的話(huà)內嵌的 url 數據會(huì )相當的長(cháng)。
在Canvas API中,上下文CanvasRenderingContext2D對象提供了三個(gè)方法用于像素級操作:createImageData, getImageData, 和putImageData。
(1)createImageData()方法。
createImageData() 方法創(chuàng )建新的空白ImageData 對象。新對象的默認像素值 transparent black。
ImageData對象保存了圖像像素值。每個(gè)對象有三個(gè)屬性:width,height和data。data 屬性類(lèi)型為CanvasPixelArray,用于儲存width*height*4個(gè)像素值。每一個(gè)像素有RGB值和透明度alpha值(其值為 0 至255,包括alpha在內)。像素的順序從左至右,從上到下,按行存儲。其中:
R - 紅色 (0-255)
G - 綠色 (0-255)
B - 藍色 (0-255)
A - alpha 通道 (0-255;0 是透明的,255 是完全可見(jiàn)的)
transparent black 表示 (0,0,0,0)。
像素值以數組形式存在,由于每個(gè)像素有4條信息,因此數組的大小是 ImageData 對象的4倍??梢允褂?ImageDataObject.data.length獲得數組的大小。
createImageData() 方法有兩種調用形式:
1) var imgData=ctx.createImageData(width,height);
以指定的尺寸(以像素計)創(chuàng )建新的 ImageData 對象,其中參數width給定ImageData 對象的寬度,height給定ImageData 對象的高度。
2)var imgData=ctx.createImageData(imageData);
創(chuàng )建與指定的另一個(gè) ImageData 對象尺寸相同的新 ImageData 對象(不會(huì )復制圖像數據)。
(2)putImageData()方法。
putImageData() 方法用于將圖像數據(從指定的 ImageData 對象)放回畫(huà)布上。其調用格式為:
ctx.putImageData(imgData,x,y,dirtyX,dirtyY,dirtyWidth,dirtyHeight);
各參數的說(shuō)明如下:
imgData 規定要放回畫(huà)布的 ImageData 對象。
x ImageData 對象左上角的 x 坐標,以像素計。
y ImageData 對象左上角的 y 坐標,以像素計。
dirtyX 可選。水平值(x),以像素計,在畫(huà)布上放置圖像的位置。
dirtyY 可選。垂直值(y),以像素計,在畫(huà)布上放置圖像的位置。
dirtyWidth 可選。在畫(huà)布上繪制圖像所使用的寬度。
dirtyHeight 可選。在畫(huà)布上繪制圖像所使用的高度。
例34 createImageData和putImageData方法的簡(jiǎn)單應用。
var imgData=ctx.createImageData(100,100);
for (var i=0;i<imgData.data.length;i+=4)
{
imgData.data[i+0]=255;
imgData.data[i+1]=0;
imgData.data[i+2]=0;
imgData.data[i+3]=255;
}
ctx.putImageData(imgData,10,10);
上述代碼執行后,會(huì )在畫(huà)布中繪制一個(gè)紅色矩形,矩形的左上角坐標為(10,10),寬度和高度均為100。
(3)getImageData()方法。
getImageData方法可以用來(lái)讀?。◤椭疲〤anvas的內容,返回一個(gè)ImageData對象,包含了每個(gè)像素的信息。其調用形式為:
var imgData=ctx.getImageData(x,y,width,height);
各參數描述如下:
x 開(kāi)始復制的左上角位置的 x 坐標。
y 開(kāi)始復制的左上角位置的 y 坐標。
width 將要復制的矩形區域的寬度。
height 將要復制的矩形區域的高度。
imgData對象有一個(gè)data屬性,它的值是一個(gè)一維數組。該數組的值依次是每個(gè)像素的紅、綠、藍、alpha通道值,每個(gè)值的范圍是0~255??梢酝ㄟ^(guò)操作這個(gè)數組的值達到操作圖像的目的。修改這個(gè)數組以后,使用putImageData方法將數組內容重新繪制在Canvas上。
例35 通過(guò)操作像素數組的值來(lái)操作圖像。
<!DOCTYPE html>
<head>
<title>通過(guò)操作像素數組的值來(lái)操作圖像</title>
<script type="text/javascript">
function draw(id1,id2)
{
var canvas1=document.getElementById(id1);
var ctx1=canvas1.getContext('2d');
var canvas2=document.getElementById(id2);
var ctx2=canvas2.getContext('2d');
var imgData1=ctx1.createImageData(100,100);
for (i=0;i<imgData1.data.length;i+=4)
{
imgData1.data[i+0]=255;
imgData1.data[i+1]=0;
imgData1.data[i+2]=0;
imgData1.data[i+3]=255;
}
ctx1.putImageData(imgData1,10,10);
var imgData2=ctx1.getImageData(10,10,100,100);
for (i=imgData2.data.length/4;i<imgData2.data.length*3/4;i+=4)
{
imgData2.data[i+0]=0;
imgData2.data[i+2]=255;
}
ctx2.putImageData(imgData2,10,10);
}
</script>
</head>
<body onload="draw('myCanvas1','myCanvas2');">
<canvas id="myCanvas1" width="200" height="200" style="border:3px double #996633;">
</canvas>
<canvas id="myCanvas2" width="200" height="200" style="border:3px double #996633;">
</canvas>
</body>
</html>
在瀏覽器中打開(kāi)保存這段HTML代碼的html文件,可以看到在瀏覽器窗口中顯示如圖41所示的內容。

圖41 通過(guò)操作像素數組的值來(lái)操作圖像的示例
關(guān)于更多的Canvas API的知識,可以參閱文檔“HTML 5 Canvas 參考手冊”(https://www.w3school.com.cn/tags/html_ref_canvas.asp)。
聯(lián)系客服