---
# System prepended metadata

title: 用Canvas畫一個小城堡
tags: [Canvas, 滑鼠互動]

---

# 用Canvas畫一個小城堡

在繪製canvas的時候，因為我們會持續的更新影格，所以可以建立一個 draw()的function，讓要執行的動作都寫在裡面，這樣才能夠不斷的呼叫。

```
function draw(){
	....
}
draw()
```
## 靜態圖像
### 1.繪製背景網格
![](https://i.imgur.com/9R5FyHt.png)
網格需要每隔50px畫一條線，所以利用for迴圈找出間隔50px的距離，並畫上線與寫上刻度
```
// 網格背景
function draw() {
	ctx.beginPath();
	for (var i = 0; i < 10; i++) {
		let pos = i * 50;
		//垂直線
		ctx.moveTo(pos, 0);
		ctx.lineTo(pos, 400);
		//寫刻度
		ctx.fillText(pos, pos, 10);
		
		
		//水平線
		ctx.moveTo(0, pos);
		ctx.lineTo(400, pos);
		//寫刻度
		ctx.fillText(pos, 10, pos);
		
	}
	ctx.strokeStyle="rgba(0,0,0,0.1)"
	ctx.stroke();
}
```


### 2.繪製內容物
內容物的積木因為都是矩形，所以可以用rect來畫，畫法有兩種，一個是畫單一矩形，另一個是利用bsginPath()開始一個路徑，等全部畫完之後，再一起填塞

![](https://i.imgur.com/zNOsvvS.png)


單一路徑畫法:
```
// 	紅色方塊(一次只畫一塊)
ctx.fillStyle = "#e86920";
ctx.fillRect(300, 300, 50, 50);
ctx.strokeRect(300, 300, 50, 50);
```

多重路徑畫法:
```
// 	畫多塊之後再一起上色
ctx.beginPath();
ctx.rect(50, 300, 50, 50);
ctx.rect(250, 250, 50, 100);
ctx.fillStyle = "#fcd92d";
ctx.fill();
ctx.stroke();
```

半圓的畫法:
這邊須注意因為圓型的繪圖方向為逆時針，基礎會是一個朝下畫的圓型，若需要改編繪圖方向，則需再最後加上一個參數回`true`
```
// 拱門 從左上角的點開始連
	ctx.beginPath();
	ctx.moveTo(100, 200);
	ctx.lineTo(250, 200);
	ctx.lineTo(250, 250);
	ctx.lineTo(200, 250);
	ctx.arc(175, 250, 25, 0, Math.PI, true);
	ctx.lineTo(100, 250);
	ctx.closePath();
	ctx.fillStyle = "white";
	ctx.fill();
	ctx.stroke();
```


以此類推就可以畫出一個小城堡了~

![](https://i.imgur.com/E0bCgWO.png)



## 動畫

### 1.製作車子往前跑
利用setInterVal，每隔300ms就執行一次draw()，並且定義一個time的變數，每執行一次就會+1，就可以把持續更新的數值給車子的X軸，記得要把上一個畫面給清除`ctx.clearRect(0, 0, 400, 400);`，如果不清除上一格畫面的話，車子就會這樣留下軌跡
![](https://i.imgur.com/G6wjgFA.png)


X值越大車子就會越往左，超過440就會永遠消失了。所以為了要讓車子可以不斷地從左方進來，需要限制車子跑的範圍，可以利用餘數的方式`let carx = (time % 440) - 40;`把x軸控制在0~400之間，這樣車子就可以順利跑起來了!

![](https://i.imgur.com/1Gj3vJs.gif)


### 2.抓到滑鼠位置
先在canvas上建立監聽mousemove事件，抓到滑鼠的offsetX與offsetY後，把它存在物件內，方便取得
```
// 監聽滑鼠位置讓draw()內的程式可以使用
canvas.addEventListener("mousemove", function (evt) {
	mouse.x = evt.offsetX;
	mouse.y = evt.offsetY;
});
```

取得數值之後，我們要讓旗幟的Y軸可以上下移動，需要改變的點有三個，皆為Y軸，所以用剛剛取得的滑鼠Y位置，來變成Y軸移動的範圍，為了讓變動的數值可被控制，所以都/5來減少移動的範圍
`滑鼠移動最大的範圍為0-400，而屋頂距離上方畫布的距離只有150，所以要控制在0-150之內`

![](https://i.imgur.com/jNTRR1K.png)

```
//旗子
ctx.beginPath();
ctx.moveTo(175, 150);
ctx.lineTo(175, 100 - mouse.y / 5);
ctx.lineTo(200, 110 - (time % 5)- mouse.y / 5);
ctx.lineTo(175, 120 - mouse.y / 5);
ctx.closePath();
ctx.fillStyle = "hsl("+(mouse.x % 360)+",50%,50%)";
ctx.fill();
ctx.stroke();
```

- (time % 5 ):是為了上旗子的抖動可以控制在5px左右，因為要持續抖動，所以是用time時間數值去控制
- `ctx.fillStyle = "hsl("+(mouse.x % 360)+",50%,50%)"` 讓旗子在滑鼠左右移動時可以變色

[Codepen範例](https://codepen.io/abbyd0208/pen/jOyzYqm?editors=0010)

###### tags: `Canvas`,`滑鼠互動`