
前言
(註:以下範例程式碼可直接在 HTML 檔案中執行,每段範例包含一個 <canvas> 畫布元素以及對應的 JavaScript 繪圖代碼。)
繪製矩形
Canvas 和 SVG 不同,只內建支援一種基本圖形:矩形。為了方便繪製矩形,Canvas 2D API 提供了三個方法,分別用於填充矩形、繪製矩形邊框,以及清除矩形區域:
- fillRect(x, y, width, height):繪製一個填滿顏色的矩形。
- strokeRect(x, y, width, height):繪製一個矩形的邊框(只描邊,不填滿)。
- clearRect(x, y, width, height):清除指定矩形區域的內容,將該區域變為透明。
上述方法的參數意義相同:x、y 是矩形左上角的座標位置,width 和 height 是矩形的寬度與高度。Canvas 的坐標原點在畫布的左上角,座標單位為像素。例如,x = 0, y = 0 代表畫布的左上角位置。
讓我們透過範例來看看如何使用這些矩形繪製方法。下方程式碼會在畫布上繪製一個藍色實心矩形、在其中清出一塊透明區域,並於透明區域內繪製一個紅色邊框矩形。
<canvas id="exampleRect" width="300" height="150"></canvas>
<script>
const canvas = document.getElementById("exampleRect");
const ctx = canvas.getContext("2d");
// 設定填充與描邊樣式
ctx.fillStyle = "blue"; // 填充色:藍色
ctx.strokeStyle = "red"; // 邊框色:紅色
// 繪製藍色填滿矩形
ctx.fillRect(20, 20, 100, 100);
// 清除一塊矩形區域(將其變透明)
ctx.clearRect(40, 40, 60, 60);
// 繪製紅色邊框矩形
ctx.strokeRect(50, 50, 40, 40);
</script>
上述程式執行後,我們可以看到一個藍色實心的正方形,其中間被清除了 60x60 像素的區域變為透明,而在該透明區域的內部框出了紅色邊框。需要注意的是,這幾種矩形繪製方法呼叫後會立即在畫布上繪製出結果,不需要再呼叫其他函數來渲染。稍後介紹的路徑繪圖則不同,繪製路徑需要更多步驟才能將圖形渲染出來。
在繪製矩形的同時,我們也可以透過 ctx.fillStyle 和 ctx.strokeStyle 來設定填充和描邊的顏色(預設為黑色),用 ctx.lineWidth 設定描邊線條的粗細等樣式。良好地運用這些屬性可以讓圖形繪製更具彈性和美感。
結束矩形的介紹後,我們將繼續探索如何使用 路徑(Path) 來繪製任意形狀與線條。這使我們不再侷限於矩形,而能畫出各種不規則圖形和曲線。
繪製路徑與直線
在 Canvas 中繪製自訂的圖形需要運用「路徑」(path)的概念。簡單來說,路徑是一系列相連的點(由直線或曲線段連接)所構成的形狀輪廓。我們可以想像拿著畫筆在紙上畫線:先決定起筆的位置,然後依序畫出各段線條或曲線,最後(選擇性地)收筆閉合圖形。在 Canvas 裡,繪製路徑大致包含以下步驟:
- 開始一條新路徑:使用 beginPath() 開始一個新的繪圖路徑。呼叫此方法後,先前的路徑狀態會被重置,我們即將開始繪製新的圖形。
- 繪製路徑:使用各種繪圖命令將筆觸移動到指定座標並畫出線段或曲線,如 moveTo、lineTo、arc、quadraticCurveTo、bezierCurveTo 等等。這些命令會定義路徑的形狀。
- (可選)封閉路徑:使用 closePath() 封閉路徑。如果當前的位置不在起點,closePath() 會自動繪製一直線段連回起始點,形成一個封閉形狀。若路徑已經封閉或僅有一個點,呼叫這個方法沒有效果。
- 渲染圖形:路徑定義完成後,使用 stroke() 勾勒(描邊)或 fill() 填滿 來將路徑繪製到畫布上。stroke() 會繪製路徑的輪廓,而 fill() 會填充路徑圍成的區域。如果使用 fill() 來繪製未封閉的路徑,Canvas 會自動將端點相連以封閉該形狀;但使用 stroke() 則不會自動封閉未閉合路徑,因此若需要封閉可以在繪製前手動呼叫 closePath()。
以上步驟看起來抽象,我們以繪製一個簡單的三角形為例來瞭解。下方範例在 Canvas 上畫出兩個三角形:一個填滿的實心三角形,和一個只有邊框的空心三角形。
<canvas id="examplePath" width="200" height="200"></canvas>
<script>
const canvas = document.getElementById("examplePath");
const ctx = canvas.getContext("2d");
// **繪製填滿的三角形**
ctx.beginPath(); // 開始新路徑
ctx.moveTo(50, 150); // 移動畫筆至起點 (50,150)
ctx.lineTo(150, 150); // 畫線到 (150,150)
ctx.lineTo(100, 50); // 畫線到 (100,50) — 形成三角形的第三個點
ctx.fill(); // 填滿路徑(自動封閉路徑並填充)
// **繪製空心的三角形**
ctx.beginPath(); // 開始新路徑(不與之前的路徑相連)
ctx.moveTo(50, 50); // 起點 (50,50)
ctx.lineTo(150, 50); // 畫線到 (150,50)
ctx.lineTo(150, 150); // 畫線到 (150,150)
ctx.closePath(); // 手動封閉路徑(將最後一點連回起點)
ctx.stroke(); // 描邊繪製三角形邊框
</script>
在上述程式碼中,第一次繪圖我們使用 fill() 來完成三角形,由於填充操作會自動將形狀閉合,無需特別呼叫 closePath() 就能得到完整的實心三角形。而第二次繪圖我們選擇 stroke() 來描邊,此時就必須在繪製結束前呼叫 ctx.closePath(),否則只會出現兩條相連的線段,而非一個封閉的三角形。
可以看到,moveTo(x, y) 在一開始指定了繪圖的起點位置,相當於把筆移到紙上的某點而不畫下任何痕跡;接著的 lineTo(x, y) 則會從前一個位置畫一條直線到新的座標點。利用這兩個基本操作組合,我們可以繪製各種折線和多邊形。若我們希望開始繪製一條新線而不與前一段相連,就需要再次呼叫 moveTo 或重新 beginPath() 來重設起點。
到此為止,我們已經能透過路徑畫出任意的折線圖形了。接下來,我們將介紹如何繪製圓形與弧形這類平滑曲線,以及進一步的貝茲曲線,讓繪圖更加多元。
繪製弧形與圓形
圓形和弧線是圖形繪製中常見的元素,例如鐘錶的錶盤、進度環、餅圖等。在 Canvas 中,可以使用 arc(x, y, radius, startAngle, endAngle, anticlockwise) 方法在當前路徑中新增一段弧形(arc)。它所繪製的可以是一整圈的圓,也可以是圓的一部分弧段。各個參數的意義如下:
- x, y:圓心的座標位置。
- radius:圓的半徑大小。
- startAngle:弧形開始的位置角度(以弧度表示)。
- endAngle:弧形結束的位置角度(同樣以弧度表示)。
- anticlockwise:布林值,表示繪製方向。false 表示按順時針方向繪製弧(預設值),true 則表示按逆時針方向繪製。
需要特別注意,startAngle 和 endAngle 是用 弧度 (radians) 來表示角度而非一般的度數。例如,要畫出 90 度的四分之一圓弧,我們應使用 Math.PI/2 表示 90 度(因為 Math.PI 弧度等於 180 度)。如果需要在度數和弧度之間轉換,可以使用公式:radians = (Math.PI/180) * degrees。另外,起始角度是從圓的正右側方向(沿著 x 軸正方向)開始計算。例如,若 startAngle = 0,代表從圓心正右方開始畫弧。
下面透過範例來展示如何繪製圓形和弧形。我們將畫出一個圓形的輪廓以及一段彩色的圓弧。
<canvas id="exampleArc" width="250" height="150"></canvas>
<script>
const canvas = document.getElementById("exampleArc");
const ctx = canvas.getContext("2d");
// 繪製一個圓形的邊框
ctx.beginPath();
ctx.arc(75, 75, 50, 0, Math.PI * 2, false); // 從0到2π,即一整圈
ctx.strokeStyle = "green";
ctx.lineWidth = 5;
ctx.stroke(); // 描邊出綠色圓形
// 繪製一段圓弧(例如四分之一圓)
ctx.beginPath();
ctx.arc(175, 75, 50, 0, Math.PI / 2, false); // 從0到 π/2,四分之一圓
ctx.strokeStyle = "orange";
ctx.lineWidth = 5;
ctx.stroke(); // 描邊出橘色弧線
</script>
第一部分程式碼使用 arc(75, 75, 50, 0, 2 * Math.PI, false) 畫出一整圈的圓並用 stroke() 描邊成綠色,形成一個直徑 100 像素的空心圓形。第二部分程式則畫出一段從 0 弧度到 π/2 弧度的橘色弧線(也就是從正右方到正下方的四分之一圓弧)。在這兩個例子中,我們皆先呼叫 beginPath() 開始一條新路徑,確保每次繪製的形狀不會彼此連接影響。當需要畫一個完整的圓時,startAngle 可以是 0,endAngle 設為 2π(約等於 6.283),而弧線方向順時針或逆時針對完整圓形沒有影響(對於非完整圓則影響繪製的方向和形狀位置)。
透過 arc() 函數,我們可以輕鬆繪製圓形或圓弧等曲線圖形。而在某些情況下,我們需要繪製的不僅是單純的圓,而是更靈活的貝茲曲線,例如繪製不規則的曲線邊緣或平滑的波浪形狀。下一節將介紹 Canvas 中的二次與三次貝茲曲線繪製。
二次貝茲曲線
二次貝茲曲線(Quadratic Bézier curve)是一種類型的平滑曲線,由一個 控制點 和一個終點來定義曲線的形狀。Canvas 提供了 quadraticCurveTo(cp1x, cp1y, x, y) 方法來繪製二次貝茲曲線。其中參數 (cp1x, cp1y) 是控制點座標,決定曲線的彎曲方向和強度,而 (x, y) 是曲線的終點座標;曲線的起點則是當前路徑下上一個繪畫點(或使用 moveTo 設定的點)。
可以把二次貝茲曲線想像成從起點出發,朝向控制點的方向彎曲,最後在終點處結束。控制點有點像一顆"磁石",吸引著曲線朝它靠近,因此曲線的形狀會向控制點凸出。
我們來看一個範例,繪製一條簡單的二次貝茲曲線。此範例中,我們會標示出起點、終點和控制點的位置,以方便觀察曲線如何被控制點拉出彎曲。
<canvas id="exampleQuadCurve" width="300" height="200"></canvas>
<script>
const canvas = document.getElementById("exampleQuadCurve");
const ctx = canvas.getContext("2d");
// 定義二次貝茲曲線的三個關鍵點
const startX = 50, startY = 150; // 起點
const cp1X = 150, cp1Y = 50; // 控制點
const endX = 250, endY = 150; // 終點
// 繪製二次貝茲曲線
ctx.beginPath();
ctx.moveTo(startX, startY); // 移動到起點
ctx.quadraticCurveTo(cp1X, cp1Y, endX, endY); // 繪製二次貝茲曲線到終點
ctx.strokeStyle = "blue";
ctx.lineWidth = 3;
ctx.stroke(); // 描邊繪製曲線
// 繪製控制點和輔助線(幫助理解曲線形狀)
ctx.fillStyle = "red";
ctx.beginPath(); // 繪製控制點(紅色圓點)
ctx.arc(cp1X, cp1Y, 5, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = "black";
ctx.beginPath(); // 繪製起點和終點(黑色圓點)
ctx.arc(startX, startY, 5, 0, Math.PI * 2);
ctx.arc(endX, endY, 5, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath(); // 畫出虛線輔助線連接控制點與兩端
ctx.setLineDash([5, 5]);
ctx.moveTo(startX, startY);
ctx.lineTo(cp1X, cp1Y);
ctx.lineTo(endX, endY);
ctx.strokeStyle = "gray";
ctx.stroke();
</script>
以上程式會畫出一條由左下方 (50,150) 出發,彎向上方 (150,50) 控制點,最後抵達右下方 (250,150) 的藍色平滑曲線。我們特地在圖中以紅點標示控制點位置,以黑點標示起點和終點,並用灰色虛線連接起點、控制點和終點,形成一種「三角形」的形狀輔助視覺化曲線參考。透過這些輔助標示,我們可以清楚看到藍色的貝茲曲線是如何朝紅色控制點凸出,然後落向終點的。
每次呼叫 quadraticCurveTo 時,都是從當前路徑位置繪製至指定的終點。我們也可以連續呼叫多次 quadraticCurveTo 來畫出由多段二次曲線組成的複雜圖形。例如,可以用多段曲線繪製出對話框的圓角邊框、雲朵般的不規則圖案等等。二次貝茲曲線因只有一個控制點,形狀相對容易預測,但在更複雜的情況下,一個控制點可能不足以精細地控制曲線形態,這時就需要用到有兩個控制點的 三次貝茲曲線。
三次貝茲曲線
三次貝茲曲線(Cubic Bézier curve)提供了兩個控制點,因而能繪製出更加靈活多變的曲線形狀。Canvas 使用 bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) 來繪製三次貝茲曲線。相比二次曲線,函式多了第二組控制點參數 (cp2x, cp2y),其 (x, y) 終點參數的意義則相同。曲線的起點仍然是目前路徑的最後一個座標點。
兩個控制點決定了曲線在起點和終點處的方向和離散程度:曲線從起點出發時朝向第一個控制點靠近,並在接近終點時受到第二個控制點的拉引。因此,三次貝茲曲線通常能達到比二次曲線更複雜的彎折效果。我們可以將其想像成"從起點朝控制點1方向出發,途中轉向控制點2,最後抵達終點"。
我們使用一個範例來直觀感受三次貝茲曲線的繪製,並對比二次與三次曲線控制點的差異。下方程式將繪製一條三次貝茲曲線,並同樣標示出起點、終點和兩個控制點的位置。
<canvas id="exampleCubicCurve" width="300" height="250"></canvas>
<script>
const canvas = document.getElementById("exampleCubicCurve");
const ctx = canvas.getContext("2d");
// 定義三次貝茲曲線的關鍵點
const startX = 50, startY = 200; // 起點
const cp1X = 50, cp1Y = 50; // 控制點1
const cp2X = 250, cp2Y = 50; // 控制點2
const endX = 250, endY = 200; // 終點
// 繪製三次貝茲曲線
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.bezierCurveTo(cp1X, cp1Y, cp2X, cp2Y, endX, endY);
ctx.strokeStyle = "purple";
ctx.lineWidth = 3;
ctx.stroke();
// 繪製控制點和輔助線以輔助觀察
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(cp1X, cp1Y, 5, 0, Math.PI * 2); // 控制點1(紅色)
ctx.arc(cp2X, cp2Y, 5, 0, Math.PI * 2); // 控制點2(紅色)
ctx.fill();
ctx.fillStyle = "black";
ctx.beginPath();
ctx.arc(startX, startY, 5, 0, Math.PI * 2); // 起點(黑色)
ctx.arc(endX, endY, 5, 0, Math.PI * 2); // 終點(黑色)
ctx.fill();
ctx.beginPath();
ctx.setLineDash([5, 5]); // 虛線連線控制點和端點
ctx.moveTo(startX, startY);
ctx.lineTo(cp1X, cp1Y);
ctx.lineTo(cp2X, cp2Y);
ctx.lineTo(endX, endY);
ctx.strokeStyle = "gray";
ctx.stroke();
</script>
這段程式碼在畫布上繪出一條紫色的三次貝茲曲線。可以觀察到,曲線從起點 (50,200) 出發時朝向第一個控制點 (50,50) 上方移動,接著在途中拐彎朝向第二個控制點 (250,50),最後緩緩落向終點 (250,200)。我們以兩個紅點標示了控制點的位置,以黑點標示起點和終點,同時用灰色虛線將四個關鍵點依序相連,以示意整條曲線的幾何結構。通過這些輔助標記可以發現,二次曲線只有一個控制點,曲線形狀呈現單一的弓形;而三次曲線有兩個控制點,曲線可以在中途產生變向,更加靈活多變。
如果您嘗試改變控制點或終點的位置,將會發現曲線形狀會跟著明顯變化。熟練掌握貝茲曲線的控制對於繪製複雜的有機圖形非常關鍵,例如想畫出心形、雲朵、橢圓等,常需要精心調整多段貝茲曲線的控制點來完成。此外,由於在 Canvas 中無法直接"看到"尚未繪製完成的曲線(不像圖形軟體能即時拖曳點線調整),在編寫代碼時需要一些耐心和試驗來選擇控制點座標,以得到理想的曲線形狀。
至此,我們已經介紹了Canvas路徑繪圖中常用的各種曲線繪製方法——從直線、圓弧到二次和三次貝茲曲線。接下來,我們將探討如何讓使用者以更直覺的方式,在 Canvas 上自由繪製曲線。
自由繪製曲線
有時我們希望允許使用者透過滑鼠或觸控,直接在 Canvas 上繪圖,好比一支畫筆讓人自由揮灑。要實現這種自由繪製曲線的效果,關鍵思路是在使用者拖曳滑鼠時,不斷取得滑鼠在畫布上的座標,並繪製連接上一點和當前點的線段,從而形成平滑的手繪軌跡。
在 Canvas 上監聽使用者的滑鼠事件可以做到這點。典型的作法是:
- 監聽 mousedown 事件,當滑鼠按下時,使用 beginPath() 開始一條新路徑,並記錄起始座標(使用 moveTo 移動畫筆到起點)。
- 監聽 mousemove 事件,如果滑鼠處於按下狀態,則持續呼叫 lineTo() 將路徑連接到滑鼠的新座標,並即時使用 stroke() 繪製出線段。
- 監聽 mouseup 或 mouseleave 事件,在滑鼠放開或移出畫布時結束繪圖(可視需要呼叫 closePath(),通常手繪不閉合也沒關係),將狀態重置以準備下一次繪圖。
下面的範例實作了一個簡易的畫筆功能:按下滑鼠在畫布上拖曳時會畫出連續的線條,鬆開滑鼠後停止繪圖。為了簡化實現,我們使用 offsetX, offsetY 直接取得滑鼠相對於 Canvas 元素的座標。
<canvas id="exampleFreeDraw" width="400" height="200" style="border:1px solid #ccc"></canvas>
<script>
const canvas = document.getElementById("exampleFreeDraw");
const ctx = canvas.getContext("2d");
let drawing = false; // 用於記錄目前是否正在繪圖狀態
// 畫筆樣式設定
ctx.strokeStyle = "darkred";
ctx.lineWidth = 4;
ctx.lineCap = "round"; // 線條端點圓潤,讓書寫筆跡更自然
// 滑鼠按下:開始新的繪圖路徑
canvas.addEventListener("mousedown", (e) => {
drawing = true;
ctx.beginPath();
ctx.moveTo(e.offsetX, e.offsetY); // 將畫筆移動到起始點
});
// 滑鼠移動:若處於繪圖狀態,則繪製連續的曲線
canvas.addEventListener("mousemove", (e) => {
if (!drawing) return;
ctx.lineTo(e.offsetX, e.offsetY); // 畫線到滑鼠所在位置
ctx.stroke(); // 繪製當前線段
});
// 滑鼠放開或移出畫布:結束繪圖路徑
canvas.addEventListener("mouseup", () => {
drawing = false;
});
canvas.addEventListener("mouseleave", () => {
drawing = false;
});
</script>
在這段程式中,我們首先將 ctx.lineCap 設為 "round" 以使繪製的線段末端呈圓滑狀,模擬更真實的筆跡效果。當使用者按下滑鼠時,我們透過 beginPath() 開始新路徑並用 moveTo 將畫筆定位到滑鼠點擊的位置;隨後每當滑鼠移動時,我們檢查 drawing 狀態是否為真,如果是則不斷使用 lineTo 畫線到滑鼠的新位置並即時 stroke() 繪製,於是滑鼠移動軌跡就被繪製成線條;最後在 mouseup 或 mouseleave 時將 drawing 設為 false,結束這一次的繪圖。如此在使用者每次按下—拖曳—放開的動作循環中,都會對應繪製出一條獨立的筆畫。
值得注意的是,此實作並未對繪製進行平滑算法或壓力感應處理,但已足夠作為基本的手繪演示。您可以試著改變 strokeStyle 或 lineWidth 來得到不同顏色和粗細的線條效果。透過這樣的機制,可以發展出簡易畫圖板、簽名板或註解工具等功能,增強Canvas應用的互動性。
繪製文字與文字描邊
除了各種圖形與路徑,Canvas 也能夠繪製文字。無論是製作圖表標籤、在圖片上添加說明文字,或是創造像海報一樣的圖文並茂效果,Canvas 的文字 API 都相當實用。Canvas 繪製文字主要用到兩個方法:
- fillText(text, x, y):在指定座標繪製填滿的文字(實心字)。
- strokeText(text, x, y):在指定座標繪製描邊的文字(字體外框)。
兩者差別在於呈現效果不同:fillText 繪出的文字以 fillStyle 作為填充顏色,文字本體實心;而 strokeText 則使用 strokeStyle 來繪製字的輪廓,產生空心的文字描邊效果。繪製文字前,通常我們會先設定字型和大小,使用 ctx.font 屬性,例如 ctx.font = "20px Arial"。若未設定字型,Canvas 會使用預設字型(一般是 10px sans-serif)。
以下範例展示如何在 Canvas 上繪製文字,以及填充和描邊的效果差異:
<canvas id="exampleText" width="400" height="100"></canvas>
<script>
const canvas = document.getElementById("exampleText");
const ctx = canvas.getContext("2d");
ctx.font = "24px Microsoft JhengHei"; // 設定字型和大小
ctx.fillStyle = "black";
ctx.strokeStyle = "red";
ctx.lineWidth = 2;
ctx.fillText("Hello Canvas!你好 Canvas!", 20, 40); // 實心文字
ctx.strokeText("Hello Canvas!你好 Canvas!", 20, 80); // 空心描邊文字
</script>
程式執行後,我們可以在畫布上看到上方繪製了一行黑色實心文字"Hello Canvas!你好 Canvas!",下方繪製了一行紅色空心文字,文字的字型被設定為微軟正黑體(Microsoft JhengHei)24px大小。由此可以觀察到 fillText 與 strokeText 的效果差別:前者字體本身填滿顏色,後者只有字的外輪廓線條。您可以調整 strokeStyle 來改變文字輪廓顏色,或調整 lineWidth 改變描邊粗細,以獲得不同風格的文字效果。如果需要文字排列換行或更複雜的文字版面,Canvas 需要開發者自行計算座標或切割字串(因 Canvas 文字繪製不會自動換行)。
文字API使我們能將說明、標籤直接繪製在Canvas圖形上,搭配前面介紹的圖形繪製方法,可以創作出內容豐富的教學示意圖或資料視覺化圖表。例如,在遊戲中顯示分數、在圖表上標出數值、在圖像上添加水印等等,都可以使用 Canvas 的文字繪製來達成。
常見問題 (FAQ)
本單元匯總了幾個在使用 Canvas 進行基礎繪圖時,初學者經常遇到的問題與解答,希望能幫助您順利排解疑惑:
Q1:為什麼我執行繪圖程式後,Canvas 上什麼都沒顯示出來?
A1: 可能原因有幾種:
- 沒有取得 2D 繪圖上下文(context):在繪圖前一定要透過 canvas.getContext("2d") 取得繪圖用的 context 物件,才能呼叫各種繪圖方法。如果這步驟遺漏,ctx 會是 null,繪圖命令自然不會有效果。
- 忘記調用渲染方法:如果使用了路徑繪圖,如 moveTo、lineTo 等,結束後需要呼叫 ctx.fill() 或 ctx.stroke() 才會真正把圖形畫出來。如果只定義了路徑但沒有填充或描邊,Canvas 不會呈現任何圖形。
- 繪圖顏色與背景相同:有時圖形其實畫出來了,但可能因為 fillStyle 或 strokeStyle 的顏色剛好和背景相近(甚至透明)而導致肉眼看不見。請確保設定了一個可辨識的繪圖顏色。
Q2:繪製直線/曲線時,為何起點沒有出現在畫布上?
A2: 請檢查是否在繪圖前正確地使用了 moveTo(x, y) 來設定路徑的起點。如果省略 moveTo 就直接 lineTo(x, y),那麼起點會被默認為先前路徑最後繪製的位置,或者 (0,0)(若沒有先前路徑)。這常常導致線條從畫布角落意外地連過來。養成每次繪製新圖形前使用 beginPath() 並搭配 moveTo 設定起始點的習慣,可以避免不必要的連線。
Q3:使用 arc() 繪製弧形時角度總是算不準,如何正確設定角度?
A3: 請確認您使用的是弧度而非度數。Canvas 的 arc 方法所有角度參數都需以 Radians (弧度) 表示。例如,要表達 45 度,應使用約 0.785 (即 π/4) 的弧度值。如果您比較習慣以度數思考,可以在程式中先將度數轉換,如使用 (Math.PI/180) * 度數 的公式做換算。另外,請留意 startAngle 和 endAngle 是相對於圓心右側水平軸的角度,而 anticlockwise 參數會影響弧形繪製的方向,這些都會影響弧線出現的位置和形狀。建議多實驗小角度與大角度的搭配,並善用顏色或輔助圖形來確認弧線範圍是否如預期。
Q4:為什麼呼叫了 fillText/strokeText 卻看不到文字?
A4: 可能原因如下:
- 沒有設定字型或字體太小:Canvas 預設字型大小僅為 10px,可能較小且不顯眼。您可以透過 ctx.font 設定較大的字型大小以及字體,比如 "16px sans-serif" 或 "20px 微軟正黑體" 等,以確保文字清晰可見。
- 文字座標在畫布範圍外:請確認傳入 fillText/strokeText 的座標在 Canvas 寬高範圍內。如果 x、y 超出了畫布邊界,文字將無法顯示在畫布上。
- 填充或描邊顏色問題:若您使用 strokeText 而 ctx.strokeStyle 預設為白色或透明,可能導致文字描邊不可見。請將描邊顏色設為對比背景的顏色。
Q5:Canvas 繪圖看起來失真/模糊,如何改善畫質?
A5: 這通常發生在高 DPI 顯示器或拉伸 Canvas 元素時。幾種改善方法:
- 使用更高解析度:將 Canvas 的實際畫素尺寸設大一些(透過 HTML 屬性設定 width、height),再用 CSS 縮放顯示,可以增加細節。如 Retina 螢幕下可將 Canvas 實際大小設定為顯示大小的兩倍,繪圖時等比縮放座標,達到高清效果。
- 避免非整數座標:如果繪製線條時座標不是整數,單像素線可能會因為位於像素格的邊緣而呈現模糊。可以嘗試將座標或線寬調整,使線條對齊像素網格(例如將 0.5 的偏移加入座標,使線條落在整像素上)。
- 適當使用抗鋸齒:Canvas 瀏覽器預設會抗鋸齒,但在某些情況下可能導致模糊。如果追求像素級銳利,可考慮關閉陰影或使用位圖字型等方式,不過大多數情況下透過提高解析度即可解決問題。
結論
透過本篇教學的逐步講解,我們從 Canvas 基礎繪圖的起點——矩形開始,一路學習了如何使用 JavaScript 操控 Canvas 來繪製各種基本圖形和路徑。我們探討了路徑的繪製原理,學會使用直線段與弧線段來構成圖形,進而深入了解了二次與三次貝茲曲線的強大之處,能夠創造出平滑而多樣的曲線造型。我們也動手實作了自由繪製曲線的互動功能,讓 Canvas 不僅能畫出預定的圖形,還能即時捕捉使用者的筆跡。同時,我們沒有忽略文字繪製,在 Canvas 上輸出填充或描邊文字為圖形增添說明。整體而言,Canvas 2D API 提供了豐富的繪圖工具,只要掌握了這些基礎,本真性地運用創意,就能在瀏覽器中畫出精彩的影像與圖案。
在實際應用中,您可以將這些技術組合起來:例如,使用路徑繪製各種圖形後,再用文字標註說明,或者讓使用者透過自由繪製在既有圖像上做重點標記。隨著熟練度提高,還可以探索 Canvas 更進階的功能,如影像繪製、圖形變形 (transform)、合成效果 (compositing) 等等。不論您是想製作網頁遊戲、資料視覺化、繪圖應用或是簡單的動畫展示,Canvas 都是值得深入研究的強大工具。希望本教程內容能為您的 Canvas 繪圖之旅奠定堅實的基礎,讓您在未來的創作中揮灑自如,畫出屬於自己的精彩作品!