# 圖形與動畫基礎：Canvas 與他的朋友們

::: tip 核心問題

以前的網頁只能展示乾巴巴的文字和圖片。但如果你想做打磚塊遊戲、華麗的動態特效、或是可以自由拖曳的資料報表，僅僅靠 `<div>` 是遠遠不夠的。這就是 **Canvas（畫布）** 誕生的原因。

本指南將帶你從畫下第一條線開始，一路打怪升級，最終親手寫出能在瀏覽器中流暢執行 60 幀的粒子引擎。

:::

---

## 1. Canvas 概述

如果說早期的網頁是用**樂高積木**（HTML 標籤）拼湊起來的靜態模型，那麼 HTML5 的 `<canvas>` 標籤就是扔給你一張**巨大的數位白紙**，然後遞給你一支靠程式碼控制的**畫筆**，剩下的全交給你自由發揮。

這裡面的畫沒有任何標籤結構。你用畫筆塗上去的心血，一旦落筆就變成了最純粹的**「像素顏料」**。

### 1.1 Canvas vs SVG：兩種不同流派的藝術家

在前端畫圖界，Canvas 有個宿敵叫 **SVG**。它們代表了兩種截然不同的繪畫觀念：

- **Canvas（點陣圖畫板）：**
  - **原理**：就像真實在紙上塗色，幾筆畫上去就變成一團顏料（像素點）。
  - **優勢**：電腦只管往螢幕上「灑顏料」，效能起飛！能同時畫出大幾千個活蹦亂跳的閃爍粒子。
  - **缺點**：畫完就沒法單獨反悔（沒法透過 DOM 節點選擇），且放大會造成馬賽克發虛。
- **SVG（向量圖拼接）：**
  - **原理**：就像做 PPT。你畫一個圓，它就生成一個獨立標籤的「圓實體」放在畫面上。
  - **優勢**：不管放大 100 倍還是 10 萬倍，永遠極為清晰。每個形狀都是獨立的 DOM 節點，你可以隨時用 CSS 和 JS 改變它的顏色或綁定點擊事件。
  - **缺點**：如果你試圖放幾萬個物件亂飛，繁重的 DOM 樹和排版引擎會直接把瀏覽器卡死。

**簡單總結：玩動態遊戲、做酷炫粒子特效用 Canvas；畫精密的 Logo、寫互動清晰的小圖表用 SVG。**

---

## 2. 第一筆：理解反直覺的座標系

### 座標軸翻轉的機制說明

當你準備下筆時，得先明白 Canvas 裡的尺子是反著的。對於傳統的數學課座標系，中心點零點在中間，越往上越大。但在電腦螢幕顯示領域，幾乎所有裝置的「原點（0，0）」都定在**螢幕的最左上角**。向右走 X 軸變大沒問題，但是**向下走，Y 軸變大。**

**Canvas 座標系統的核心原則：**
- **原生單位：** 像素 (px)，與螢幕物理像素 1:1 對應。
- **X 軸：** 向右為正方向，從 `0` 到 `canvas.width`。
- **Y 軸：** 向下為正方向，從 `0` 到 `canvas.height`。

👇 拖曳下面的小圓點，直觀感受電腦圖形學中的座標原點與走向：

<CoordinateSystemDemo />

### 2.2 給你的魔法畫筆上調料

有了座標體系，我們就能召喚畫筆了（程式碼中稱為 `Context`，或縮寫 `ctx`）。就如同拿著真實的調色盤作畫，Canvas 的 API 設計完美遵循了物理作畫的三個步驟：

1. **調色（State）**：透過 `fillStyle` 設定填充色，`strokeStyle` 設定描邊色。
2. **構形（Path）**：構思你是要畫一條線（`lineTo`）、還是一個圓（`arc`）、亦或一個矩形（`rect`）。
3. **極簡下筆（Render）**：決定是內部填充（`fill()`）還是勾勒邊緣（`stroke()`）。

由於 Canvas 是純粹的點陣圖畫布，「落子無悔」，你一旦畫下，它立刻乾涸成為像素，無法再被撤銷為獨立物件。

👇 嘗試在下面的演示中挑選不同形狀和顏色，看看背後的程式碼是如何執行上述「三步走」的：

<CanvasBasicsDemo />

---

## 3. 逐幀動畫：Flipbook 模式與平滑優化

既然 Canvas 一旦填色就變成了永久的像素，那麼各種 HTML5 頁遊裡滿屏亂跑的角色是怎麼做出來的？

答案是**「騙過你的眼睛」**。這和手翻動畫書或者電影膠片的原理一模一樣。

1. **擦黑板（Clear）：** 用 `clearRect()` 把整塊畫布上的內容毫不留情地清空。
2. **計算新位置（Update）：** 讓角色的 X 座標往前偷偷加 2 個像素點。
3. **下筆重畫（Render）：** 把角色在新的位置重新畫一次。
4. **瘋狂迴圈（Loop）：** 結合瀏覽器內建的極為精準的節拍器 `requestAnimationFrame`。它會以顯示器的重新整理率（通常是每秒 60 次，即 60 FPS）重複這三個動作。

由於人眼自帶「視覺殘留」，在每秒 60 次的【擦除 -> 更新 -> 重繪】中，你看到的不僅不是閃爍的黑板，反而是如同絲綢般順滑的動畫。

👇 在下方的演示中調整播放速度，觀察每一幀的位移是如何連綴成流暢運動的：

<AnimationLoopDemo />

---

## 在 Canvas 裡面點擊互動的實作方法

因為 Canvas 畫布在瀏覽器眼裡只是一張沒有任何結構的「顏料布」。假設你在畫布上用 `arc()` 畫了一隻怪獸，當你想要實現「點擊怪獸扣血」時，你**根本沒法**使用傳統的 `document.getElementById` 來取得這個怪獸。因為在 HTML 結構中，只有那個寬 600 像素的死板 `<canvas>` 標籤。

這就是圖形程式設計中最經典的問題：**碰撞檢測 (Collision Detection) 與事件代理**。

由於瀏覽器只知道你的滑鼠點擊了 Canvas 的螢幕座標 `(x, y)`，你需要自己去透過國中的幾何數學進行反算：
- **對於圓形：** 透過勾股定理計算 `滑鼠點擊處` 到 `圓心位置` 的距離，如果距離小於半徑，則說明「被點中了」。
- **對於矩形：** 判斷點擊的 `x` 是否在矩形的左右邊界內，同時 `y` 是否在上下邊界內。

無論你的畫布上有多少元素，滑鼠懸停或點擊事件永遠是綁定在 Canvas 這個唯一容器上的，這就是終極的「事件委託」。

👇 試著在下面使用滑鼠（點擊、拖曳、懸停）或鍵盤（方向鍵移動），體會這種「手動算距離」的底層互動邏輯：

<EventHandlingDemo />

---

## 5. 解放算力：粒子系統與視覺魔法

到了這一步，當我們把「座標系」、「動畫迴圈」以及「顏色與形狀」全部融合，並將其數量暴增到成百上千個微小碎片時，你就掌握了引爆視覺的終極殺器：**粒子系統（Particle System）**。

其核心思路極為粗暴且有效：
1. 建立一個巨大的陣列，裡面塞滿了幾百個獨立的「粒子物件」。
2. 每個物件擁有自己的獨立生命週期（`life`）、加速度（`vx/vy`）、重力阻尼（`gravity`）。
3. 每次 `requestAnimationFrame` 觸發時，遍歷更新這幾百個粒子，然後渲染，最後悄悄清理掉那些「死亡」（生命值耗盡/掉出螢幕）的粒子。

你的瀏覽器一瞬間就能變成一台製造煙花、大雪和爆炸的夢工廠。

👇 點擊不同的效果，調整重力與粒子數，觀察它們是如何透過最簡單的物理數學公式呈現出複雜的群體視覺：

<ParticleSystemDemo />

---

## 6. 守護 FPS 榮耀：應對高燒的 CPU 的方法

讓成千上萬個物件在一秒內計算並重畫 60 遍是非常消耗效能的。如果毫無章法，你的電腦風扇很快就會起飛。

以下是真正引擎大佬用來搶救幀率的「護體絕技」：

1. **局部擦黑板（髒矩形 Dirty Rect）：**
   一個角色在寬廣的草原上奔跑，你千萬不要每幀去 `clearRect` 整片大草原！角色經過哪一小塊，你就用「小板擦」擦掉那一塊並覆蓋重繪，效能立刻飆升指數倍。

2. **後台替身魔法（離屏 Canvas）：**
   如果背景是繁星漫天、有著各種複雜絢麗的山脈，每次都即時渲染太蠢了。我們通常在記憶體裡偷偷建一個看不見的 `<canvas>`，把它精美地畫上去一次。之後的每一幀重新整理中，只需要透過 `drawImage()` 將這張合成好的「靜態底片」直接貼出，免去了海量的基礎計算。

3. **批量洗畫筆（Batching）：**
   調色盤裡從紅色換到藍色，在底層是昂貴的。如果畫布上有 1000 個紅色圓和 1000 個藍色圓交叉散落。最快的方法是：先把紅顏料準備好，遍歷畫完所有紅圈，再換藍顏料畫所有藍圈。這是著名的批量渲染（Batch Rendering）思想。

👇 將物件數量拉到 3000 以上，看著網頁掉進卡頓的深淵，再依次開啟右下方的「最佳化技術」開關，親眼見證實打實的幀率搶救：

<PerformanceDemo />

---

## 7. 專業名詞總結

| 術語 | 通俗解釋 |
| --- | --- |
| **Canvas** | HTML5 提供的 2D 畫布。繪製極快，但畫完就變成顏料像素，不支援透過 DOM 操作內容。 |
| **SVG** | 向量圖。放大永遠不模糊，且每個圖形都是獨立的標籤元素，可以輕易綁定各種 CSS 樣式和互動。 |
| **Context (ctx)** | 你申請到的那支「2D 魔法畫筆」，用來調色、設定形狀和繪製各種特殊效果。 |
| **requestAnimationFrame** | 瀏覽器內建的神級節拍器，會嚴格依照顯示器的重新整理率執行回呼，是製作絲滑動畫的不二之選。 |
| **FPS (Frame Rate)** | 幀率。60 FPS 代表一秒內瀏覽器幫你無縫擦除了 60 次畫布並重畫了 60 幅新圖。 |
| **髒矩形 (Dirty Rect)** | 只在發生變化的那一點微小區域內進行精準擦除和重繪，從而強力保留效能。 |
| **離屏 Canvas** | 藏在記憶體裡的「影子畫布」。把極度複雜但不會動的景物提前畫好，以後就當死貼圖拿來重複使用。 |

> 從一條簡單的直線段，到宏大絢麗的粒子系統引擎；一切看似魔法的特效，不過是每秒 60 次的座標計算與重繪輪迴罷了。
