對於慣於使用 CSS 來設定 Element 外觀的前端開發者而言,對於 Canvas 可能會有些抗拒,原因是 Canvas 所有的呈現圖像皆須以 JavaScript 手動刻出來,雖然很麻煩但是能製作出來非常炫麗的效果,了解原理以後在撰寫方面也比較自由。

在開始之前首先我們必須針對不支援 Canvas 的瀏覽器作處理。(儘管目前大多數瀏覽器都支援,但我認為仍需添加這機制,就算是提醒用戶升級瀏覽器這樣對於整體網站的顧客友善度也是好的

<canvas>
  <p>Your Broswer not support canvas</p>
</canvas>

運用瀏覽器不認識 Canvas 會忽略它的這個特點,可在 Canvas 區塊內添加所謂的替代顯示方案,可以加上文字、圖片、甚至是 Flash。

有了替代顯示方案後,我們可以開始著重在 Canvas 本身的撰寫,首先我們先創造一個 Canvas Element 並且用 Canvas 本身的 width 、 height 屬性設定寬高。大多數的開發者可能會習慣用 CSS 來設定,但此舉會造成在 Canvas 上繪製的圖像變形

<canvas id="game" width="320px" height="240px">
  <p>Your Broswer not support canvas</p>
</canvas>
#game {
  border: 2px dashed lightblue;
}

下面是上面代碼的呈現效果,我們可以得到一個淡藍色的虛線框畫布。

Your Broswer not support canvas


有了畫布,我們就可以開始撰寫 JavaScript 代碼。

要在 Canvas 上畫圖要先認識 getContext() 方法,它是畫面顯示的管理員,任何圖像都需要經過它繪製、呈現。

如果講白話一點 Canvas 是一個白板,getContext() 是取得筆、板擦這些工具,JavaScript 則是拿工具在白板上畫的人,寫 JavaScript 的人是神(大誤)

var canvas = document.getElementById("game");
var ctx = canvas.getContext("2d");

如上方代碼,我們用 getContext("2d") 取得了畫圖用的工具。

到這裡也許有人有疑問,為什麼是 2d?那是因為我們接下來要畫的是 2d 的圖像,所以取得 2d 的工具來作畫。也許未來會有 3d、4d、5d(誇飾法)。而時下最炫麗的 WebGL 則是採用 getContext("webgl")getContext("experimental-webgl") 方法。

再來我們要認識取得的 2d 工具裡面有些甚麼東西可以用,最基礎的有三個。

  • stroke(輪廓)
  • fill(色塊)
  • clear(板擦)

針對 stroke(輪廓)部分,先來看一段代碼演示。

var canvas = document.getElementById("game");
var ctx = canvas.getContext("2d");
drawStroke(ctx, {x:30, y:30}, {x:150, y:30}, 8, "lightgreen", "butt");
drawStroke(ctx, {x:30, y:50}, {x:150, y:50}, 10, "rgb(200, 200, 0)", "round");
drawStroke(ctx, {x:30, y:70}, {x:150, y:70}, 12, "rgba(255, 0, 0, 0.2)", "square");

function drawStroke(context, xy, txy, thickness, color, cap) {
  context.beginPath();
  context.moveTo(xy.x, xy.y);
  context.lineTo(txy.x, txy.y);
  context.lineWidth = thickness ; 
  context.strokeStyle = color;
  context.lineCap = cap;
  context.stroke();
}

由上方代碼我們可以得到三個顏色形狀皆不同的線段,由於畫線段是個重複指令極多的動作,因此我寫了一個 drawStroke 的函式來統一處理。

  • beginPath()
    就如他的字面上的意義一樣,開始一個新的路徑。如果沒有下這個指令的話,當執行 stroke() 時,你之前設定的線段也都會再畫一次。
  • moveTo()、lineTo()
    設定線段的起始點、終點。
  • lineWidth
    線段的粗細。
  • strokeStyle
    設定線段的顏色。從上方代碼可以知道設定顏色的方式可以接受 rgb()、rgba() 的方式,因此能實現透明度線段的功能
  • lineCap 
    設定線段的結尾方式,共有三種 buff(方形)、round(圓形)、square(方形,但會額外增長線段寬度的一半長度)
  • stroke()
    將設定的線段畫在畫布上。特別要注意的是每次執行 stroke() 時,它只會實現最後一次 beginPath() 之後的設定。

經過這些指令就可以畫出一個簡單的線段。也許有些人現在會覺得[就只是畫線段,這超麻煩的,而且又沒甚麼特別炫。]但這看起來簡單、樸素的東西,卻是之後華麗特效的關鍵(像是彈跳的圖表、粒子特效、遊戲連連看的即時演算線段)。

而在繪製輪廓時,總會遇到兩線相交的時刻,此時如果想設定交點的樣式怎麼辦呢?

透過 lineJoin 就可以達到這需求,看下列代碼。

var canvas = document.getElementById("game");
var ctx = canvas.getContext("2d");
ctx.moveTo(150, 50);
ctx.lineTo(50, 100);
ctx.lineTo(100, 200);
ctx.lineWidth = 60 ; 
ctx.lineJoin = "round";
ctx.stroke();

上方代碼我們可以得到兩條相交的線段,且其交點為圓角,會得到這結果是因為設定了 lineJoin = "round" 的關係。

了解了輪廓的畫法後,再來看看 fill(色塊),其實用法跟 stroke() 差不多,先來看一段代碼演示。

var canvas = document.getElementById("game");
var ctx = canvas.getContext("2d");
ctx.moveTo(50, 50);
ctx.lineTo(0, 100);
ctx.lineTo(100, 200);
ctx.fillStyle = "rgba(200, 200, 100, 0.5)";
ctx.closePath();
ctx.fill();

從代碼可以看到三個沒見過的指令 closePath()、fill()、fillStyle。

  • closePath()
    把現在的線段關閉成一個封閉的形狀,會從最近一次的起點、終點之間畫條線。
  • fill()
    將目前的封閉圖案填色,必須要有兩條線段以上才看得出效果。值得一提的是就算沒有 closePath() 它一樣可以畫出一個封閉圖像,但這在代碼撰寫上實在不是一個好的習慣
  • fillStyle
    用法跟 strokeStyle 一樣,一樣能接收 rgb()、rgba()。

現在我們能透過 stroke()fill() 來畫出圖案,但要能擦掉才是重點啊!其實用法很簡單,我們來看一段代碼演示。

var canvas = document.getElementById("game");
var ctx = canvas.getContext("2d");
ctx.moveTo(50, 50);
ctx.lineTo(0, 100);
ctx.lineTo(100, 200);
ctx.fillStyle = "rgba(200, 200, 100, 0.5)";
ctx.closePath();
ctx.fill();
ctx.clearRect(0, 0, 100, 100);

clearRect(x, y, width, height) ,x、y 代表著清除圖形的起點座標,width、height 代表清除的範圍。

以此範例而言,會在座標(0, 0) 的位置將範圍寬 100 高 100 內的所有圖案清除掉。


雖然學會以上方法我們已經能夠畫出眾多不同的形狀組合,但一點一滴用 moveTo、lineTo 實在有些刻苦,當然這點可以透過我們自己寫函式來簡化,除此之外也有內建的函示可以調用

fillRect()strokeRect() 分別可以繪製出方形的色塊、輪廓,以下是代碼演示。

var canvas = document.getElementById("game");
var ctx = canvas.getContext("2d");
ctx.strokeStyle = "lightblue";
ctx.strokeRect(10, 10, 50, 50);
ctx.fillStyle = "lightgray";
ctx.fillRect(100, 10 , 50, 50);

由上方代碼可以得到一個淺藍的方形輪廓跟淺灰的方形色塊。


arrow
arrow

    法奇 發表在 痞客邦 留言(0) 人氣()