canvas元素的基礎(chǔ)知識(shí)
在頁(yè)面上放置一個(gè)canvas元素,就相當(dāng)于在頁(yè)面上放置了一塊畫(huà)布,可以在其中進(jìn)行圖形的描繪。
canvas元素只是一塊無(wú)色透明的區(qū)域,需要利用JS編寫(xiě)在其中進(jìn)行繪畫(huà)的腳本。
在頁(yè)畫(huà)中放置canvas元素
首先,應(yīng)該要指定的是·id、width、height三個(gè)屬性 。
<canvas id="canvas" width="400" height="300" />
繪制矩形
用canvas元素繪制圖形時(shí),需要經(jīng)過(guò)幾道步驟:
- 取得
canvas元素 - 取得上下文 (
context):圖形上下文中是一個(gè)封裝了很多繪圖功能的對(duì)象。需要使用canvas對(duì)象的getContext方法來(lái)獲得圖形上下文。將getContext的參數(shù)設(shè)為2d。 - 填充與繪制邊框:填充(
fill)是指填滿圖形內(nèi)部,繪制邊框(stroke)只繪制圖形的外邊框。 - 設(shè)定繪圖樣式 (
style),指定顏色值:fillStyle屬性用來(lái)設(shè)置填充的樣式和顏色,strokeStyle屬性用來(lái)設(shè)置邊框的樣式和顏色。 - 指定線寬:
lineWidth屬性用來(lái)設(shè)置圖形邊框的寬度。 - 繪制矩形:分別使用
fillRect方法與strokeRect方法來(lái)填充矩形和繪制矩形邊框。
與矩形相關(guān)的方法有:fillRect()、strokeRect()和clearRect()。
context.fillRect(x, y, width, height);
context.strokeRect(x, y, width, height);
context.clearRect(x, y, width, height);
這里的context指的是圖形上下文對(duì)象,這三個(gè)方法使用同樣的參數(shù),x指矩形起點(diǎn)的橫坐標(biāo),y指矩形起點(diǎn)的縱坐標(biāo),坐標(biāo)原點(diǎn)為canvas畫(huà)布的最左上角,width指矩形的寬度,height指矩形的高度。
fillRect()方法在畫(huà)布上繪制的矩形會(huì)填充指定的顏色。
strokeRect()方法在畫(huà)布上繪制的矩形區(qū)域會(huì)使用指定的顏色描邊。
clearRect()方法會(huì)擦除指定的矩形區(qū)域中的圖形,使得矩形區(qū)域中的顏色全部變?yōu)橥该鳌?/p>
function draw(id) {
var canvas = document.getElementById(id);
if(canvas == null) { return false; }
var context = canvas.getContext('2d');
context.fillStyle = '#eeeeff';
context.fillRect(0, 0, 400, 300);
context.fillStyle = 'red';
context.strokeStyle = 'blue';
context.lineWidth = 1;
context.fillRect(50, 50, 100, 100);
context.strokeRect(50, 50, 100, 100);
}
運(yùn)行效果如圖:

使用路徑
繪制圓形
需要執(zhí)行如下步驟:
開(kāi)始創(chuàng)建路徑
使用圖形上下文對(duì)象的beginPath方法。
context.beginPath();
該方法不使用參數(shù),通過(guò)該方法開(kāi)始路徑的創(chuàng)建。在幾次循環(huán)的創(chuàng)建路徑的過(guò)程中,每次開(kāi)始創(chuàng)建時(shí)都要調(diào)用beginPath()方法。
創(chuàng)建圖形的路徑
創(chuàng)建圓形路徑時(shí),需要使用圖形上下文對(duì)象的arc方法。
context.arc(x, y, radius, startAngle, endAngle, anticlockwise);
該方法使用6個(gè)參數(shù),x為繪制圓形的起點(diǎn)橫坐標(biāo),y為繪制圓形的起點(diǎn)縱坐標(biāo),radius為圓形半徑,startAngle為開(kāi)始角度,endAngle為結(jié)束角度,anticlockwise為是否按順時(shí)針?lè)较蜻M(jìn)行繪制。anticlockwise參數(shù)為一個(gè)布爾值的參數(shù),為true時(shí),按順時(shí)針繪制,為false時(shí),按逆時(shí)針繪制。
路徑創(chuàng)建完成后,關(guān)閉路徑
使用圖形上下文對(duì)象的closePath方法將路徑閉合。
context.closePath();
將路徑閉合后,路徑的創(chuàng)建工作就完成了,但這時(shí)只是路徑創(chuàng)建完畢,還沒(méi)有真正繪制圖形。
設(shè)定繪制樣式,調(diào)用繪制方法,繪制路徑
使用fill()、stroke()、fillStyle()和strokeStyle()指定繪制樣式。
// 繪制圓形
function draw(id) {
var canvas = document.getElementById(id);
if(canvas == null) { return false; }
var context = canvas.getContext('2d');
context.fillStyle = '#eeeeff';
context.fillRect(0, 0, 400, 300);
var n = 0;
for(var i = 0;i<10; i++) {
context.beginPath();
context.arc(i * 25, i * 25, i * 10, 0, Math.PI * 2, true);
context.closePath();
context.fillStyle = 'rgba(255, 0, 0, 0.25)';
context.fill();
}
}
效果如圖:

路徑未閉合
如果把上例中的開(kāi)始創(chuàng)建路徑語(yǔ)句和閉合路徑語(yǔ)句刪除,在畫(huà)布中先是繪制一個(gè)深紅色的半徑最小的圓,然后每次半徑變大的同時(shí),圓的顏色也在逐漸變淡。
在循環(huán)時(shí)的具體繪制過(guò)程:
- 創(chuàng)建并繪制第一個(gè)圓
- 創(chuàng)建第二個(gè)圓,這時(shí)因?yàn)闆](méi)有把第一個(gè)圓的路徑閉合,所以第一個(gè)圓的路徑也保留著。繪制第二個(gè)圓的時(shí)候,第一個(gè)圓會(huì)根據(jù)該路徑重復(fù)繪制,第二個(gè)圓只繪制一次,而第一個(gè)圓繪制了兩次。
- 創(chuàng)建第三個(gè)圓,繪制時(shí),第三個(gè)圓只繪制了一次,第二個(gè)圓繪制兩次,第一個(gè)圓繪制了三次。
- 同上......
所以如果不閉合路徑,已經(jīng)創(chuàng)建的路徑會(huì)永遠(yuǎn)保留著。
效果如圖:

moveTo與lineTo
繪制直線時(shí),一般會(huì)用到moveTo與lineTo兩種方法。
moveTo方法的作用是將光標(biāo)移動(dòng)到指定坐標(biāo)點(diǎn),繪制直線的時(shí)候以這個(gè)坐標(biāo)點(diǎn)為起點(diǎn)。
moveTo(x, y);
lineTo方法在moveTo方法中指定的直線起點(diǎn)與參數(shù)中指定的直線終點(diǎn)之間繪制一條直線。
lineTo(x, y);
使用該方法繪制完直線后,光標(biāo)自動(dòng)移動(dòng)到lineTo方法的參數(shù)所指定的直線終點(diǎn)。
因此,在創(chuàng)建路徑時(shí),需要使用moveTo方法將光標(biāo)移動(dòng)到指定的直線起點(diǎn),然后使用lineTo方法在直線起點(diǎn)與直線終點(diǎn)之間創(chuàng)建路徑,然后將光標(biāo)移動(dòng)到直線終點(diǎn),在下一次使用lineTo方法的時(shí)候,會(huì)以當(dāng)前光標(biāo)所在坐標(biāo)點(diǎn)為直線起點(diǎn),并在下一個(gè)用lineTo方法指定的直線終點(diǎn)之間創(chuàng)建路徑,它會(huì)不斷重復(fù)這個(gè)過(guò)程,來(lái)完成復(fù)雜圖形的路徑繪制。
function draw(id) {
var canvas = document.getElementById(id);
if (canvas == null) { return false; }
var context = canvas.getContext('2d');
context.fillStyle = "#EEEEFF";
context.fillRect(0, 0, 400, 300);
var n = 0;
var dx = 150;
var dy = 150;
var s = 100;
context.beginPath();
context.fillStyle = 'rgb(100,255,100)';
context.strokeStyle = 'rgb(0,0,100)';
var x = Math.sin(0);
var y = Math.cos(0);
var dig = Math.PI / 15 * 11;
for(var i = 0; i < 30; i++) {
var x = Math.sin(i * dig);
var y = Math.cos(i * dig);
context.lineTo( dx + x * s,dy + y * s);
}
context.closePath();
context.fill();
context.stroke();
}
運(yùn)行效果如圖:

使用bezierCurveTo繪制貝濟(jì)埃曲線
繪制貝濟(jì)埃曲線需要使用bezierCurveTo()方法,該方法可以說(shuō)是lineTo的曲線版。
context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
該方法有6個(gè)參數(shù)。繪制曲線時(shí),需要兩個(gè)控制點(diǎn),cp1x為第一個(gè)控制點(diǎn)的橫坐標(biāo),cp1y為第一個(gè)控制點(diǎn)的縱坐標(biāo);cp2x為第二個(gè)控制點(diǎn)的橫坐標(biāo),cp2y為第二個(gè)控制點(diǎn)的縱坐標(biāo);x為貝濟(jì)埃曲線的終點(diǎn)橫坐標(biāo),y為貝濟(jì)埃曲線的終點(diǎn)縱坐標(biāo)。
function draw(id) {
var canvas = document.getElementById(id);
if (canvas == null) { return false; }
var context = canvas.getContext('2d');
context.fillStyle = "#EEEEFF";
context.fillRect(0, 0, 400, 300);
var n = 0;
var dx = 150;
var dy = 150;
var s = 100;
context.beginPath();
context.globalCompositeOperation ='and';
context.fillStyle = 'rgb(100,255,100)';
var x = Math.sin(0);
var y = Math.cos(0);
var dig = Math.PI / 15 * 11;
context.moveTo(dx,dy);
for(var i = 0; i < 30; i++) {
var x = Math.sin(i * dig);
var y = Math.cos(i * dig);
context.bezierCurveTo(dx + x * s,dy + y * s - 100,dx + x * s + 100,dy + y * s,dx + x * s,dy + y * s);
}
context.closePath();
context.fill();
context.stroke();
}
運(yùn)行效果如圖:

另外,還可以使用quadraticCurveTo方法繪制二次樣條曲線。
context.quadraticCurveTo(in float cpx,in float cpy,in float x,in float y)
這四個(gè)參數(shù)分別是控制點(diǎn)的橫坐標(biāo),控制點(diǎn)的縱坐標(biāo),二次樣條曲線終點(diǎn)的橫坐標(biāo),二次樣條曲線終點(diǎn)的縱坐標(biāo)。
繪制漸變圖形
繪制線性漸變
漸變是指在填充時(shí)從一種顏色慢慢過(guò)渡到另一種顏色。最簡(jiǎn)單的是兩點(diǎn)之間的線性漸變。
繪制線性漸變時(shí),需要使用到LinearGradient對(duì)象,可以使用圖形上下文對(duì)象的createLinearGradient()方法創(chuàng)建該對(duì)象。
context.createLinearGradient(xStart, yStart, xEnd, yEnd);
該方法有4個(gè)參數(shù),xStart為漸變起始點(diǎn)的橫坐標(biāo),yStart為漸變起始點(diǎn)的縱坐標(biāo),xEnd為漸變結(jié)束點(diǎn)的橫坐標(biāo),yEnd為漸變結(jié)束點(diǎn)的縱坐標(biāo)。
在創(chuàng)建linearGradient對(duì)象后,使用addColorStop()方法設(shè)定漸變的顏色。
context.addColorStop(offset, color);
offset參數(shù)為所設(shè)定的顏色離開(kāi)漸變起始點(diǎn)的偏移量,該參數(shù)的值是一個(gè)范圍在0~1之間的浮點(diǎn)值,漸變起始點(diǎn)的偏移量為0,漸變結(jié)束點(diǎn)的偏移量為1。color為繪制時(shí)的顏色。
因?yàn)槭菨u變,所以至少需要使用兩次addColorStop方法以追加兩個(gè)顏色(開(kāi)始顏色和結(jié)束顏色),可以追加多個(gè)顏色。例如從藍(lán)色漸變到白色然后漸變到綠色。這時(shí)藍(lán)色的位移量為0,白色的位移量為0.5,綠色的位移量為1。
接著把fillStyle或strokeStyle設(shè)定為linearGradient對(duì)象,然后執(zhí)行填充的方法,就可以繪制漸變圖形了。
function draw(id) {
var canvas = document.getElementById(id);
if(canvas == null) { return false; }
var context = canvas.getContext('2d');
var g1 = context.createLinearGradient(0, 0, 0, 300);
g1.addColorStop(0, 'rgb(255, 255, 0)');
g1.addColorStop(1, 'rgb(0, 255, 255)');
context.fillStyle = g1;
context.fillRect(0, 0, 400, 300);
var n = 0;
var g2 = context.createLinearGradient(0, 0, 300, 0);
g2.addColorStop(0, 'rgba(0, 0, 255, 0.5)');
g2.addColorStop(1, 'rgba(255, 0, 0, 0.5)');
for(var i = 0; i < 10; i++) {
context.beginPath();
context.fillStyle = g2;
context.arc(i * 25, i * 25, i * 10, 0, Math.PI * 2, true);
context.closePath();
context.fill();
}
}
效果如圖:

繪制徑向漸變
徑向漸變是指沿著圓形的半徑方向向外進(jìn)行擴(kuò)散的漸變方式。比如在描繪太陽(yáng)時(shí),沿著太陽(yáng)的半徑方向向外擴(kuò)散出去的光暈,就是一種徑向漸變。
使用圖形上下文對(duì)象的createRadialGradient方向繪制徑向漸變。
context.createRadialGradient(xStart, yStart, radiusStart, xEnd, yEnd, radiusEnd);
該方法使用六個(gè)參數(shù),xStart為漸變開(kāi)始圓的圓心橫坐標(biāo),yStart為漸變開(kāi)始圓的圓心縱坐標(biāo),radiusStart為開(kāi)始圓的半徑,xEnd為漸變結(jié)束圓的圓心橫坐標(biāo),yEnd為漸變結(jié)束圓的圓心縱坐標(biāo),radiusEnd為結(jié)束圓的半徑。
在這個(gè)方法中,分別指定了兩個(gè)圓的大小與位置。從第一個(gè)圓的圓心處向外進(jìn)行擴(kuò)散漸變,一直擴(kuò)散到第二個(gè)圓的外輪廓處。
在設(shè)定顏色時(shí),與線性漸變相同,使用addColorStop方法進(jìn)行設(shè)定。同樣也需要設(shè)定0~1之間的浮點(diǎn)數(shù)來(lái)作為漸變轉(zhuǎn)折點(diǎn)的偏移量。
function draw(id) {
var canvas = document.getElementById(id);
if(canvas == null) { return false; }
var context = canvas.getContext('2d');
var g1 = context.createRadialGradient(400, 0, 0, 400, 0, 400);
g1.addColorStop(0.1, 'rgb(255, 255, 0)');
g1.addColorStop(0.3, 'rgb(255, 0, 255)');
g1.addColorStop(1, 'rgb(0, 255, 255)');
context.fillStyle = g1;
context.fillRect(0, 0, 400, 300);
var n = 0;
var g2 = context.createRadialGradient(250, 250, 0, 250, 250, 300);
g2.addColorStop(0.1, 'rgba(255, 0, 0, 0.5)');
g2.addColorStop(0.7, 'rgba(255, 255, 0, 0.5)');
g2.addColorStop(1, 'rgba(0, 0, 255, 0.5)');
for(var i = 0; i < 10; i++) {
context.beginPath();
context.fillStyle = g2;
context.arc(i * 25, i * 25, i * 10, 0, Math.PI * 2, true);
context.closePath();
context.fill();
}
}
效果如圖:

繪制變換圖形
坐標(biāo)變換
繪制圖形的時(shí)候,我們經(jīng)常想要旋轉(zhuǎn)圖形,或?qū)D形使用變形處理,使用Canvas的坐標(biāo)變換處理功能,可以實(shí)現(xiàn)這個(gè)效果。
在計(jì)算機(jī)上繪制圖形的時(shí)候,是以坐標(biāo)單位為基準(zhǔn)來(lái)進(jìn)行圖形繪制的。默認(rèn)情況下,Canvas畫(huà)布的最左上角對(duì)應(yīng)于坐標(biāo)軸原點(diǎn)(0, 0)。
對(duì)坐標(biāo)的變形處理有三種方式。
平移
使用圖形上下文對(duì)象的translate方法移動(dòng)坐標(biāo)軸原點(diǎn)。
context.translate(x, y);
x表示坐標(biāo)軸原點(diǎn)向左移動(dòng)多少個(gè)單位,默認(rèn)情況下為像素;y表示將坐標(biāo)軸原點(diǎn)向下移動(dòng)多少個(gè)單位。
擴(kuò)大
使用圖形上下文對(duì)象的scale方法將圖形放大。
context.scale(x, y);
x是水平方向放大的倍數(shù),y是垂直方向放大的倍數(shù)。
旋轉(zhuǎn)
使用圖形上下文對(duì)象的rotate方法將圖形進(jìn)行旋轉(zhuǎn)。
context.rotate(angle);
rotate方法接收一個(gè)參數(shù)angle,angle是指旋轉(zhuǎn)的角度,旋轉(zhuǎn)的中心點(diǎn)是坐標(biāo)軸的原點(diǎn)。旋轉(zhuǎn)是以順時(shí)針?lè)较蜻M(jìn)行的,要想逆時(shí)針旋轉(zhuǎn),將angle設(shè)定為負(fù)數(shù)就可以了。
function draw(id) {
var canvas = document.getElementById(id);
if(canvas == null) { return false; }
var context = canvas.getContext('2d');
context.fillStyle = '#eeeeff';
context.fillRect(0, 0, 400, 300);
// 圖形繪制
context.translate(200, 50);
context.fillStyle = 'rgba(255, 0, 0, 0.25)';
for(var i = 0; i <50; i++) {
context.translate(200, 50);
context.scale(0.95, 0.95);
context.rotate(Math.PI / 10);
context.fillRect(0, 0, 100, 50);
}
}
運(yùn)行效果如圖:

坐標(biāo)變換與路徑的結(jié)合使用
如果要對(duì)矩形進(jìn)行變形,使用坐標(biāo)變換就行了。但對(duì)使用路徑繪制出來(lái)的圖形進(jìn)行變換的時(shí)候,考慮的事情就多了。因?yàn)槭褂昧俗鴺?biāo)變換之后,已經(jīng)創(chuàng)建好的路徑就不能用了,必須要重新創(chuàng)建路徑。重新創(chuàng)建好路徑后,坐標(biāo)變換方法又失效了。
必須先另外寫(xiě)一個(gè)創(chuàng)建路徑的函數(shù),然后在坐標(biāo)變換的同時(shí)調(diào)用該函數(shù),這樣才能解決這個(gè)問(wèn)題。
function draw(id) {
var canvas = document.getElementById(id);
if(canvas == null) { return false; }
var context = canvas.getContext('2d');
context.fillStyle = '#eeeeff';
context.fillRect(0, 0, 400, 300);
// 圖形繪制
context.translate(200, 50);
for(var i = 0; i < 50; i++) {
context.translate(25, 25);
context.scale(0.95, 0.95);
context.rotate(Math.PI / 10);
create5Star(context);
context.fill();
}
}
function create5Star(context) {
var n = 0;
var dx = 100;
var dy = 0;
var s = 50;
// 創(chuàng)建路徑
context.beginPath();
context.fillStyle = 'rgba(255, 0, 0, 0.5)';
var x = Math.sin(0);
var y = Math.cos(0);
var dig = Math.PI / 5 * 4;
for (var i = 0; i < 5; i++) {
var x = Math.sin(i * dig);
var y = Math.cos(i * dig);
context.lineTo(dx + x * s, dy + y * s);
}
context.closePath();
}
上面的代碼可以繪制一個(gè)將五角星一邊旋轉(zhuǎn)一邊縮小的圖形。在create5Star函數(shù)中,創(chuàng)建了一個(gè)五角星的路徑,然后在draw函數(shù)的for循環(huán)中,首先依次執(zhí)行translate、scale、rotate方法,然后執(zhí)行create5Star函數(shù)創(chuàng)建路徑,最后執(zhí)行fill填充。
在create5Star函數(shù)中,只創(chuàng)建了一個(gè)五角星,因坐標(biāo)軸變換,在畫(huà)布中,此五角星一邊縮小一邊旋轉(zhuǎn),之后產(chǎn)生一個(gè)新的五角星新的五角星又采用同樣的方法進(jìn)行繪制,最終繪制出來(lái)一串具有變形效果的五角星的圖形。
效果如圖:

矩陣變換
變換矩陣是專門用來(lái)實(shí)現(xiàn)圖形變形的,它與坐標(biāo)一起配合使用,以達(dá)到變形的目的。當(dāng)圖形上下文被創(chuàng)建完畢時(shí),事實(shí)上也創(chuàng)建了一個(gè)默認(rèn)的變換矩陣,如果不對(duì)這個(gè)變換矩陣進(jìn)行修改,那么接下來(lái)繪制的圖形將以畫(huà)布的最左上角為坐標(biāo)原點(diǎn)繪制圖形,繪制出來(lái)的圖形也不經(jīng)過(guò)縮放、變形的處理,但如果對(duì)這個(gè)變換矩陣進(jìn)行修改,那么情況就完全不一樣了。
使用圖形上下文對(duì)象的transform方法修改變換矩陣。
context.transform(m11, m22, m21, dx, dy);
該方法使用一個(gè)新的變換矩陣與當(dāng)前變換矩陣進(jìn)行乘法運(yùn)算。
m11 m21 dx
m12 m22 dy
0 0 1
m11,m21,m12,m22四個(gè)參數(shù)用來(lái)修改使用這個(gè)方法之后,繪制圖形時(shí)的計(jì)算方法,以達(dá)到變形目的,dx和dy參數(shù)移動(dòng)坐標(biāo)原點(diǎn),dx表示將坐標(biāo)原點(diǎn)在x軸上向右移動(dòng)x個(gè)單位,默認(rèn)情況下以像素為單位,dy表示將坐標(biāo)原點(diǎn)在y軸上向下移動(dòng)y個(gè)單位。
translate、scale和rotate這三個(gè)方法實(shí)際上都是隱式的修改了變換矩陣,都可以使用transform方法來(lái)進(jìn)行代替。
translate(x, y)可以使用context.transform(1, 0, 0, 1, x, y)或context.transform(0, 1, 1, 0, x, y)方法進(jìn)行代替,前面四個(gè)參數(shù)表示不對(duì)圖形進(jìn)行縮放、變形,將dx設(shè)為x表示將坐標(biāo)原點(diǎn)向右移動(dòng)x個(gè)單位,dy設(shè)為y表示將坐標(biāo)原點(diǎn)向下移動(dòng)y個(gè)單位。
scale(x, y)可以使用context.transform(x, 0, 0, y, 0, 0)或context.transform(0, y, x, 0, 0, 0)方法代替,前面四個(gè)參數(shù)表示將圖形橫向擴(kuò)大x倍,縱向擴(kuò)大y倍。dx,dy為0表示不移動(dòng)坐標(biāo)原點(diǎn)。
rotate(angle)替換方法如下:
context.transform(Math.cos(angle * Math.PI / 180),
Math.sin(angle * Math.PI / 180),
-Math.sin(angle * Math.PI / 180),
Math.cos(angle * Math.PI / 180), 0, 0);
// 或
context.transform(-Math.sin(angle * Math.PI / 180),
Math.cos(angle * Math.PI / 180),
-Math.cos(angle * Math.PI / 180),
Math.sin(angle * Math.PI / 180), 0, 0);
其中前面四個(gè)參數(shù)以三角函數(shù)的形式結(jié)合起來(lái),共同完成圖形按angle角度的順時(shí)針旋轉(zhuǎn)處理,dx,dy為0表示不移動(dòng)坐標(biāo)原點(diǎn)。
function draw(id) {
var canvas = document.getElementById(id);
if(canvas == null) { return false; }
var context = canvas.getContext('2d');
// 定義顏色
var colors = ['red', 'orange', 'yellow', 'green', 'blue', 'navy', 'purple'];
// 定義線寬
context.lineWidth = 10;
context.transform(1, 0, 0, 1, 100, 0);
// 循環(huán)繪制圓弧
for(var i = 0; i < colors.length; i++) {
// 定義每次向下移動(dòng)10個(gè)像素的變換矩陣
context.transform(1, 0, 0, 1, 0, 10);
// 設(shè)定顏色
context.strokeStyle = colors[i];
// 繪制圓弧
context.beginPath();
context.arc(50, 100, 100, 0, Math.PI, true);
context.stroke();
}
}
上面的代碼用循環(huán)的方法繪制了幾個(gè)圓弧,圓弧的大小與位置均不變,只是使用了transform方法讓坐標(biāo)原點(diǎn)每次向下移動(dòng)10個(gè)像素,使得繪制出來(lái)的圓弧相互重疊,然后對(duì)圓弧設(shè)置七彩顏色,使這些圓弧的外觀達(dá)到彩虹的效果。
效果如圖:

使用了transform方法后,接下來(lái)要繪制的圖形都會(huì)按照移動(dòng)后的坐標(biāo)原點(diǎn)與新的變換矩陣相結(jié)合的方法進(jìn)行繪制,必要時(shí)可以使用setTransform方法將變換矩陣進(jìn)行重置。
context.setTransform(m11, m12, m21, m22, dx, dy);
該方法的參數(shù)與transform相同,事實(shí)上,該方法的作用為將畫(huà)布上的最左上角重置為坐標(biāo)原點(diǎn),當(dāng)圖形上下文創(chuàng)建完畢時(shí)將所創(chuàng)建的初始變換矩陣設(shè)置為當(dāng)前變換矩陣,然后使用transform方法。
function draw(id) {
var canvas = document.getElementById(id);
if(canvas == null) { return false; }
var context = canvas.getContext('2d');
// 繪制紅色長(zhǎng)方形
context.strokeStyle = 'red';
context.strokeRect(30, 10, 60, 20);
// 繪制順時(shí)針旋轉(zhuǎn)45°后的藍(lán)色長(zhǎng)方形
// 繪制45°圓弧
var rad = 45 * Math.PI / 180;
// 定義順時(shí)針旋轉(zhuǎn)45°的變換矩陣
context.setTransform(Math.cos(rad), Math.sin(rad), -Math.sin(rad), Math.cos(rad), 0, 0);
// 繪制圖形
context.strokeStyle = 'blue';
context.strokeRect(30, 10, 60, 20);
// 繪制方法2.5倍后的綠色長(zhǎng)方形
// 定義放大2.5倍的變換矩陣
context.setTransform(2.5, 0, 0, 2.5, 0, 0);
// 繪制圖形
context.strokeStyle = 'green';
context.strokeRect(30, 10, 60, 20);
// 將坐標(biāo)原點(diǎn)向右移動(dòng)40像素,向下移動(dòng)80像素后繪制灰色長(zhǎng)方形
// 定義將坐標(biāo)原點(diǎn)向右移動(dòng)40像素,向下移動(dòng)80像素的矩陣
context.setTransform(1, 0, 0, 1, 40, 80);
// 繪制圖形
context.strokeStyle = 'gray';
context.strokeRect(30, 10, 60, 20);
}
上面的代碼先創(chuàng)建了一個(gè)紅色邊框的長(zhǎng)方形,然后將該長(zhǎng)方形順時(shí)針旋轉(zhuǎn)45度,繪制出一個(gè)新的長(zhǎng)方形,并繪制其邊框?yàn)樗{(lán)色,然后將紅色長(zhǎng)方形擴(kuò)大2.5倍繪制新的長(zhǎng)方形,邊框?yàn)榫G色,最后在紅色長(zhǎng)方形右下方繪制同樣大小的長(zhǎng)方形,邊框?yàn)榛疑?br> 效果如圖:

圖形組合
在H5中,只要用圖形上下文對(duì)象的globalCompositeOperation屬性就能自己決定圖形的組合方式了。
context.globalCompositeOperation = type;
type的值必須是下面幾種字符串之一:
-
source-over(默認(rèn)值):表示新圖形覆蓋在原有圖形之上 -
destination-over:表示在原有圖形之下繪制新圖形 -
source-in:新圖形與原有圖形作in運(yùn)算,只顯示新圖形中與原有圖形相重疊的部分,新圖形與原有圖形的其他部分均變成透明 -
destination-in:原有圖形與新圖形作in運(yùn)算,只顯示原有圖形中與新圖形相重疊的部分,新圖形與原有圖形的其他部分均變成透明 -
source-out:新圖形與原有圖形作out運(yùn)算,只顯示新圖形中與原有圖形不重疊的部分,新圖形與原有圖形的其他部分均變成透明 -
destination-out:原有圖形與新圖形作out運(yùn)算,只顯示原有圖形中與新圖形不重疊的部分,新圖形與原有圖形的其他部分均變成透明 -
source-atop:只繪制新圖形中與原有圖形重疊的部分與未被重疊覆蓋的原有圖形,新圖形的其他部分變成透明 -
destination-atop:只繪制原有圖形中被新圖形重疊覆蓋的部分與新圖形的其他部分 ,原有圖形中的其他部分變成透明,不繪制新圖形中與原有圖形相重疊的部分 -
lighter:原有圖形與新圖形均繪制,重疊部分做加色處理 -
xor:只繪制新圖形中與原有圖形不重疊的部分,重疊部分變成透明 -
copy:只繪制新圖形,原有圖形中未與新圖形重疊的部分變成透明
如果指定的type不在這幾個(gè)字符串當(dāng)中,則按默認(rèn)方式組合圖形。
function draw(id) {
var canvas = document.getElementById(id);
if (canvas == null) { return false; }
var context = canvas.getContext('2d');
var oprtns = new Array(
"source-atop",
"source-in",
"source-out",
"source-over",
"destination-atop",
"destination-in",
"destination-out",
"destination-over",
"lighter",
"copy",
"xor"
);
i=8;
// 顯示想要查看的組合效果
// 繪制原有圖形(藍(lán)色長(zhǎng)方形)
context.fillStyle = "blue";
context.fillRect(10, 10, 60, 60);
/* 設(shè)置組合方式,從組合的參數(shù)數(shù)組中挑選組合方式,此處因?yàn)閕是8,
所以選擇oprtns數(shù)組中第9(數(shù)組從0開(kāi)始計(jì)算)個(gè)組合方式lighter */
context.globalCompositeOperation = oprtns[i];
//設(shè)置新圖形(紅色圓形)
context.beginPath();
context.fillStyle = "red";
context.arc(60, 60, 30, 0, Math.PI * 2, false);
context.fill();
}
上面的代碼將所有的圖形組合方式放在一個(gè)數(shù)組中,然后通過(guò)變量i來(lái)指定挑選哪種組合方式進(jìn)行顯示。
效果如圖:

給圖形繪制陰影
使用Canvas元素可以給圖形添加陰影效果。
圖形上下文對(duì)象的關(guān)于陰影繪制的屬性:
-
shadowOffsetX:陰影的橫向位移量,默認(rèn)為0。 -
shadowOffsetY:陰影的縱向位移量,默認(rèn)為0。 -
shadowColor:陰影的顏色。 -
shadowBlur:陰影的模糊范圍,可選。屬性值是比0大的數(shù)字,否則將被忽略。
function draw(id) {
var canvas = document.getElementById(id);
if (canvas == null) { return false; }
var context = canvas.getContext('2d');
context.fillStyle = "#EEEEFF";
context.fillRect(0, 0, 400, 300);
context.shadowOffsetX = 10;
context.shadowOffsetY = 10;
context.shadowColor = 'rgba(100,100,100,0.5)';
context.shadowBlur = 7.5;
// 圖形繪制
context.translate(0,50);
for(var i = 0;i < 3;i++){
context.translate(50,50);
create5Star(context);
context.fill();
}
}
function create5Star(context) {
var n = 0;
var dx = 100;
var dy = 0;
var s = 50;
//創(chuàng)建路徑
context.beginPath();
context.fillStyle = 'rgba(255,0,0,0.5)';
var x = Math.sin(0);
var y = Math.cos(0);
var dig = Math.PI / 5 * 4;
for(var i = 0; i < 5; i++) {
var x = Math.sin(i * dig);
var y = Math.cos(i * dig);
context.lineTo( dx + x * s,dy + y * s);
}
context.closePath();
}
效果如圖:

上面的代碼使用translate方法繪制了幾個(gè)呈移動(dòng)狀態(tài)的五角星。同時(shí)給每個(gè)五角星都加上了陰影效果。繪制陰影的時(shí)候使用了圖形上下文對(duì)象的繪制陰影屬性,這幾個(gè)屬性與路徑無(wú)關(guān),只要設(shè)定一次之后,全部五角星就都具有陰影效果了。
如果不想讓全部五角星都具有陰影效果,需要把shadowColor屬性設(shè)定為rgba(0, 0, 0, 0)。
使用圖像
繪制圖像
繪制圖像使用drawlmage()方法。
context.drawlmage(image,x,y);
context.drawlmage(image,x,y,w,h);
cont ext.drawlmage(image,sx,sy,sw,sh,dx,dy,dw,dh);
第一種方法只使用三個(gè)參數(shù),image是一個(gè)Image對(duì)象,用該對(duì)象來(lái)裝載圖像文件。 x和y為繪制時(shí)該圖像在畫(huà)布中的起始坐標(biāo)。
第二種方法中前三個(gè)參數(shù)與第一種方法中的使用方法一樣,w、h是指繪制時(shí)的圖像的寬度與高度。第一種方法中省略了這兩個(gè)參數(shù),所以繪制出來(lái)的圖像與原圖大小相同,而第二種方法可以用來(lái)進(jìn)行圖像縮放 。
第三種方法可以用來(lái)將畫(huà)布中已繪制好的圖像的全部或者局部區(qū)域復(fù)制到畫(huà)布中的另一個(gè)位置上。該方法使用九個(gè)參數(shù),image代表被復(fù)制的圖像文件,sx與sy分別表示源圖像的被復(fù)制區(qū)域在畫(huà)布中的起始橫坐標(biāo)與起始縱坐標(biāo),sw與sh表示被復(fù)制區(qū)域的寬度與高度,dx與dy表示復(fù)制后的目標(biāo)圖像在畫(huà)布中的起始橫坐標(biāo)與起始縱坐標(biāo),dw與dh表示復(fù)制后的目標(biāo)圖像的寬度與高度。該方法可以只復(fù)制圖像的局部,只要將sx與sy設(shè)為局部區(qū)域的起始點(diǎn)坐標(biāo), 將sw與sh設(shè)為局部區(qū)域的寬度與高度就可以了。該方能也可以用來(lái)將源圖像進(jìn)行縮放,只要將dw與dh設(shè)為縮放后的寬度與高度就可以了。
繪制圖像時(shí)首先使用不帶參數(shù)的new方法創(chuàng)建Image對(duì)象,然后設(shè)定Image對(duì)象的src屬性為需要繪制的圖像文件的路徑。
image = new Image();
image.src = "image1.jpg"; //設(shè)置圖像路徑
然后就可以使用drawlmage方能繪制該圖像文件了。
事實(shí)上,即使設(shè)定好Image對(duì)象的src屬性后,也不一定立刻就能把圖像繪制完畢,譬如,有時(shí)該圖像文件是一個(gè)來(lái)源于網(wǎng)絡(luò)的比較大的圖像文件,這時(shí)就得耐心等待圖像全部裝載完畢才能看見(jiàn)該圖像。
這種情況下,需要先等圖形加載完畢再繪制圖像文件。
image.onload = function(){
// 繪制圖像的函數(shù)
}
在Image對(duì)象的onload事件中同步執(zhí)行繪制圖像的函數(shù),就可以一邊裝載一邊繪制了。
function draw(id) {
var canvas = document.getElementById(id);
if (canvas == null) { return false; }
var context = canvas.getContext('2d');
context.fillStyle = "#EEEEFF";
context.fillRect(0, 0, 400, 300);
image = new Image();
image.src = "tyl.jpg";
image.onload = function() {
drawImg(context, image);
};
}
function drawImg(context, image){
for(var i = 0;i < 7;i++) {
context.drawImage(image, 0 + i * 50, 0 + i * 25, 100, 100);
}
}
效果如圖:

下面的代碼使用了八個(gè)參數(shù)的drawlmage方法將圖像的局部放大,并復(fù)制到畫(huà)布中另一個(gè)地方,該示例通常用來(lái)做圖像局部的特寫(xiě)放大處理。
function draw(id) {
var canvas = document.getElementById(id);
if (canvas == null) { return false; }
var context = canvas.getContext('2d');
context.fillStyle = "#EEEEFF";
context.fillRect(0, 0, 400, 300);
image = new Image();
image.src = "tyl.jpg";
image.onload = function() {
drawImg(context, image);
};
}
function drawImg(context, image){
var i = 0;
// 首先調(diào)用該方法繪制原始圖像
context.drawImage(image, 0, 0, 100, 100);
// 繪制將局部區(qū)域進(jìn)行放大后的圖像
context.drawImage(image, 23, 5, 57, 80, 110, 0, 100, 100);
}
運(yùn)行效果如圖:

圖像平鋪
圖像平鋪就是用按一定比例縮小后的圖像將畫(huà)布填滿,有兩種方法可以實(shí)現(xiàn)圖像平鋪,一種是使用drawImage方法。
function draw(id) {
var image = new Image();
var canvas = document.getElementById(id);
if (canvas == null) { return false; }
var context = canvas.getContext('2d');
image.src = "tyl2.jpg";
image.onload = function(){
drawImg(canvas, context, image);
};
}
function drawImg(canvas, context, image){
//平鋪比例
var scale = 5;
//縮小后圖像寬度
var n1 = image.width / scale;
//縮小后圖像高度
var n2 = image.height / scale;
//平鋪橫向個(gè)數(shù)
var n3 = canvas.width / n1;
//平鋪縱向個(gè)數(shù)
var n4 = canvas.height / n2;
for(var i = 0;i < n3;i++) {
for(var j = 0;j < n4;j++) {
context.drawImage(image, i * n1, j * n2, n1, n2);
}
}
}
運(yùn)行效果如圖:

在H5中,要達(dá)到平鋪效果,還可以使用更簡(jiǎn)便的圖形上下文對(duì)象的createPattern方法。
context.createPattern(image,type);
該方法使用兩個(gè)參數(shù),image參數(shù)為要平鋪的圖像 ,type參數(shù)的值必須是下面的字符串值之一:
-
no-repeat:不平鋪 -
repeat-x:橫方向平鋪 -
repeat-y:縱方 向平鋪 -
repeat:全方向平鋪
創(chuàng)建了lmage對(duì)象并指定圖像文件后,使用createPattern方法創(chuàng)建填充樣式,然后將該樣式指定繪圖形上下文對(duì)象的fillStyle屬性,最后再填充畫(huà)布,就可以看到重復(fù)填充的效果了。
function draw(id) {
var image = new Image();
var canvas = document.getElementById(id);
if (canvas == null) { return false; }
var context = canvas.getContext('2d');
image.src = "tyl3.jpg";
image.onload = function() {
//創(chuàng)建填充樣式,全方向平鋪
var ptrn = context.createPattern(image, 'repeat');
//指定填充樣式
context.fillStyle = ptrn;
//填充畫(huà)布
context.fillRect(0,0,400,300);
};
}
圖像裁剪
圖像裁剪功能是指在畫(huà)布內(nèi)使用路徑,只繪制該路徑所包括區(qū)域內(nèi)的圖像,不繪制路徑外部的圖像。
使用圖形上下文對(duì)象的不帶參數(shù)的clip方法來(lái)實(shí)現(xiàn)Canvas元素的圖像裁剪功能。該方法使用路徑來(lái)對(duì)Canvas畫(huà)布設(shè)置一個(gè)裁剪區(qū)域。因此,必須先創(chuàng)建好路徑。路徑創(chuàng)建完成后,調(diào)用clip方法設(shè)置裁剪區(qū)域。
function draw(id) {
var canvas = document.getElementById(id);
if (canvas == null) { return false; }
var context = canvas.getContext('2d');
var gr = context.createLinearGradient(0, 400, 300, 0);
gr.addColorStop(0, 'rgb(255, 255, 0)');
gr.addColorStop(1, 'rgb(0, 255, 255)');
context.fillStyle = gr;
context.fillRect(0, 0, 400, 300);
image = new Image();
image.onload = function(){
drawImg(context, image);
};
image.src = "tyl.jpg";
}
function drawImg(context, image) {
create5StarClip(context);
context.drawImage(image, -50, -150, 300, 300);
}
function create5StarClip(context) {
var n = 0;
var dx = 100;
var dy = 0;
var s = 150;
context.beginPath();
context.translate(100, 150);
var x = Math.sin(0);
var y = Math.cos(0);
var dig = Math.PI / 5 * 4;
for(var i = 0; i < 5; i++) {
var x = Math.sin(i * dig);
var y = Math.cos(i * dig);
context.lineTo(dx + x * s, dy + y * s);
}
context.clip();
}
運(yùn)行效果如圖:

在上面的代碼中,把畫(huà)布背景繪制完成后,調(diào)用create5StarClip函數(shù)。在函數(shù)中,創(chuàng)建一個(gè)五角星的路徑,然后使用clip方法設(shè)置裁剪區(qū)域。
裁剪區(qū)域一旦設(shè)置好之后,后面繪制的所有圖形就都可以用這個(gè)裁剪區(qū)域。如果要取消這個(gè)已經(jīng)設(shè)置好的裁剪區(qū)域,需要使用繪制狀態(tài)的保存與恢復(fù)功能。這兩個(gè)功能保存與恢復(fù)圖形上下文的臨時(shí)狀態(tài)。在設(shè)置圖像裁剪區(qū)域時(shí),首先調(diào)用save方法保存圖形上下文的當(dāng)前狀態(tài),在繪制完經(jīng)過(guò)裁剪的圖像后,再調(diào)動(dòng)restore恢復(fù)之前保存的圖形上下文的狀態(tài),通過(guò)這種方法,對(duì)之后繪制的圖像取消裁剪區(qū)域。
function draw(id) {
var canvas =document.getElementById(id);
if(canvas == null) {return false; }
var context = canvas.getContext('2d');
var gr = context.createLinearGradient(0, 400, 300, 0);
gr.addColorStop(0, 'rgb(255, 255, 0)');
gr.addColorStop(1, 'rgb(0, 255, 255)');
context.fillStyle = gr;
context.fillRect(0, 0, 400, 300);
image = new Image();
image.onload = function() {
drawImg(context, image);
};
image.src = 'tyl.jpg';
}
function drawImg(context, image) {
create5StarClip(context);
context.drawImage(image, -50, -150, 300, 300);
}
function create5StarClip(context) {
var n = 0;
var dx = 100;
var dy = 0;
var s = 150;
context.beginPath();
context.translate(100, 150);
var x = Math.sin(0);
var y = Math.cos(0);
var dig = Math.PI / 5 * 4;
for(var i = 0; i < 5; i++) {
var x = Math.sin(i * dig);
var y = Math.cos(i * dig);
context.lineTo(dx + x * s, dy + y * s);
}
context.clip();
}
效果如圖:

像素處理
使用Canvas API能夠獲取圖像中的每一個(gè)像素,然后得到該像素顏色的rgb值或rgba值。使用圖形上下文對(duì)象的getImageData方法來(lái)獲取圖像中的像素。
var imageData = context.getImageData(sx, sy, sw, sh);
該方法使用四個(gè)參數(shù),sx、sy分別表示所獲取區(qū)域的起點(diǎn)橫坐標(biāo)、起點(diǎn)縱坐標(biāo),sw、sh分別表示所獲取區(qū)域的寬度和高度。
imagedata變量是一個(gè)CanvasPixelArray對(duì)象,具有height,width,data等屬性。data屬性是一個(gè)保存像素?cái)?shù)據(jù)的數(shù)組,內(nèi)容類似[ r1, g1, b1, a1, r2, g2 ,b2, a2, r3, g3, b3, a3, ...],其中,r1,g1,b1,a1為第一個(gè)像素的紅色值,綠色值,藍(lán)色值,透明度值;r2,g2,b2,a2分別為第二個(gè)像素的紅色值,綠色值,藍(lán)色值,透明度值,依此類推。data.length為所取得像素的數(shù)量。
var image = new Image();
var context = canvas.getContext('2d');
image.onload = function () {
context.drawImage(image, 0, 0);
var imageData;
context.drawImage(image, 0, 0);
imageData = context.getImageData(0, 0, image.width, image.height);
}
取得了這些像素后,就可以對(duì)這些像素進(jìn)行處理了,接下來(lái)可以進(jìn)行諸如蒙版處理、面部識(shí)別等較復(fù)雜的圖像處理操作。
下面的代碼用Canvas API將圖像進(jìn)行了反顯操作。在得到像素?cái)?shù)組后,將該數(shù)組中的每個(gè)像素顏色進(jìn)行了反顯操作,然后保存回像素?cái)?shù)組,最后使用圖形上下文對(duì)象的putImageData方法將反顯操作后的圖形重新繪制在畫(huà)布上。
context.putImageData(imageData, dx, dy[, dirtyX, dirtyY, dirtyWidth, dirtyHeight]);
該方法使用七個(gè)參數(shù),imageData為像素?cái)?shù)組,dx、dy分別表示重繪圖像的起點(diǎn)橫坐標(biāo)、起點(diǎn)縱坐標(biāo)。后面的四個(gè)參數(shù)為可選參數(shù),給出一個(gè)矩形的起點(diǎn)橫坐標(biāo)、起點(diǎn)縱坐標(biāo)、寬度和高度,如果加上后邊這四個(gè)參數(shù),則只繪制像素?cái)?shù)組中這個(gè)矩形范圍內(nèi)的圖像。
function draw(id) {
var canvas = document.getElementById(id);
if (canvas == null) { return false; }
var context = canvas.getContext('2d');
var image = new Image();
image.src = 'tyl.jpg';
image.onload = function () {
context.drawImage(image, 0, 0);
var imageData = context.getImageData(0, 0, image.width, image.height);
for(var i = 0,n = imageData.length; i < n; i += 4) {
imageData.data[i + 0] = 255 - imageData.data[i + 0]; // red
imageData.data[i + 1] = 255 - imageData.data[i + 1]; // green
imageData.data[i + 2] = 255 - imageData.data[i + 2]; // blue
}
context.putImageData(imageData, 0, 0);
}
}
對(duì)于像素操作只有部分瀏覽器支持。
繪制文字
可以在Canvas畫(huà)布中進(jìn)行文字的繪制,同時(shí)也可以指定繪制文字的字體、大小、對(duì)齊方式等,還可以進(jìn)行文字的紋理填充等。
繪制文字時(shí)可以使用fillText方法或strokeText方法。
fillText方法用填充方式繪制字符串。
void fillText(text,x,y,[maxWidth]);
該方法接受四個(gè)參數(shù),第一個(gè)參數(shù)text表示要繪制的文字,第二個(gè)參數(shù)x表示繪制文字的起點(diǎn)橫坐標(biāo),第三個(gè)參數(shù)y表示繪制文字的起點(diǎn)縱坐標(biāo),第四個(gè)參數(shù)maxWidth為可選參數(shù), 表示顯示文字時(shí)的最大寬度,可以防止文字溢出。
strokeText方法用輪廓方式繪制字符串。
void strokeText(text,x,y,[maxWidth]);
該方法參數(shù)部分的解釋與fillText方法相同。
在進(jìn)行文字的繪制之前,可以先對(duì)該對(duì)象的有關(guān)文字繪制的屬性進(jìn)行設(shè)置:
-
font屬性:設(shè)置文字字體 -
textAlign屬性:設(shè)置文字水平對(duì)齊方式,屬性值可以為start 、end、left、right、center。默認(rèn)值為start -
textBaseline屬性 : 設(shè)置文字垂直對(duì)齊方式,屬性值可以為top、hanging、middle、alphabetic、ideographic、bottom。默認(rèn)值為alphabetic
function draw(id) {
var canvas = document.getElementById(id);
if (canvas == null) { return false; }
var context = canvas.getContext('2d');
context.fillStyle = '#00f';
context.font = 'italic 30px sans-serif';
context.textBaseline = 'top';
//填充字符串
context.fillText('示例文字', 0, 0);
context.font = 'bold 30px sans-serif';
//輪廓字符串
context.strokeText('示例文字', 0, 50);
}
運(yùn)行效果:

在使用CSS樣式的時(shí)候,有時(shí)我們會(huì)希望能在文字周圍制作一個(gè)漂亮的邊框,在定義邊框?qū)挾鹊臅r(shí)候,我們需要首先計(jì)算出在這個(gè)邊框里最長(zhǎng)一行的文字的寬度。這時(shí),我們可以使用圖形上下文對(duì)象的measureText方能來(lái)得到文字的寬度。
metrics = context.measureText(text)
measureText方法接受一個(gè)參數(shù)text,該參數(shù)為需要繪制的文字,該方法返回一個(gè)TextMetrics對(duì)象, TextMetrics對(duì)象的width屬性表示使用當(dāng)前指定的字體后text參數(shù)中指定的文字的總文字寬度。
function draw(id) {
var canvas = document.getElementById(id);
if (canvas == null) { return false; }
var context = canvas.getContext('2d');
context.font = 'italic 20px sans-serif';
/* 定義繪制文字*/
var txt = "字符串的寬度為";
/* 獲取文字寬度 */
var tm1 = context.measureText(txt);
/* 繪制文字 */
context.fillText(txt, 10, 30);
context.fillText(tm1.width, tm1.width + 10, 30);
/* 改變字體 */
context.font = "bold 30px sans-serif";
/* 重新獲取文字寬度 */
var tm2 = context.measureText(txt);
/* 重新繪制文字*/
context.fillText(txt, 10, 70);
context.fillText(tm2.width, tm2.width + 10, 70);
}
效果如圖:

補(bǔ)充知識(shí)
保存與恢復(fù)狀態(tài)
save與restore這兩個(gè)方能均不帶參數(shù),分別保存與恢復(fù)圖形上下文的當(dāng)前繪畫(huà)狀態(tài)。在需要保存與恢復(fù)當(dāng)前狀態(tài)時(shí),首先調(diào)用save方法將當(dāng)前狀態(tài)、保存到棧中,在做完想做的工作后,再調(diào)用restore從棧中取出之前保存的圖形上下文的狀態(tài)進(jìn)行恢復(fù)。
var x, y;
for (var j = 1; j < 50; j++){
ctx.save() ;
// 改變繪畫(huà)狀態(tài),進(jìn)行想要的操作
ctx.fillStyle = '#fff';
x = 75 - Math.floor(Math.random() * 150);
y = 75 - Math.floor(Math.random() * 150);
ctx.translate(x, y);
drawStar(ctx, Math.floor(Math.random() * 4) + 2);
ctx.restore();
}
保存與恢復(fù)狀態(tài)可以應(yīng)用在以下場(chǎng)合:
- 圖像或圖形變形
- 圖像裁剪
- 改變圖形上下文的以下屬性:
fillStyle、font、lineWidth、shadowBlur、shadowColor、shadowOffsetX、shadowOffsetY、textAlign、strokeStyle、textBaseline、lineJoin、lineCap、miterLimit、globalAlpha、globalCompositeOperation。
保存文件
在畫(huà)布中繪制完成一幅圖形或圖像后,很多時(shí)候我們需要將該圖形或圖像保存到文件中, 使用Canvas API可以完成這步工作。
Canvas API保存文件的原理實(shí)際上是把當(dāng)前的繪畫(huà)狀態(tài)輸出到一個(gè)data URL地址所指向的數(shù)據(jù)中的過(guò)程,所謂data URL, 是指目前大多數(shù)瀏覽器能夠識(shí)別的一種base64位編碼的URL,主要用于小型的、可以在網(wǎng)頁(yè)中直接嵌入,而不需要從外部文件嵌入的數(shù)據(jù),比如img元素中的圖像文件等。data URL的格式類似于data:image/png;base64,iVBORdfoAAA...。
Canvas API使用toDataURL方法把繪畫(huà)狀態(tài)輸出到一個(gè)data URL中,然后重新裝載,客戶可直接把裝載后的文件進(jìn)行保存。
canvas.toDataURL(type);
參數(shù)type表示要輸出數(shù)據(jù)的MIME類型。
function draw(id) {
var canvas = document.getElementById(id);
if (canvas == null) { return false; }
var context = canvas.getContext('2d');
context.fillStyle = "rgb(0, 0, 255)";
context.fillRect(0, 0, canvas.width, canvas.height);
context.fillStyle = "rgb(255, 255, 0)";
context.fillRect(10, 20, 50, 50);
window.location = canvas.toDataURL("image/jpeg");
}
效果如圖:

簡(jiǎn)單動(dòng)畫(huà)的制作
在Canvas 中制作動(dòng)面實(shí)際上就是一個(gè)不斷擦除、重繪、擦除、 重繪的過(guò)程,具體步驟如下 :
- 預(yù)先編寫(xiě)好用來(lái)繪圖的函數(shù),在該函數(shù)中先用
clearRect方法將畫(huà)布整體或局部擦除。 - 使用
setInterval方法設(shè)置動(dòng)畫(huà)的間隔時(shí)間。
var context;
var width,height;
var i;
function draw(id) {
var canvas = document.getElementById(id);
if (canvas == null) { return false; }
context = canvas.getContext('2d');
width = canvas.width;
height = canvas.height;
i = 0;
setInterval(rotate, 100);
}
function rotate() {
context.clearRect(0, 0, width, height);
context.fillStyle = "red";
context.fillRect(i, 0, 20, 20);
i = i + 20;
}
上面的代碼將繪制一個(gè)紅色小方塊,使其在畫(huà)布中從左向右緩慢移動(dòng)。
效果如圖:

下面的代碼給出一個(gè)通過(guò)動(dòng)畫(huà)來(lái)循環(huán)顯示所有參數(shù)組合效果。
var globalId;
var i = 0;
function draw(id) {
globalId = id;
setInterval(Composite, 1000);
}
function Composite() {
var canvas = document.getElementById(globalId);
if (canvas == null) { return false; }
var context = canvas.getContext('2d');
var oprtns = new Array(
"source-atop",
"source-in",
"source-out",
"source-over",
"destination-atop",
"destination-in",
"destination-out",
"destination-over",
"lighter",
"copy",
"xor"
);
if(i > 10) { i=0; }
context.clearRect(0, 0, canvas.width, canvas.height);
context.save();
//繪制原有圖形(藍(lán)色長(zhǎng)方形)
context.fillStyle = "blue";
context.fillRect(10, 10, 60, 60);
//設(shè)置組合方式
context.globalCompositeOperation = oprtns[i];
//設(shè)置新圖形(紅色圓形)
context.beginPath();
context.fillStyle = "red";
context.arc(60, 60, 30, 0, Math.PI * 2, false);
context.fill();
context.restore();
i = i + 1;
}
效果如圖:
