=========================

引言

--

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

第7章:曲線

在進入具體的曲線繪製方法之前,我們先釐清一個關鍵概念:弧線(Arc)曲線(Curve) 在 Canvas 中並不是同一回事。

  • 弧線(上一章的主角)是圓的一部分。它由圓心、半徑、起始角和結束角定義,具有固定的曲率(半徑恆定)。你可以把它想像成用圓規畫出來的一段圓弧──它的彎曲程度是均勻的。
  • 曲線(本章的主角,即貝塞爾曲線)則不侷限於圓。它透過起點、終點和一個或多個控制點來「拉伸」形狀,可以產生從拋物線到 S 形、波浪形等千變萬化的路徑。它的曲率是連續變化的,沒有固定的半徑

在 Canvas 中,弧線使用 arc 和 arcTo 繪製,而曲線則使用 quadraticCurveTo(二次貝塞爾)和 bezierCurveTo(三次貝塞爾)。理解了這一點,我們就能更準確地選擇工具來繪製想要的圖形。

7.1 二次貝塞爾曲線:quadraticCurveTo

二次貝塞爾曲線由一個控制點定義,其數學本質是一段拋物線弧。雖然拋物線通常有對稱軸,但在畫布上,隨著控制點位置的變化,拋物線弧可以朝向任意方向,看起來可能並不對稱。

<div><div><div></div><span>js</span></div><div><div> <span>体验AI代码助手</span></div><div> <span>代码解读</span></div><div>复制代码</div></div></div>```
<span><span>quadraticCurveTo</span>(cpx, cpy, x, y)</span>

- `(cpx, cpy)`:控制點座標
- `(x, y)`:終點座標
- 起點需先透過 `moveTo` 指定。

下面的程式碼在同一個畫布上繪製三條二次貝塞爾曲線,起點均為 `(30,100)`,終點均為 `(270,100)`,但控制點的高度不同。你可以清楚地看到控制點越靠上,曲線被「拉」得越高。

<div><div><div></div><span>html</span></div><div><div> <span>体验AI代码助手</span></div><div> <span>代码解读</span></div><div>复制代码</div></div></div>```
<span><span>canvas</span> <span>id</span>=<span>"quadraticDemo"</span> <span>width</span>=<span>"320"</span> <span>height</span>=<span>"200"</span> <span>style</span>=<span>"border:1px solid #ccc"</span>></span><span><span>canvas</span>></span>
<span><span>script</span>></span><span></span>
<span> <span>const</span> canvas = <span>document</span>.<span>getElementById</span>(<span>'quadraticDemo'</span>);</span>
<span> <span>const</span> ctx = canvas.<span>getContext</span>(<span>'2d'</span>);</span>
<span></span>
<span> <span>// 第一條:控制點在 (150, 30) —— 向上拉</span></span>
<span> ctx.<span>beginPath</span>();</span>
<span> ctx.<span>moveTo</span>(<span>30</span>, <span>100</span>);</span>
<span> ctx.<span>quadraticCurveTo</span>(<span>150</span>, <span>30</span>, <span>270</span>, <span>100</span>);</span>
<span> ctx.<span>strokeStyle</span> = <span>'red'</span>;</span>
<span> ctx.<span>lineWidth</span> = <span>2</span>;</span>
<span> ctx.<span>stroke</span>();</span>
<span></span>
<span> <span>// 第二條:控制點在 (150, 100) —— 與起點終點同高,變成直線</span></span>
<span> ctx.<span>beginPath</span>();</span>
<span> ctx.<span>moveTo</span>(<span>30</span>, <span>100</span>);</span>
<span> ctx.<span>quadraticCurveTo</span>(<span>150</span>, <span>100</span>, <span>270</span>, <span>100</span>);</span>
<span> ctx.<span>strokeStyle</span> = <span>'green'</span>;</span>
<span> ctx.<span>stroke</span>();</span>
<span></span>
<span> <span>// 第三條:控制點在 (150, 170) —— 向下拉</span></span>
<span> ctx.<span>beginPath</span>();</span>
<span> ctx.<span>moveTo</span>(<span>30</span>, <span>100</span>);</span>
<span> ctx.<span>quadraticCurveTo</span>(<span>150</span>, <span>170</span>, <span>270</span>, <span>100</span>);</span>
<span> ctx.<span>strokeStyle</span> = <span>'blue'</span>;</span>
<span> ctx.<span>stroke</span>();</span>
<span><span><span>script</span>></span></span>

image.png

效果:紅線上拱,綠線平直,藍線下凹。控制點的 y 座標直接決定了曲線的彎曲方向。

7.2 三次貝塞爾曲線:bezierCurveTo

三次貝塞爾曲線有兩個控制點,能塑造出 S 形、波浪等更複雜的形狀。

<div><div><div></div><span>javascript</span></div><div><div> <span>体验AI代码助手</span></div><div> <span>代码解读</span></div><div>复制代码</div></div></div>```
<span><span>bezierCurveTo</span>(cp1x, cp1y, cp2x, cp2y, x, y)</span>

- `(cp1x, cp1y)`:第一個控制點(影響曲線起始段的走向)
- `(cp2x, cp2y)`:第二個控制點(影響曲線結束段的走向)
- `(x, y)`:終點座標
- **起點** 同樣由 `moveTo` 指定。

以下程式碼從 `(50,100)` 到 `(250,100)` 繪製一條三次貝塞爾曲線。第一個控制點 `(100,20)` 將曲線拉向上方,第二個控制點 `(200,180)` 將曲線拉向下方,形成優美的 S 形。

<div><div><div></div><span>html</span></div><div><div> <span>体验AI代码助手</span></div><div> <span>代码解读</span></div><div>复制代码</div></div></div>```
<span><span>canvas</span> <span>id</span>=<span>"bezierDemo"</span> <span>width</span>=<span>"320"</span> <span>height</span>=<span>"200"</span> <span>style</span>=<span>"border:1px solid #ccc"</span>></span><span><span>canvas</span>></span>
<span><span>script</span>></span><span></span>
<span> <span>const</span> canvas = <span>document</span>.<span>getElementById</span>(<span>'bezierDemo'</span>);</span>
<span> <span>const</span> ctx = canvas.<span>getContext</span>(<span>'2d'</span>);</span>
<span></span>
<span> ctx.<span>beginPath</span>();</span>
<span> ctx.<span>moveTo</span>(<span>50</span>, <span>100</span>);</span>
<span> ctx.<span>bezierCurveTo</span>(<span>100</span>, <span>20</span>, <span>200</span>, <span>180</span>, <span>250</span>, <span>100</span>);</span>
<span> ctx.<span>strokeStyle</span> = <span>'purple'</span>;</span>
<span> ctx.<span>lineWidth</span> = <span>3</span>;</span>
<span> ctx.<span>stroke</span>();</span>
<span><span><span>script</span>></span></span>

image.png

理論上透過貝塞爾曲線,你可以繪製出任何圖形。如果你對控制點如何影響曲線仍感到抽象,強烈推薦 cubic-bezier.com/ 這個網站。

第8章:文字

Canvas 不僅可以繪製圖形,還能直接渲染文字。本章將介紹如何控制文字的字型、位置、對齊方式,以及如何測量文字尺寸,為你在畫布上添加文字標籤、設計動態文字效果打下基礎。

8.1 文字繪製基礎

Canvas 提供了兩種繪製文字的方法:

  • fillText(text, x, y, maxWidth):在指定位置繪製實心字元。
  • strokeText(text, x, y, maxWidth):在指定位置繪製空心(僅描邊)字元。

xy 是文字繪製的起始座標(具體位置受 textAligntextBaseline 影響,後文會講)。可選的 maxWidth 參數用於限制文字的最大寬度。

<div><div><div></div><span>html</span></div><div><div> <span>体验AI代码助手</span></div><div> <span>代码解读</span></div><div>复制代码</div></div></div>```
<span><span>canvas</span> <span>id</span>=<span>"textBasic"</span> <span>width</span>=<span>"400"</span> <span>height</span>=<span>"150"</span> <span>style</span>=<span>"border:1px solid #ccc"</span>></span><span><span>canvas</span>></span>
<span><span>script</span>></span><span></span>
<span>  <span>const</span> canvas = <span>document</span>.<span>getElementById</span>(<span>'textBasic'</span>);</span>
<span>  <span>const</span> ctx = canvas.<span>getContext</span>(<span>'2d'</span>);</span>
<span></span>
<span>  <span>// 填充文字</span></span>
<span>  ctx.<span>font</span> = <span>'20px Arial'</span>;</span>
<span>  ctx.<span>fillStyle</span> = <span>'blue'</span>;</span>
<span>  ctx.<span>fillText</span>(<span>'实心文本'</span>, <span>50</span>, <span>50</span>);</span>
<span></span>
<span>  <span>// 描邊文字</span></span>
<span>  ctx.<span>font</span> = <span>'20px Arial'</span>;</span>
<span>  ctx.<span>strokeStyle</span> = <span>'red'</span>;</span>
<span>  ctx.<span>lineWidth</span> = <span>1</span>;</span>
<span>  ctx.<span>strokeText</span>(<span>'空心文本'</span>, <span>50</span>, <span>100</span>);</span>
<span><span><span>script</span>></span></span>

![image.png](https://i.imgur.com/YuPgBvD.jpeg)

這裡 `font` 屬性與 CSS 的 `font` 簡寫語法一致,用於設定文字的字型大小、字體系列等。預設值為 `"10px sans-serif"`。

### 8.2 文字佈局

#### 8.2.1 水平對齊:`textAlign`

`textAlign` 屬性決定文字在水平方向上相對於繪圖點的對齊方式。可選值:

- `start`(預設):在從左到右的語言中左對齊,從右到左的語言中右對齊。
- `end`:與 `start` 相反。
- `left`:總是左對齊。
- `right`:總是右對齊。
- `center`:文字中心與繪圖點對齊。

為了直觀理解,我們可以繪製一個參考點,然後分別用不同對齊方式繪製文字,觀察它們相對於該點的位置。

<div><div><div></div><span>html</span></div><div><div> <span>体验AI代码助手</span></div><div> <span>代码解读</span></div><div>复制代码</div></div></div>```
<span><span>canvas</span> <span>id</span>=<span>"textAlignDemo"</span> <span>width</span>=<span>"400"</span> <span>height</span>=<span>"200"</span> <span>style</span>=<span>"border:1px solid #ccc"</span>></span><span><span>canvas</span>></span>
<span><span>script</span>></span><span></span>
<span> <span>const</span> canvas = <span>document</span>.<span>getElementById</span>(<span>'textAlignDemo'</span>);</span>
<span> <span>const</span> ctx = canvas.<span>getContext</span>(<span>'2d'</span>);</span>
<span></span>
<span> <span>// 繪製參考豎線(x = 200)</span></span>
<span> ctx.<span>beginPath</span>();</span>
<span> ctx.<span>strokeStyle</span> = <span>'gray'</span>;</span>
<span> ctx.<span>setLineDash</span>([<span>5</span>, <span>5</span>]);</span>
<span> ctx.<span>moveTo</span>(<span>200</span>, <span>0</span>);</span>
<span> ctx.<span>lineTo</span>(<span>200</span>, <span>200</span>);</span>
<span> ctx.<span>stroke</span>();</span>
<span> ctx.<span>setLineDash</span>([]); <span>// 恢復實線</span></span>
<span></span>
<span> ctx.<span>font</span> = <span>'16px Arial'</span>;</span>
<span> ctx.<span>fillStyle</span> = <span>'black'</span>;</span>
<span> ctx.<span>textBaseline</span> = <span>'middle'</span>; <span>// 讓文字垂直置中,便於觀察水平對齊</span></span>
<span></span>
<span> <span>// 在 x=200 處用不同 textAlign 繪製文字</span></span>
<span> ctx.<span>textAlign</span> = <span>'start'</span>;</span>
<span> ctx.<span>fillText</span>(<span>'start'</span>, <span>200</span>, <span>30</span>);</span>
<span></span>
<span> ctx.<span>textAlign</span> = <span>'center'</span>;</span>
<span> ctx.<span>fillText</span>(<span>'center'</span>, <span>200</span>, <span>70</span>);</span>
<span></span>
<span> ctx.<span>textAlign</span> = <span>'end'</span>;</span>
<span> ctx.<span>fillText</span>(<span>'end'</span>, <span>200</span>, <span>110</span>);</span>
<span></span>
<span> ctx.<span>textAlign</span> = <span>'left'</span>;</span>
<span> ctx.<span>fillText</span>(<span>'left'</span>, <span>200</span>, <span>150</span>);</span>
<span></span>
<span> ctx.<span>textAlign</span> = <span>'right'</span>;</span>
<span> ctx.<span>fillText</span>(<span>'right'</span>, <span>200</span>, <span>190</span>);</span>
<span><span><span>script</span>></span></span>

1.webp

8.2.2 垂直對齊:textBaseline

textBaseline 屬性控制文字在垂直方向上相對於繪圖點的對齊方式。可選值:

  • top:文字的頂部(em 方格的上沿)對齊到 y 座標。
  • hanging:懸掛基線(某些印度字體使用),通常比 top 稍低。
  • middle:文字的中間對齊到 y 座標。
  • alphabetic(預設):字母基線(拉丁字母的底部,如 a、x 的下沿)。
  • ideographic:表意文字基線(漢字、日文字元的底部),通常比 alphabetic 稍低。
  • bottom:文字的底部(em 方格的下沿)對齊到 y 座標。

同樣,我們可以繪製一條參考橫線,觀察不同基線相對於該線的位置。

<div><div><div></div><span>html</span></div><div><div> <span>体验AI代码助手</span></div><div> <span>代码解读</span></div><div>复制代码</div></div></div>```
<span><span>canvas</span> <span>id</span>=<span>"baselineDemo"</span> <span>width</span>=<span>"500"</span> <span>height</span>=<span>"250"</span> <span>style</span>=<span>"border:1px solid #ccc"</span>></span><span><span>canvas</span>></span>
<span><span>script</span>></span><span></span>
<span>  <span>const</span> canvas = <span>document</span>.<span>getElementById</span>(<span>'baselineDemo'</span>);</span>
<span>  <span>const</span> ctx = canvas.<span>getContext</span>(<span>'2d'</span>);</span>
<span></span>
<span>  <span>// 繪製參考橫線(y = 120)</span></span>
<span>  ctx.<span>beginPath</span>();</span>
<span>  ctx.<span>strokeStyle</span> = <span>'gray'</span>;</span>
<span>  ctx.<span>setLineDash</span>([<span>5</span>, <span>5</span>]);</span>
<span>  ctx.<span>moveTo</span>(<span>0</span>, <span>120</span>);</span>
<span>  ctx.<span>lineTo</span>(<span>500</span>, <span>120</span>);</span>
<span>  ctx.<span>stroke</span>();</span>
<span>  ctx.<span>setLineDash</span>([]);</span>
<span></span>
<span>  ctx.<span>font</span> = <span>'20px Arial'</span>;</span>
<span>  ctx.<span>fillStyle</span> = <span>'black'</span>;</span>
<span>  ctx.<span>textAlign</span> = <span>'left'</span>;</span>
<span></span>
<span>  <span>// 繪製不同 baseline 的文字,y 座標統一為 120</span></span>
<span>  ctx.<span>textBaseline</span> = <span>'top'</span>;</span>
<span>  ctx.<span>fillText</span>(<span>'top'</span>, <span>30</span>, <span>120</span>);</span>
<span></span>
<span>  ctx.<span>textBaseline</span> = <span>'hanging'</span>;</span>
<span>  ctx.<span>fillText</span>(<span>'hanging'</span>, <span>120</span>, <span>120</span>);</span>
<span></span>
<span>  ctx.<span>textBaseline</span> = <span>'middle'</span>;</span>
<span>  ctx.<span>fillText</span>(<span>'middle'</span>, <span>240</span>, <span>120</span>);</span>
<span></span>
<span>  ctx.<span>textBaseline</span> = <span>'alphabetic'</span>;</span>
<span>  ctx.<span>fillText</span>(<span>'alphabetic'</span>, <span>350</span>, <span>120</span>);</span>
<span></span>
<span>  ctx.<span>textBaseline</span> = <span>'ideographic'</span>;</span>
<span>  ctx.<span>fillText</span>(<span>'ideographic'</span>, <span>30</span>, <span>200</span>);</span>
<span></span>
<span>  ctx.<span>textBaseline</span> = <span>'bottom'</span>;</span>
<span>  ctx.<span>fillText</span>(<span>'bottom'</span>, <span>180</span>, <span>200</span>);</span>
<span><span><span>script</span>></span></span>

![2.webp](https://i.imgur.com/sJ4Y5Eq.jpeg)

#### 8.2.3 文字方向:`direction`

`direction` 屬性設定文字的方向,影響 `start` 和 `end` 的對齊行為。可選值:

- `ltr`:從左到右
- `rtl`:從右到左
- `inherit`(預設):繼承 canvas 或文件的設定

通常用於多語言混合場景。下面簡單示例:

<div><div><div></div><span>javascript</span></div><div><div> <span>体验AI代码助手</span></div><div> <span>代码解读</span></div><div>复制代码</div></div></div>```
<span>ctx.<span>direction</span> = <span>'rtl'</span>;</span>
<span>ctx.<span>textAlign</span> = <span>'start'</span>;</span>
<span>ctx.<span>fillText</span>(<span>'نص عربي'</span>, <span>200</span>, <span>50</span>); <span>// 阿拉伯語文字,從右向左顯示</span></span>

8.3 限制文字寬度:maxWidth 參數

fillTextstrokeText 都支援可選的第四個參數 maxWidth。當文字的原始寬度超過 maxWidth 時,瀏覽器會水平壓縮字體(而不是截斷或換行)以適應指定寬度。

<div><div><div></div><span>html</span></div><div><div> <span>体验AI代码助手</span></div><div> <span>代码解读</span></div><div>复制代码</div></div></div>```
<span><span>canvas</span> <span>id</span>=<span>"maxWidthDemo"</span> <span>width</span>=<span>"400"</span> <span>height</span>=<span>"150"</span> <span>style</span>=<span>"border:1px solid #ccc"</span>></span><span><span>canvas</span>></span>
<span><span>script</span>></span><span></span>
<span>  <span>const</span> canvas = <span>document</span>.<span>getElementById</span>(<span>'maxWidthDemo'</span>);</span>
<span>  <span>const</span> ctx = canvas.<span>getContext</span>(<span>'2d'</span>);</span>
<span></span>
<span>  ctx.<span>font</span> = <span>'24px Arial'</span>;</span>
<span>  ctx.<span>fillStyle</span> = <span>'blue'</span>;</span>
<span></span>
<span>  <span>// 無限制</span></span>
<span>  ctx.<span>fillText</span>(<span>'无限制文本'</span>, <span>20</span>, <span>40</span>);</span>
<span></span>
<span>  <span>// 限制寬度為 100px</span></span>
<span>  ctx.<span>fillText</span>(<span>'压缩文本'</span>, <span>20</span>, <span>90</span>, <span>100</span>);</span>
<span><span><span>script</span>></span></span>

![image.png](https://i.imgur.com/7VKnJ3a.jpeg)

### 8.4 文字度量:`measureText()`

`measureText(text)` 方法回傳一個 [TextMetrics](https://link.juejin.cn?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fzh-CN%2Fdocs%2FWeb%2FAPI%2FTextMetrics) 物件,包含指定文字在當前字型下的尺寸資訊。最常用的屬性是 `width`(文字的寬度,單位像素)。

<div><div><div></div><span>html</span></div><div><div> <span>体验AI代码助手</span></div><div> <span>代码解读</span></div><div>复制代码</div></div></div>```
<span><span>canvas</span> <span>id</span>=<span>"measureDemo"</span> <span>width</span>=<span>"400"</span> <span>height</span>=<span>"150"</span> <span>style</span>=<span>"border:1px solid #ccc"</span>></span><span><span>canvas</span>></span>
<span><span>script</span>></span><span></span>
<span> <span>const</span> canvas = <span>document</span>.<span>getElementById</span>(<span>'measureDemo'</span>);</span>
<span> <span>const</span> ctx = canvas.<span>getContext</span>(<span>'2d'</span>);</span>
<span></span>
<span> ctx.<span>font</span> = <span>'24px Arial'</span>;</span>
<span> <span>const</span> text = <span>'Hello Canvas'</span>;</span>
<span> <span>const</span> metrics = ctx.<span>measureText</span>(text);</span>
<span></span>
<span> <span>// 在畫布中央繪製文字</span></span>
<span> <span>const</span> x = (canvas.<span>width</span> - metrics.<span>width</span>) / <span>2</span>;</span>
<span> <span>const</span> y = <span>80</span>;</span>
<span></span>
<span> ctx.<span>fillStyle</span> = <span>'black'</span>;</span>
<span> ctx.<span>fillText</span>(text, x, y);</span>
<span></span>
<span> <span>// 繪製一個矩形框表示文字寬度</span></span>
<span> ctx.<span>strokeStyle</span> = <span>'red'</span>;</span>
<span> ctx.<span>lineWidth</span> = <span>1</span>;</span>
<span> ctx.<span>strokeRect</span>(x, y - <span>24</span>, metrics.<span>width</span>, <span>24</span>); <span>// 粗略框選,基線為 alphabetic</span></span>
<span><span><span>script</span>></span></span>

image.png

第9章:圖片

Canvas 不僅能繪製圖形和文字,還能直接操作圖片。本章將介紹如何在畫布上繪製圖片、用圖片填充形狀,以及如何透過裁剪實現有趣的視覺效果。 在 Canvas 中繪製圖片,首先需要有一個圖片來源。通常我們使用 JavaScript 的 Image 物件來載入圖片。

<div><div><div></div><span>html</span></div><div><div> <span>体验AI代码助手</span></div><div> <span>代码解读</span></div><div>复制代码</div></div></div>```
<span><span>canvas</span> <span>id</span>=<span>"canvas1"</span> <span>width</span>=<span>"400"</span> <span>height</span>=<span>"300"</span>></span><span><span>canvas</span>></span>
<span><span>script</span>></span><span></span>
<span>  <span>const</span> canvas = <span>document</span>.<span>getElementById</span>(<span>'canvas1'</span>);</span>
<span>  <span>const</span> ctx = canvas.<span>getContext</span>(<span>'2d'</span>);</span>
<span>  <span>const</span> img = <span>new</span> <span>Image</span>();</span>
<span>  img.<span>src</span> = <span>'https://via.placeholder.com/150'</span>; <span>// 示例圖片</span></span>
<span>  img.<span>onload</span> = <span>() =></span> {</span>
<span>    ctx.<span>drawImage</span>(img, <span>50</span>, <span>50</span>);</span>
<span>  };</span>
<span><span><span>script</span>></span></span>

**關鍵點:** 一定要在圖片的 `load` 事件之後呼叫 `drawImage`,否則畫布上不會有任何內容。

最簡單的 `drawImage` 用法是傳入圖片物件以及目標座標 `(x, y)`,圖片會按照原始尺寸繪製。

![image.png](https://i.imgur.com/OXvp8sz.jpeg)

### 9.2 `drawImage` 的三種形式

`drawImage` 方法有三種呼叫形式,可以滿足不同的繪製需求。下面我們用一個完整的示例來對比展示這三種形式。

基礎繪製:`drawImage(img, x, y)` —— 圖片保持原始尺寸。

縮放繪製:`drawImage(img, x, y, width, height)` —— 圖片縮放到指定尺寸。

裁剪+縮放繪製:`drawImage(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)` —— 從來源圖片中裁剪一塊區域,再繪製到畫布上並縮放。

示例:在同一個畫布上,分別用三種方式繪製同一張圖片:

<div><div><div></div><span>html</span></div><div><div> <span>体验AI代码助手</span></div><div> <span>代码解读</span></div><div>复制代码</div></div></div>```
<span><span>canvas</span> <span>id</span>=<span>"compareCanvas"</span> <span>width</span>=<span>"600"</span> <span>height</span>=<span>"400"</span> <span>style</span>=<span>"border:1px solid #ccc"</span>></span><span><span>canvas</span>></span>
<span><span>script</span>></span><span></span>
<span> <span>const</span> canvas = <span>document</span>.<span>getElementById</span>(<span>'compareCanvas'</span>);</span>
<span> <span>const</span> ctx = canvas.<span>getContext</span>(<span>'2d'</span>);</span>
<span> <span>const</span> img = <span>new</span> <span>Image</span>();</span>
<span> img.<span>src</span> = <span>'https://picsum.photos/200'</span>; <span>// 一張 200x150 的示例圖</span></span>
<span> img.<span>onload</span> = <span>() =></span> {</span>
<span> <span>// 1. 基礎繪製(原始尺寸)</span></span>
<span> ctx.<span>drawImage</span>(img, <span>30</span>, <span>30</span>);</span>
<span> ctx.<span>fillStyle</span> = <span>'black'</span>;</span>
<span> ctx.<span>font</span> = <span>'12px sans-serif'</span>;</span>
<span> ctx.<span>fillText</span>(<span>'基础绘制 (原始尺寸)'</span>, <span>30</span>, <span>20</span>);</span>
<span></span>
<span> <span>// 2. 縮放繪製(寬120,高90)</span></span>
<span> ctx.<span>drawImage</span>(img, <span>250</span>, <span>30</span>, <span>120</span>, <span>90</span>);</span>
<span> ctx.<span>fillText</span>(<span>'缩放绘制 (120x90)'</span>, <span>250</span>, <span>20</span>);</span>
<span></span>
<span> <span>// 3. 裁剪+縮放繪製:從原圖 (50,30) 位置裁剪 100x80 區域,繪製到 (400,30) 處並縮放到 150x120</span></span>
<span> ctx.<span>drawImage</span>(img, <span>50</span>, <span>30</span>, <span>100</span>, <span>80</span>, <span>400</span>, <span>30</span>, <span>150</span>, <span>120</span>);</span>
<span> ctx.<span>fillText</span>(<span>'裁剪+缩放'</span>, <span>400</span>, <span>20</span>);</span>
<span> };</span>
<span><span><span>script</span>></span></span>

3.webp

9.3 用圖片填充形狀:createPattern

createPattern 方法可以基於圖片建立一個平鋪圖案(類似於 CSS 的 background-repeat),然後將這個圖案賦值給 fillStyle,之後繪製的所有形狀(矩形、圓形、任意路徑)都會用該圖片平鋪填充。

<div><div><div></div><span>js</span></div><div><div> <span>体验AI代码助手</span></div><div> <span>代码解读</span></div><div>复制代码</div></div></div>```
<span><span>const</span> pattern = ctx.<span>createPattern</span>(img, repetition);</span>

- `repetition` 取值:`"repeat"`(預設,雙向平鋪)、`"repeat-x"`(水平平鋪)、`"repeat-y"`(垂直平鋪)、`"no-repeat"`(不平鋪)。

<div><div><div></div><span>html</span></div><div><div> <span>体验AI代码助手</span></div><div> <span>代码解读</span></div><div>复制代码</div></div></div>```
<span><span>canvas</span> <span>id</span>=<span>"patternCanvas"</span> <span>width</span>=<span>"400"</span> <span>height</span>=<span>"200"</span>></span><span><span>canvas</span>></span>
<span><span>script</span>></span><span></span>
<span> <span>const</span> canvas = <span>document</span>.<span>getElementById</span>(<span>'patternCanvas'</span>);</span>
<span> <span>const</span> ctx = canvas.<span>getContext</span>(<span>'2d'</span>);</span>
<span> <span>const</span> img = <span>new</span> <span>Image</span>();</span>
<span> img.<span>src</span> = <span>'https://picsum.photos/30/30'</span>; </span>
<span> img.<span>onload</span> = <span>() =></span> {</span>
<span> <span>const</span> pattern = ctx.<span>createPattern</span>(img, <span>'repeat'</span>);</span>
<span> ctx.<span>fillStyle</span> = pattern;</span>
<span></span>
<span> <span>// 填充矩形</span></span>
<span> ctx.<span>fillRect</span>(<span>20</span>, <span>20</span>, <span>150</span>, <span>100</span>);</span>
<span></span>
<span> <span>// 填充圓形</span></span>
<span> ctx.<span>beginPath</span>();</span>
<span> ctx.<span>arc</span>(<span>280</span>, <span>80</span>, <span>50</span>, <span>0</span>, <span>Math</span>.<span>PI</span> * <span>2</span>);</span>
<span> ctx.<span>fill</span>();</span>
<span> };</span>
<span><span><span>script</span>></span></span>

4.webp

9.4 圖片與文字結合:圖案文字

同樣的技巧也可以用在文字上:將圖案賦值給 fillStyle,然後使用 fillText 繪製文字,文字內部就會顯示圖片平鋪效果,形成紋理字。

<div><div><div></div><span>html</span></div><div><div> <span>体验AI代码助手</span></div><div> <span>代码解读</span></div><div>复制代码</div></div></div>```
<span><span>canvas</span> <span>id</span>=<span>"textureTextCanvas"</span> <span>width</span>=<span>"400"</span> <span>height</span>=<span>"150"</span>></span><span><span>canvas</span>></span>
<span><span>script</span>></span><span></span>
<span>  <span>const</span> canvas = <span>document</span>.<span>getElementById</span>(<span>'textureTextCanvas'</span>);</span>
<span>  <span>const</span> ctx = canvas.<span>getContext</span>(<span>'2d'</span>);</span>
<span>  <span>const</span> img = <span>new</span> <span>Image</span>();</span>
<span>  img.<span>src</span> = <span>'https://picsum.photos/100'</span>;</span>
<span>  img.<span>onload</span> = <span>() =></span> {</span>
<span>    <span>const</span> pattern = ctx.<span>createPattern</span>(img, <span>'repeat'</span>);</span>
<span>    ctx.<span>fillStyle</span> = pattern;</span>
<span>    ctx.<span>font</span> = <span>'bold 60px Arial'</span>;</span>
<span>    ctx.<span>fillText</span>(<span>'纹理'</span>, <span>50</span>, <span>100</span>);</span>
<span>  };</span>
<span><span><span>script</span>></span></span>

![image.png](https://i.imgur.com/fxQlX2G.jpeg)

### 9.5 圖片與形狀結合:裁剪(`clip`)入門

`clip()` 方法是 Canvas 中一個非常實用的功能:它基於當前路徑設定一個裁剪區域,之後所有繪製的內容只顯示在該區域內部。超出部分會被隱藏。

使用方法:

先用 `beginPath` 和繪圖命令(如矩形、圓形、任意路徑)定義一個路徑。

呼叫 `clip()`,將當前路徑設定為裁剪區域。

執行繪製(圖片、圖形、文字等),它們只會出現在裁剪區域內。

通常配合 `save()` 和 `restore()` 使用,避免裁剪影響後續繪製。

示例:將圖片裁剪成圓形頭像

<div><div><div></div><span>html</span></div><div><div> <span>体验AI代码助手</span></div><div> <span>代码解读</span></div><div>复制代码</div></div></div>```
<span><span>canvas</span> <span>id</span>=<span>"clipCanvas"</span> <span>width</span>=<span>"200"</span> <span>height</span>=<span>"200"</span>></span><span><span>canvas</span>></span>
<span><span>script</span>></span><span></span>
<span> <span>const</span> canvas = <span>document</span>.<span>getElementById</span>(<span>'clipCanvas'</span>);</span>
<span> <span>const</span> ctx = canvas.<span>getContext</span>(<span>'2d'</span>);</span>
<span> <span>const</span> img = <span>new</span> <span>Image</span>();</span>
<span> img.<span>src</span> = <span>'https://via.placeholder.com/200/ff9900/ffffff?text=头像'</span>;</span>
<span> img.<span>onload</span> = <span>() =></span> {</span>
<span> ctx.<span>save</span>(); <span>// 保存當前狀態(無裁剪)</span></span>
<span> <span>// 繪製圓形路徑</span></span>
<span> ctx.<span>beginPath</span>();</span>
<span> ctx.<span>arc</span>(<span>100</span>, <span>100</span>, <span>80</span>, <span>0</span>, <span>Math</span>.<span>PI</span> <span>2</span>);</span>
<span> ctx.<span>clip</span>(); <span>// 設定裁剪區域為圓形內部</span></span>
<span> <span>// 繪製圖片,圖片只會在圓形區域內顯示</span></span>
<span> ctx.<span>drawImage</span>(img, <span>20</span>, <span>20</span>, <span>160</span>, <span>160</span>);</span>
<span> ctx.<span>restore</span>(); <span>// 恢復狀態,移除裁剪區域</span></span>
<span></span>
<span> <span>// 繪製一個邊框參考</span></span>
<span> ctx.<span>strokeStyle</span> = <span>'gray'</span>;</span>
<span> ctx.<span>beginPath</span>();</span>
<span> ctx.<span>arc</span>(<span>100</span>, <span>100</span>, <span>80</span>, <span>0</span>, <span>Math</span>.<span>PI</span>
<span>2</span>);</span>
<span> ctx.<span>stroke</span>();</span>
<span> };</span>
<span><span><span>script</span>></span></span>

5.webp

🚀下篇預告:變形、漸層與陰影篇

在下一篇中,你將學到:

  • 座標變換(平移、旋轉、縮放)的靈活運用,輕鬆實現複雜的圖形變換;
  • 繪圖狀態的管理與保存,高效切換樣式;

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


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

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝12   💬10   ❤️2
364
🥈
我愛JS
📝2   💬9   ❤️2
93
🥉
💬1  
4
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登