iT邦幫忙

2026 iThome 鐵人賽

DAY 8
0
Modern Web

前端互動視覺技術實作 LAB系列 第 8

Day 8|Canvas 2D(1)畫出第一個圖形

  • 分享至 

  • xImage
  •  

網頁上的動態圖表、2D 遊戲畫面、粒子動畫和簽名板,很多都可以透過 HTML5 的 <canvas> 來實現。這篇文章會從 Canvas 2D 繪圖的基本概念開始,帶你一步步畫出第一個 2D 圖形。

Canvas 是什麼?為甚麼要用?

<canvas> 是 HTML5 新增的一塊繪圖畫布,我們可以用 JS 在上面畫圖形、文字、圖片,甚至做動畫。

你可能會想這些東西用 HTML、CSS 不是也做得到嗎?

這樣說也沒錯啦XD,但差別在於,傳統HTML+CSS 通常是建立一個個 DOM 元素,再去調整它們的樣式和位置;<canvas> 則是一張由像素組成的點陣圖(bitmap),比較像真的拿了一張畫布,直接用 JavaScript 告訴它「在哪個座標、畫什麼東西」。所以當畫面上有很多東西要一直移動、變化或重新繪製時,就很適合使用 Canvas。

建立 Canvas 畫布、取得畫筆與座標系統

我們先在檔案中設定畫布與畫筆,先在 HTML 放入 <canvas>

<canvas id="myCanvas" width="400" height="300"></canvas>

<script setup>
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
</script>

#myCanvas 就是我們的畫布,可以用 widthheight 來設定尺寸;接著用 JavaScript 找到元素,再取得 2D 繪圖環境(context),之後的繪圖指令都會透過 ctx 執行,你可以把ctx想成畫筆。

座標系統的部分,只需要注意,Canvas 的座標原點 (0, 0) 位於左上角,x 軸數值越大會越往右,而 y 軸數值越大則會越往下。這與一般數學座標系統的 y 軸方向相反,我都是直接記得,原點是在左上,這樣就不會搞混了~~

基本繪圖 API

矩形(Rect)

Canvas 有三個常用的矩形方法:

ctx.fillRect(50, 50, 100, 80); // 畫填色矩形
ctx.strokeRect(200, 50, 100, 80); // 畫矩形邊框
ctx.clearRect(0, 0, 50, 50); // 清除一塊區域,恢復透明,橡皮擦的概念

它們的參數都是 (x, y, 寬, 高),其中 xy 是矩形左上角的座標。

路徑(Path)自訂形狀

ctx.beginPath(); // 開始新路徑
ctx.moveTo(50, 200); // 移到起點,不畫線
ctx.lineTo(150, 250); // 從目前位置畫到這個點
ctx.lineTo(100, 300); // 再畫到下一個點
ctx.closePath(); // 連回起點,形成封閉圖形
ctx.stroke(); // 畫出邊框

流程很簡單:beginPath() 開始路徑 → moveTo() 設定起點 → lineTo() 畫線 → closePath() 連回起點 → 最後用 stroke() 畫出邊框。如果想填滿圖形,則可以改用 fill()

每次畫新圖形前,記得呼叫 beginPath()。少了這一步,新路徑可能會接到上一條路徑,出現意料之外的線條。

圓形與弧線

圓形和弧線都使用 arc()

ctx.beginPath();
ctx.arc(200, 150, 50, 0, Math.PI * 2); // 畫完整的圓
ctx.fill();

arc() 的參數是 (x, y, 半徑, 起始角度, 結束角度)xy 是圓心座標,角度使用弧度(radian)。完整一圈是 Math.PI * 2,也就是 360 度。若要畫半圓,可以把角度設為 0Math.PI

顏色與線條樣式

畫圖前可以先像這樣,設定填色、邊框顏色和線條粗細:

ctx.fillStyle = "#ff6b6b"; // 填色
ctx.strokeStyle = "#4834d4"; // 邊框顏色
ctx.lineWidth = 4; // 線條粗細

樣式要在繪圖指令之前設定,才會套用到接下來的圖形。fillStylestrokeStyle 可以使用色碼、rgb()rgba() 或顏色名稱,例如 "tomato"

動手練習實作畫一張圖

把前面的技巧組合起來,我們可以用簡單的幾何圖形,畫一張簡單的圖,這邊我就先示範畫卡比獸(五官簡單又可愛 XD)請看 demo ⇒ Day7 demo
https://ithelp.ithome.com.tw/upload/images/20260827/20183149WiIwI3Sxje.png

function drawSnorlax(step = 4) {
  ctx.clearRect(0, 0, width, height);
  ctx.lineCap = "round";
  ctx.lineJoin = "round";
  ctx.lineWidth = 7;
  ctx.strokeStyle = "#17212b";

  // 畫藍綠色橢圓頭部
  if (step >= 1) {
    ctx.fillStyle = "#365f64";
    ctx.beginPath();
    ctx.ellipse(200, 160, 120, 108, 0, 0, Math.PI * 2);
    ctx.fill();
  }

  // 畫左右三角形耳朵
  if (step >= 2) {
    ctx.fillStyle = "#365f64";
    ctx.beginPath();
    ctx.moveTo(96, 108);
    ctx.lineTo(118, 48);
    ctx.lineTo(154, 76);
    ctx.closePath();
    ctx.fill();

    ctx.beginPath();
    ctx.moveTo(246, 76);
    ctx.lineTo(282, 48);
    ctx.lineTo(304, 108);
    ctx.closePath();
    ctx.fill();
  }

  // 畫奶油色的橢圓臉部
  if (step >= 3) {
    ctx.fillStyle = "#f1dfb6";
    ctx.beginPath();
    ctx.ellipse(200, 178, 100, 78, 0, 0, Math.PI * 2);
    ctx.fill();

    // 畫倒三角形美人尖
    ctx.fillStyle = "#365f64";
    ctx.beginPath();
    ctx.moveTo(140, 94);
    ctx.lineTo(260, 94);
    ctx.lineTo(200, 134);
    ctx.closePath();
    ctx.fill();
  }

  // 畫瞇瞇眼
  if (step >= 4) {
    ctx.beginPath();
    ctx.moveTo(142, 162);
    ctx.lineTo(172, 162);
    ctx.moveTo(228, 162);
    ctx.lineTo(258, 162);
    ctx.stroke();

    // 畫水平線嘴巴
    ctx.beginPath();
    ctx.moveTo(170, 196);
    ctx.lineTo(230, 196);
    ctx.stroke();

    // 畫左右兩顆白色三角形牙齒
    ctx.fillStyle = "#ffffff";
    ctx.lineWidth = 3;
    ctx.beginPath();
    ctx.moveTo(166, 196);
    ctx.lineTo(182, 196);
    ctx.lineTo(174, 186);
    ctx.closePath();
    ctx.fill();
    ctx.stroke();
    ctx.beginPath();
    ctx.moveTo(218, 196);
    ctx.lineTo(234, 196);
    ctx.lineTo(226, 186);
    ctx.closePath();
    ctx.fill();
    ctx.stroke();
    ctx.lineWidth = 7;
  }
}

小結

這篇文章介紹了幾個 Canvas 基礎,包含基本的 context、fillRect()、arc()、fillStyle、strokeStyle 和 lineWidth,也透過基本的圖形畫了一個簡單的小圖,明天我們會將 Canvas 搭配 GSAP 來製作會動的卡比獸^^


上一篇
Day 7|認識瀏覽器的圖形技術
下一篇
Day 9|Canvas 2D(2)結合 GSAP 動畫
系列文
前端互動視覺技術實作 LAB19
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言