HTML5 的 canvas 元素使用 JavaScript 在网页上绘制图像。画布是一个矩形区域,您可以控制其每一像素。canvas 拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法。
內容來源:http://www.w3school.com.cn/html5/html_5_canvas.asp

是的,這次要帶給大家的是 Canvas 標籤的基礎教學,它就像一塊畫布,能讓你在 HTML 標籤當中繪圖、動畫、遊戲,那我們就來個簡單的範例吧,就是做出一款經典的打磚塊遊戲!那我們首先要建立一個畫布,這個畫布大小是 480 * 320 的大小,並且在下方會有個開始遊戲的按鈕,按下去後,才開始執行遊戲:

HTML

1
2
3
4
5
6
7
<!-- 建立一個 canvas 標籤,id 為 canvas_1,大小為 480 * 320 -->
<canvas id="canvas_1" width="480" height="320">
<!-- 倘若使用者的瀏覽器並不支援 canvas,將會顯示該段內容。 -->
您的瀏覽器必須支援 HTML5 標籤語法,才能遊玩該遊戲。
</canvas>
<br />
<button>開始遊戲</button>

實例

既然是經典的打磚塊遊戲,那麼應該要會有顆球在畫布當中跑來跑去嘛!所以我們要在 button 標籤當中加入 onclikc 事件,去啟動 JavaScript 的方法:

HTML

1
2
3
4
5
6
<!-- 建立一個 canvas 標籤,id 為 canvas_1,大小為 480 * 320 -->
<canvas id="canvas_2" width="480" height="320">
<!-- 倘若使用者的瀏覽器並不支援 canvas,將會顯示該段內容。 -->
您的瀏覽器必須支援 HTML5 標籤語法,才能遊玩該遊戲。
</canvas>
<button onclick="canvasGameStart_canvas_2()">開始遊戲</button>

JavaScript

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
function canvasGameStart_canvas_2() {
// 抓取 id 為 canvas_1 的物件。
var canvas = document.getElementById("canvas_2");
// 建立繪圖物件,目前只支援 2d
var ctx = canvas.getContext("2d");

// 球體的座標
var ballPositionX = canvas.width / 2;
var ballPositionY = canvas.height / 2;

// 球體移動的速度
var ballMoveX = 2;
var ballMoveY = 2;

function drawBall() {
// 開始一條路徑(繪圖),或者重置當前的路徑(繪圖)
ctx.beginPath();
// 繪製弧線或曲線
ctx.arc(ballPositionX, ballPositionY, 10, 0, Math.PI * 2);
// 繪製顏色、漸變或模式
ctx.fillStyle = "#0095DD";
// 填充當前的圖案
ctx.fill();
// 關閉當前繪圖
ctx.closePath();
}

function draw() {
// 清除畫面 clearPect(左上角的X座標, 左上角的Y座標, 寬度, 高度)
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 重新畫出球體
drawBall();

// 重新計算球體下一次的位置
ballPositionX += ballMoveX;
ballPositionY += ballMoveY;
}

// setInterval(function, delay)
// 每隔 delay 毫秒,執行輸入 function(函數)
setInterval(draw, 10);
}

這邊要稍微特別解釋一下 arc() 這方法,可以參考下圖:

W3C arc() image

1
2
3
4
5
6
7
// x                = 圓中心的 X 座標。
// y = 圓中心的 Y 座標。
// r = 圓的半徑。
// sAngle = 起始角度,以弧度計算。
// eAngle = 結束角度,以弧度計算。
// counterclockwise = 定義順時針繪圖還是逆時針繪圖,True 為順時針;False 為逆時針。
context.arc(x, y, r, sAngle, eAngle, counterclockwise);

實例

您的瀏覽器必須支援 HTML5 標籤語法,才能遊玩該遊戲。

然後那顆球就這麼的直直掉下去了,這並不是我們想要的!我們希望他碰到牆壁的時候,能夠反彈,如果更白話一點來講的話,如果球體碰到畫框的邊界,就讓球體以反方向彈回去,那麼我們會修改到 JavaScript 的一些 function:

#JavaScript

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 定義球體的半徑
var ballRadius = 10;
var ballMoveX = 2;
var ballMoveY = 2;

function draw() {
// Code ...

// 計算球體下一次 X 座標的位置
var ballNextPositionX = ballPositionX + ballMoveX;
// 如果碰到右邊(canvas.width-ballRadius) 或者左邊(ballRadius)
if(ballNextPositionX > canvas.width-ballRadius || ballNextPositionX < ballRadius)
// 改變 X 軸前進座標的方向
ballMoveX *= -1;

// 計算球體下一次 Y 座標的位置
var ballNextPositionY = ballPositionY + ballMoveY;
// 如果碰到底端(canvas.height-ballRadius) 或者頂端(ballRadius)
if(ballNextPositionY > canvas.height-ballRadius || ballNextPositionY < ballRadius)
// 改變 Y 軸前進座標的方向
ballMoveY *= -1;

// Code ...
}

實例

您的瀏覽器必須支援 HTML5 標籤語法,才能遊玩該遊戲。

接下來我們要來繪製出給使用者操縱的板子,並且放置於底部:

JavaScript

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// 板子的高度及寬度
var paddleHeight = 10;
var paddleWidth = 75;

// 將板子放置在底部中間
var paddleX = (canvas.width-paddleWidth)/2;

// 紀錄左右鍵是否被按下了
var rightPressed = false;
var leftPressed = false;

// 增加事件(keydown = 當鍵盤上有按鍵被按下,keyup = 當鍵盤上有按鍵被放開)
// addEventListener(event, function, useCapture)
// event = 事件名稱的字符串。
// function = 事件觸發後執行的函數。
// useCapture = 是否在捕獲階段執行。
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);

// 當鍵盤上有按鍵被按下了
function keyDownHandler(e) {
switch(e.keyCode) {
// 右鍵鍵盤控制代碼
case 39:
rightPressed = true;
break;

// 左鍵鍵盤控制代碼
case 37:
leftPressed = true;
break;
}
}

// 當鍵盤上有按鍵被放開了
function keyUpHandler(e) {
switch(e.keyCode) {
// 右鍵鍵盤控制代碼
case 39:
rightPressed = false;
break;

// 左鍵鍵盤控制代碼
case 37:
leftPressed = false;
break;
}
}

// 繪製板子
function drawPaddle() {
// 開始繪製
ctx.beginPath();
// 繪製矩形 rect(左上方X座標, 左上方Y座標, 矩形寬度, 矩形高度)
ctx.rect(paddleX, canvas.height-paddleHeight, paddleWidth, paddleHeight);
// 繪製顏色、漸變或模式
ctx.fillStyle = "#0095DD";
// 填充當前的圖案
ctx.fill();
// 結束繪製
ctx.closePath();
}

function draw() {
// Code ...

// 板子向右邊移動,但還沒超出範圍
if(rightPressed && paddleX < canvas.width-paddleWidth)
paddleX += 7;

// 板子向左邊移動,但還沒超出範圍
if(leftPressed && paddleX > 0)
paddleX -= 7;

// Code ...
}

實例

您的瀏覽器必須支援 HTML5 標籤語法,才能遊玩該遊戲。

到目前為止都還蠻正常的,但還是有些許小缺點,我們接下來希望球體碰到底下時,代表我們的板子並沒有接到球體,遊戲必須重新開始,而球體碰到我們的板子時,才執行反彈球體的動作,所以我們必須修改 draw() 的一些寫法:

JavaScript

1
2
3
4
5
6
7
8
9
10
// 原本的寫法
function draw() {
// Code ...

var ballNextPositionY = ballPositionY + ballMoveY;
if(ballNextPositionY > canvas.height-ballRadius || ballNextPositionY < ballRadius)
ballMoveY *= -1;

// Code ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 修改過後的寫法
function draw() {
// Code ...

// 球體下一次 Y 軸的位置
var ballNextPositionY = ballPositionY + ballMoveY;
// 檢查球體 Y 軸位置有沒有碰到頂端
if(ballNextPositionY < ballRadius)
// 反轉移動方向
ballMoveY *= -1;
// 檢查球體 Y 軸有沒有碰到底端
if(ballNextPositionY > canvas.height-ballRadius)
// 再次檢查球體 Y 軸有沒有碰到板子
if(ballPositionX > paddleX && ballPositionX < paddleX + paddleWidth)
// 反轉移動方向
ballMoveY *= -1;

// Code ...
}

實例

您的瀏覽器必須支援 HTML5 標籤語法,才能遊玩該遊戲。

是的!球體如果沒有碰到我們的板子,那球體將會直直的掉下去了,球體如果碰到我們的板子,那球體將會反彈,基本物件都做好了,那我們接下來必須開始做我們的磚塊了!

JavaScript

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
var brickRowCount       = 3;    // 磚塊列數
var brickColumnCount = 5; // 磚塊行數
var brickWidth = 75; // 磚塊寬度
var brickHeight = 20; // 磚塊高度
var brickPadding = 10; // 磚塊間隔空白
var brickOffsetTop = 30; // 磚塊離上邊牆距離
var brickOffsetLeft = 30; // 磚塊離左邊牆距離

// bricks 初值為空陣列
var bricks = [];

// 把磚塊在螢幕上的位置保存在一個二維陣列中,共有 brickColumnCount 行,brickRowCount 列,也將用於以後的碰撞檢測
// brickColumnCount 行迴圈
for(column = 0; column < brickColumnCount; column++) {
// 初值為空陣列
bricks[column] = [];
// brickRowCount 列迴圈
for(row = 0; row < brickRowCount; row++)
// 將每一個磚塊 X、Y 軸座標初始值設定 0
bricks[column][row] = {
x: 0,
y: 0
};
}

function drawBricks() {
// brickColumnCount 行迴圈
for(column = 0; column < brickColumnCount; column++) {
// brickRowCount 列迴圈
for(row = 0; row < brickRowCount; row++) {
// 磚塊 X 軸 = (行數 * (磚塊寬度 + 磚塊間隔空白)) + 磚塊離左邊牆距離
var brickX = (column * (brickWidth + brickPadding)) + brickOffsetLeft;
// 磚塊 Y 軸 = (列數 * (磚塊高度 + 磚塊間隔空白)) + 磚塊離上邊牆距離
var brickY = (row * (brickHeight + brickPadding)) + brickOffsetTop;

// 磚塊 x 軸座標
bricks[column][row].x = brickX;
// 磚塊 y 軸座標
bricks[column][row].y = brickY;

// 開始繪製磚塊
ctx.beginPath();
ctx.rect(brickX, brickY, brickWidth, brickHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
}
}

function draw() {
// Code ...

// 開始繪製磚塊
drawBricks();

// Code ...
}

實例

您的瀏覽器必須支援 HTML5 標籤語法,才能遊玩該遊戲。

參考資料