Skip to content

圖形與動畫(Canvas 與他的朋友們)

核心問題

以前的網頁只能展示乾巴巴的文字和圖片。但如果你想做打磚塊遊戲、華麗的動態特效、或是可以自由拖曳的資料報表,僅僅靠 <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. 第一筆:理解反直覺的座標系

2.1 這張紙的上下怎麼顛倒了?

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

Canvas 座標系統的核心原則:

  • 原生單位: 像素 (px),與螢幕物理像素 1:1 對應。
  • X 軸: 向右為正方向,從 0canvas.width
  • Y 軸: 向下為正方向,從 0canvas.height

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

Canvas Width:600px
Canvas Height:400px
Mouse Position:(0, 0)

2.2 給你的魔法畫筆上調料

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

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

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

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

🎨Canvas 基础用代码画图(通俗说:编程画板)

3. 翻頁動畫書:如何讓畫面動起來極度絲滑

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

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

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

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

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

FPS:0
Frame:0

4. 瞎子摸象:在 Canvas 裡面怎麼做點擊互動?

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

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

由於瀏覽器只知道你的滑鼠點擊了 Canvas 的螢幕座標 (x, y),你需要自己去透過國中的幾何數學進行反算:

  • 對於圓形: 透過勾股定理計算 滑鼠點擊處圓心位置 的距離,如果距離小於半徑,則說明「被點中了」。
  • 對於矩形: 判斷點擊的 x 是否在矩形的左右邊界內,同時 y 是否在上下邊界內。

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

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

Instructions / 操作说明

  • Click Mode:点击画布创建圆形,按住 Shift 可创建不同颜色

Event Log / 事件日志


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

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

其核心思路極為粗暴且有效:

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

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

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

Active Particles:0
FPS:0

6. 守護 FPS 榮耀:如何應對高燒的 CPU?

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

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

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

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

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

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

FPS:0
Frame Time:0ms
Objects:1000

7. 專業名詞總結

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

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