🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付

速通Canvas指北🦮——基礎入門篇

引言

本文緣起自筆者開發一個基於 PIXI.js 的線上動畫編輯器時,想系統學習 Canvas 相關知識,卻發現缺少合適的中文入門資料,於是萌生了撰寫這份“速通指北”的想法,歡迎感興趣的朋友訂閱我的 《Canvas 指北》專欄。

第1章:Canvas 簡介

在這一章中,我們將一起探索 Canvas 是什麼,為什麼我們需要它,以及如何在 HTML 中使用 <canvas> 元素。如果你是 Web 圖形開發的新手,不用擔心——我們會從最基礎的概念開始,逐步帶你進入 Canvas 的精彩世界。

1.1 為什麼需要 canvas

在 HTML5 出現之前,網頁上的圖形大多是靜態的——圖片、CSS 繪製的簡單形狀,或者是透過 Flash、SVG 等技術實現的動態效果。這些方法要麼功能有限,要麼需要額外的依賴,難以與現代 Web 應用的動態需求匹配。

Canvas(畫布)是 HTML5 引入的一個革命性特性。它是一個可以透過 JavaScript 腳本在網頁上繪製圖形的“畫板”。與傳統的 DOM 元素不同,Canvas 不保存繪製的圖形物件,主要透過繪圖指令即時渲染,必要時也可進行像素級處理,這使得它非常適合以下場景:

  • 動態數據可視化:比如繪製實時更新的圖表、股票走势图、數據儀表盤。
  • 遊戲開發:Canvas 的高性能使其成為 2D 小遊戲(如貪吃蛇、飛機大戰)的常用渲染方案。
  • 圖像處理:可以直接在瀏覽器中實現濾鏡、顏色調整、圖像合成等功能。
  • 動畫與特效:可以創建粒子系統、炫酷的背景動畫、互動式藝術創作。
  • 繪圖應用:比如線上白板、簽名板、簡單的畫圖工具。

簡單來說,Canvas 讓你能用 JavaScript 在網頁上“畫畫”,而且畫出來的內容是動態的、可互動的,並且性能非常出色。它讓網頁從“展示信息”進化到了“創造視覺體驗”的新階段。

1.2 <canvas> 元素

Canvas 的使用分為兩步:首先在 HTML 中放置一個 <canvas> 元素,然後透過 JavaScript 獲取它的繪圖上下文(context)並進行繪製。

1.2.1 HTML 中的 <canvas>標籤

<canvas> 是一個行內元素(inline),但通常我們把它當作區塊級元素來設定寬高,實踐中常設定 display: block;,可避免與文本基線對齊導致的底部空隙問題。它的基本語法如下:

<canvas id="myCanvas" width="400" height="300">
  您的瀏覽器不支持 Canvas,請升級或更換瀏覽器。
</canvas>
  • id:用於在 JavaScript 中定位該元素。
  • width 和 height:指定畫布的實際像素尺寸(不是 CSS 寬高)。預設值為 300×150 像素。
  • 標籤內的文本:當瀏覽器不支持 Canvas 時,會顯示這段後備內容(fallback content)。

注意:盡量不要用 CSS 的 width 和 height 來修改畫布的像素尺寸。如果透過 CSS 設定寬高,畫布會按原始像素被拉伸,導致圖形模糊。正確的做法是在 <canvas> 標籤中直接指定 width 和 height 屬性,或者透過 JavaScript 動態設定。

如果為了適配高 DPI 屏幕,通常會用 JavaScript 將 canvas 的像素尺寸設為 CSS 尺寸乘以 devicePixelRatio

1.2.2 獲取繪圖上下文

在 JavaScript 中,我們透過 <canvas> 元素的 getContext() 方法獲取繪圖上下文。上下文定義了所有繪圖方法和屬性。目前最常用的是 2D 上下文:

const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

除了 '2d',還有 'webgl'(用於 3D 繪圖)和 'webgl2' 等上下文類型。在本教程中,我們將專注於 2D 上下文。

1.2.3 第一個繪製範例

獲得了 ctx 物件後,我們就可以調用各種繪圖方法了。下面是一個簡單的例子:在畫布上繪製一個紅色的矩形。

<!doctype html>
<html>
  <canvas id="myCanvas" width="200" height="100" style="border: 1px solid black">
  </canvas>
  <script>
    const canvas = document.getElementById('myCanvas');
    const ctx = canvas.getContext('2d');
    ctx.fillStyle = 'red';
    ctx.fillRect(0, 0, 150, 75);
  </script>
</html>

這段程式碼會在畫布的 (0,0) 位置開始,繪製一個寬 150 像素、高 75 像素的紅色矩形:

image

1.3 Canvas 坐標系

Canvas 的坐標系原點 (0,0) 位於畫布左上角,Y 軸正方向是向下的(與常見的數學坐標系 Y 軸向上相反)。也就是說,y 值越大,圖形的位置越靠下。

第2章:線條

本章帶你快速掌握 Canvas 中最基礎的圖形——線條。我們將學習如何繪製直線,以及如何控制線條的粗細、端點樣式、連接樣式和虛線效果。

2.1 繪製線條

繪製線條需要掌握三個核心方法:

  • moveTo(x, y):將畫筆移動到指定坐標(不繪製任何東西)
  • lineTo(x, y):從當前位置繪製一條直線到指定坐標(僅定義路徑,不實際描邊)
  • stroke():對當前定義的路徑進行描邊

此外,還需要透過 strokeStyle 設置線條顏色(預設黑色)。

畫一條從 (50,30) 到 (250,120) 的紅色直線:

<canvas id="lineCanvas" width="300" height="150"></canvas>
<script>
  const canvas = document.getElementById('lineCanvas');
  const ctx = canvas.getContext('2d');

  ctx.strokeStyle = 'red';   // 設置線條顏色
  ctx.moveTo(50, 30);        // 起點 (50,30)
  ctx.lineTo(250, 120);      // 终点 (250,120)
  ctx.stroke();              // 描邊繪製
</script>

image

注意: 以上程式碼未使用 beginPath(),僅適用於簡單場景。如果後續需要繪製多條獨立的線,必須調用 ctx.beginPath() 重置路徑,否則之前繪製的線會被重複描邊。我們將在第4章“路徑”中詳細介紹。

2.2 線條樣式

2.2.1 線寬:lineWidth

設置線條的粗細(像素)。預設值為 1.0。

線寬越大,線條越粗。效果對比:

<canvas id="widthCanvas" width="300" height="120"></canvas>
<script>
  const canvas = document.getElementById('widthCanvas');
  const ctx = canvas.getContext('2d');

  // 線寬 1
  ctx.beginPath();
  ctx.lineWidth = 1;
  ctx.moveTo(20, 20);
  ctx.lineTo(280, 20);
  ctx.stroke();

  // 線寬 5
  ctx.beginPath();
  ctx.lineWidth = 5;
  ctx.moveTo(20, 50);
  ctx.lineTo(280, 50);
  ctx.stroke();

  // 線寬 10
  ctx.beginPath();
  ctx.lineWidth = 10;
  ctx.moveTo(20, 80);
  ctx.lineTo(280, 80);
  ctx.stroke();
</script>

image

2.2.2 端點樣式:lineCap

  • "butt"(預設):平直端點,不超出線段端點
  • "round":圓形端點,半圓直徑等於線寬
  • "square":方形端點,超出線段端點一半線寬

畫三條相同起止點的線段,分別設置不同 lineCap。效果對比:

<canvas id="capCanvas" width="320" height="120"></canvas>
<script>
  const canvas = document.getElementById('capCanvas');
  const ctx = canvas.getContext('2d');
  ctx.lineWidth = 10;
  ctx.strokeStyle = 'blue';

  // butt (預設)
  ctx.beginPath();
  ctx.lineCap = 'butt';
  ctx.moveTo(40, 30);
  ctx.lineTo(140, 30);
  ctx.stroke();

  // round
  ctx.beginPath();
  ctx.lineCap = 'round';
  ctx.moveTo(40, 60);
  ctx.lineTo(140, 60);
  ctx.stroke();

  // square
  ctx.beginPath();
  ctx.lineCap = 'square';
  ctx.moveTo(40, 90);
  ctx.lineTo(140, 90);
  ctx.stroke();
</script>

image

2.2.3 連接樣式:lineJoin

定義兩條線相交處的拐角形狀。可選值:

  • "miter"(預設):尖角(透過 miterLimit 限制尖銳程度)
  • "round":圓角
  • "bevel":平角(切去尖角)

繪製兩條折線,分別應用不同連接樣式。效果對比

<canvas id="joinCanvas" width="320" height="200"></canvas>
<script>
  const canvas = document.getElementById('joinCanvas');
  const ctx = canvas.getContext('2d');
  ctx.lineWidth = 15;

  // miter (預設)
  ctx.beginPath();
  ctx.lineJoin = 'miter';
  ctx.moveTo(30, 30);
  ctx.lineTo(80, 80);
  ctx.lineTo(130, 30);
  ctx.stroke();

  // round
  ctx.beginPath();
  ctx.lineJoin = 'round';
  ctx.moveTo(160, 30);
  ctx.lineTo(210, 80);
  ctx.lineTo(260, 30);
  ctx.stroke();

  // bevel
  ctx.beginPath();
  ctx.lineJoin = 'bevel';
  ctx.moveTo(30, 100);
  ctx.lineTo(80, 150);
  ctx.lineTo(130, 100);
  ctx.stroke();
</script>

image

2.2.4 尖角限制:miterLimit

lineJoin = "miter" 時,該屬性限制尖角的長度。尖角長度是指內角頂點到外角頂點的距離。如果尖角長度超過 miterLimit 與線寬的乘積,則連接樣式會回退為 bevel。預設值為 10.0

保持同一折線,用不同 miterLimit 值觀察尖角變化。效果對比

<canvas id="miterCanvas" width="320" height="200"></canvas>
<script>
  const canvas = document.getElementById('miterCanvas');
  const ctx = canvas.getContext('2d');
  ctx.lineWidth = 10;
  ctx.strokeStyle = 'green';
  ctx.lineJoin = 'miter'; // 必須為 miter 才生效

  // miterLimit = 1(較小,尖角被截斷為平角)
  ctx.beginPath();
  ctx.miterLimit = 1;
  ctx.moveTo(30, 30);
  ctx.lineTo(80, 90);
  ctx.lineTo(130, 30);
  ctx.stroke();

  // miterLimit = 5(中等,仍為尖角)
  ctx.beginPath();
  ctx.miterLimit = 5;
  ctx.moveTo(160, 30);
  ctx.lineTo(210, 90);
  ctx.lineTo(260, 30);
  ctx.stroke();

  // miterLimit = 10(預設,通常保持尖角)
  ctx.beginPath();
  ctx.miterLimit = 10;
  ctx.moveTo(30, 100);
  ctx.lineTo(80, 160);
  ctx.lineTo(130, 100);
  ctx.stroke();
</script>

image

2.2.5 虛線模式:setLineDash()

透過數組指定虛線的線段與間隙長度(像素)。數組元素依次為線段長、間隙長、線段長、間隙長……重複循環,若要恢復實線,可傳入空數組 ctx.setLineDash([])

繪製多條虛線,展示不同數組模式。效果對比

<canvas id="dashCanvas" width="320" height="140"></canvas>
<script>
  const canvas = document.getElementById('dashCanvas');
  const ctx = canvas.getContext('2d');
  ctx.lineWidth = 3;

  // 實線
  ctx.beginPath();
  ctx.setLineDash([]);
  ctx.moveTo(20, 20);
  ctx.lineTo(300, 20);
  ctx.stroke();

  // 虛線 [10, 5] (10畫,5空)
  ctx.beginPath();
  ctx.setLineDash([10, 5]);
  ctx.moveTo(20, 50);
  ctx.lineTo(300, 50);
  ctx.stroke();

  // 點線 [2, 4] (2畫,4空)
  ctx.beginPath();
  ctx.setLineDash([2, 4]);
  ctx.moveTo(20, 80);
  ctx.lineTo(300, 80);
  ctx.stroke();

  // 點劃線 [15, 5, 2, 5] (長畫,短空,點,短空)
  ctx.beginPath();
  ctx.setLineDash([15, 5, 2, 5]);
  ctx.moveTo(20, 110);
  ctx.lineTo(300, 110);
  ctx.stroke();
</script>

image

2.2.6 虛線偏移:lineDashOffset

lineDashOffset 屬性用於設置虛線模式的起始偏移量(像素),會改變虛線圖案在路徑上的起點位置。連續更新該值(遞增或遞減)可以實現“流動虛線”動畫效果。

效果對比:繪製三條相同的虛線,分別設置不同偏移量。

<canvas id="dashOffsetCanvas" width="320" height="120"></canvas>
<script>
  const canvas = document.getElementById('dashOffsetCanvas');
  const ctx = canvas.getContext('2d');
  ctx.lineWidth = 4;
  ctx.strokeStyle = 'purple';
  ctx.setLineDash([10, 5]); // 統一虛線模式

  // 偏移 0 (預設)
  ctx.beginPath();
  ctx.lineDashOffset = 0;
  ctx.moveTo(20, 20);
  ctx.lineTo(300, 20);
  ctx.stroke();

  // 偏移 5
  ctx.beginPath();
  ctx.lineDashOffset = 5;
  ctx.moveTo(20, 50);
  ctx.lineTo(300, 50);
  ctx.stroke();

  // 偏移 -8
  ctx.beginPath();
  ctx.lineDashOffset = -8;
  ctx.moveTo(20, 80);
  ctx.lineTo(300, 80);
  ctx.stroke();
</script>

image

🚀 下篇預告:形狀與路徑篇

在下一篇中,你將學到:

  • 如何繪製矩形、多邊形、圓形、弧線等基本形狀;
  • 路徑的深入用法,包括 beginPathclosePath 的正確姿勢;
  • 填充與描邊的區別,以及 nonzeroevenodd 兩種填充規則的原理與效果;
  • arcarcTo 的詳細對比與適用場景。

掌握了這些,你就能組合路徑創造出複雜圖形,為後續的樣式與動畫打下堅實基礎。


原文出處:https://juejin.cn/post/7612919237413699599


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝26   💬2  
745
🥈
我愛JS
💬5  
17
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付