H5 canvas

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、widthheight三個(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ì)用到moveTolineTo兩種方法。
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。
接著把fillStylestrokeStyle設(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、scalerotate方法,然后執(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、scalerotate這三個(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)裝載圖像文件。 xy為繪制時(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ù)制的圖像文件,sxsy分別表示源圖像的被復(fù)制區(qū)域在畫(huà)布中的起始橫坐標(biāo)與起始縱坐標(biāo),swsh表示被復(fù)制區(qū)域的寬度與高度,dxdy表示復(fù)制后的目標(biāo)圖像在畫(huà)布中的起始橫坐標(biāo)與起始縱坐標(biāo),dwdh表示復(fù)制后的目標(biāo)圖像的寬度與高度。該方法可以只復(fù)制圖像的局部,只要將sxsy設(shè)為局部區(qū)域的起始點(diǎn)坐標(biāo), 將swsh設(shè)為局部區(qū)域的寬度與高度就可以了。該方能也可以用來(lái)將源圖像進(jìn)行縮放,只要將dwdh設(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),swsh分別表示所獲取區(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,g1b1,a1為第一個(gè)像素的紅色值,綠色值,藍(lán)色值,透明度值;r2,g2,b2a2分別為第二個(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ù)組,dxdy分別表示重繪圖像的起點(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)

saverestore這兩個(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;
}

效果如圖:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • ??HTML5 添加的最受歡迎的功能就是 元素。這個(gè)元素負(fù)責(zé)在頁(yè)面中設(shè)定一個(gè)區(qū)域,然后就可以通過(guò) JavaScri...
    霜天曉閱讀 3,177評(píng)論 0 2
  • canvas基本標(biāo)簽 < /canvas>寬高寫(xiě)在內(nèi)部跟樣式有區(qū)別的(樣式設(shè)置-畫(huà)出的圖形寬高改變,內(nèi)部設(shè)置-畫(huà)出...
    閆子揚(yáng)閱讀 492評(píng)論 0 0
  • --繪圖與濾鏡全面解析 概述 在iOS中可以很容易的開(kāi)發(fā)出絢麗的界面效果,一方面得益于成功系統(tǒng)的設(shè)計(jì),另一方面得益...
    韓七夏閱讀 2,960評(píng)論 2 10
  • 晚上在為班級(jí)整理信息表格時(shí),剛上高一的妹妹冷不丁發(fā)了張截圖給我,是她在QQ空間在她自己的留言板留了言,內(nèi)容是她...
    小肥肥肉圓閱讀 783評(píng)論 0 2
  • 大部分時(shí)候都談不少收獲,只是經(jīng)歷過(guò)后,發(fā)現(xiàn)原來(lái)自己還這樣生活過(guò)。 一個(gè)和我一樣喜歡看玄幻小說(shuō)的朋友發(fā)問(wèn),你說(shuō)我和你...
    你的眼睛好大閱讀 571評(píng)論 0 0

友情鏈接更多精彩內(nèi)容