轉換
在本教程的早期,我們學習了畫布網格和座標空間。到目前為止,我們只使用了預設的網格,並根據我們的需要更改了整個畫布的大小。透過變換,有更強大的方法可以將原點平移到不同的位置、旋轉網格甚至縮放網格。
儲存和恢復狀態
在檢視變換方法之前,讓我們先看另外兩種方法,一旦你開始生成更復雜的繪圖,這兩種方法將是不可或缺的。
畫布狀態儲存在堆疊中。每次呼叫 save() 方法時,當前繪圖狀態都會被推入堆疊。繪圖狀態包括:
- 已應用的變換(例如,
translate、rotate和scale- 見下文)。 - 以下屬性的當前值
- 當前的剪裁路徑,我們將在下一節中看到。
您可以根據需要多次呼叫 save() 方法。每次呼叫 restore() 方法時,都會從堆疊中彈出最後一個儲存的狀態,並恢復所有儲存的設定。
儲存和恢復畫布狀態的示例
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
ctx.fillRect(0, 0, 150, 150); // Draw a Black rectangle with default settings
ctx.save(); // Save the original default state
ctx.fillStyle = "#0099ff"; // Make changes to saved settings
ctx.fillRect(15, 15, 120, 120); // Draw a Blue rectangle with new settings
ctx.save(); // Save the current state
ctx.fillStyle = "white"; // Make changes to saved settings
ctx.globalAlpha = 0.5;
ctx.fillRect(30, 30, 90, 90); // Draw a 50%-White rectangle with newest settings
ctx.restore(); // Restore to previous state
ctx.fillRect(45, 45, 60, 60); // Draw a rectangle with restored Blue setting
ctx.restore(); // Restore to original state
ctx.fillRect(60, 60, 30, 30); // Draw a rectangle with restored Black setting
}
第一步是繪製一個具有預設設定的大矩形。接下來,我們儲存此狀態並更改填充顏色。然後我們繪製第二個較小的藍色矩形並儲存狀態。再次,我們更改一些繪圖設定並繪製第三個半透明的白色矩形。
到目前為止,這與我們之前章節中的內容非常相似。然而,一旦我們呼叫第一個 restore() 語句,頂部繪圖狀態就會從堆疊中移除,設定也會被恢復。如果我們沒有使用 save() 儲存狀態,我們就需要手動更改填充顏色和透明度才能返回到之前的狀態。對於兩個屬性來說這很簡單,但如果我們有更多屬性,我們的程式碼會很快變得非常冗長。
當呼叫第二個 restore() 語句時,原始狀態(我們在第一次呼叫 save 之前設定的狀態)將被恢復,最後一個矩形將再次以黑色繪製。
平移
我們將要檢視的第一個變換方法是 translate()。此方法用於將畫布及其原點移動到網格中的不同點。
translate(x, y)-
在網格上移動畫布及其原點。
x表示水平移動的距離,y表示垂直移動網格的距離。

在進行任何變換之前儲存畫布狀態是一個好主意。在大多數情況下,呼叫 restore 方法比執行反向平移以返回原始狀態要容易得多。此外,如果您在迴圈中進行平移而不儲存和恢復畫布狀態,您可能會丟失部分繪圖,因為它繪製到了畫布邊緣之外。
translate 示例
此示例演示了平移畫布原點的一些好處。如果沒有 translate() 方法,所有矩形都將繪製在相同的位置 (0,0)。translate() 方法還使我們能夠將矩形放置在畫布上的任何位置,而無需在 fillRect() 函式中手動調整座標。這使其更容易理解和使用。
在 draw() 函式中,我們使用兩個 for 迴圈呼叫 fillRect() 函式九次。在每個迴圈中,畫布都會被平移,繪製矩形,然後畫布會恢復到其原始狀態。請注意 fillRect() 的呼叫每次都使用相同的座標,依靠 translate() 來調整繪圖位置。
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
ctx.save();
ctx.fillStyle = `rgb(${51 * i} ${255 - 51 * i} 255)`;
ctx.translate(10 + j * 50, 10 + i * 50);
ctx.fillRect(0, 0, 25, 25);
ctx.restore();
}
}
}
旋轉
第二個變換方法是 rotate()。我們使用它圍繞當前原點旋轉畫布。
rotate(angle)-
圍繞當前原點以
angle弧度順時針旋轉畫布。

旋轉中心點始終是畫布原點。要更改中心點,我們需要使用 translate() 方法移動畫布。
rotate 示例
在此示例中,我們將使用 rotate() 方法,首先圍繞畫布原點旋轉一個矩形,然後藉助 translate() 圍繞矩形中心旋轉。
注意:角度以弧度為單位,而不是度。要進行轉換,我們使用:radians = (Math.PI/180)*degrees。
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
// left rectangles, rotate from canvas origin
ctx.save();
// blue rect
ctx.fillStyle = "#0095DD";
ctx.fillRect(30, 30, 100, 100);
ctx.rotate((Math.PI / 180) * 25);
// grey rect
ctx.fillStyle = "#4D4E53";
ctx.fillRect(30, 30, 100, 100);
ctx.restore();
// right rectangles, rotate from rectangle center
// draw blue rect
ctx.fillStyle = "#0095DD";
ctx.fillRect(150, 30, 100, 100);
ctx.translate(200, 80); // translate to rectangle center
// x = x + 0.5 * width
// y = y + 0.5 * height
ctx.rotate((Math.PI / 180) * 25); // rotate
ctx.translate(-200, -80); // translate back
// draw grey rect
ctx.fillStyle = "#4D4E53";
ctx.fillRect(150, 30, 100, 100);
}
要圍繞矩形自身中心旋轉矩形,我們將畫布平移到矩形中心,然後旋轉畫布,然後將畫布平移回 0,0,然後繪製矩形。
縮放
下一個變換方法是縮放。我們使用它來增加或減少畫布網格中的單位。這可用於繪製縮小或放大的形狀和點陣圖。
scale(x, y)-
在水平方向上按 x,在垂直方向上按 y 縮放畫布單位。這兩個引數都是實數。小於 1.0 的值會減小單位大小,大於 1.0 的值會增大單位大小。值為 1.0 時單位大小不變。
使用負數可以實現軸映象(例如,使用 translate(0,canvas.height); scale(1,-1); 將獲得眾所周知的笛卡爾座標系,原點位於左下角)。
預設情況下,畫布上的一個單位正好是一個畫素。如果我們應用,例如,縮放因子 0.5,則生成的單位將是 0.5 畫素,因此形狀將以一半大小繪製。類似地,將縮放因子設定為 2.0 將增加單位大小,一個單位現在變成兩個畫素。這會導致形狀以兩倍大小繪製。
scale 示例
在最後一個示例中,我們將繪製具有不同縮放因子的形狀。
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
// draw a simple rectangle, but scale it.
ctx.save();
ctx.scale(10, 3);
ctx.fillRect(1, 10, 10, 10);
ctx.restore();
// mirror horizontally
ctx.scale(-1, 1);
ctx.font = "48px serif";
ctx.fillText("MDN", -135, 120);
}
變換
最後,以下變換方法允許直接修改變換矩陣。
transform(a, b, c, d, e, f)-
將當前變換矩陣與由其引數描述的矩陣相乘。變換矩陣由以下公式描述:
如果任何引數為
Infinity,則變換矩陣必須標記為無窮大,而不是方法丟擲異常。
此函式的引數是:
a(m11)-
水平縮放。
b(m12)-
水平傾斜。
c(m21)-
垂直傾斜。
d(m22)-
垂直縮放。
e(dx)-
水平移動。
f(dy)-
垂直移動。
setTransform(a, b, c, d, e, f)-
將當前變換重置為單位矩陣,然後使用相同的引數呼叫
transform()方法。這基本上是在一步中撤銷當前變換,然後設定指定的變換。 resetTransform()-
將當前變換重置為單位矩陣。這等同於呼叫:
ctx.setTransform(1, 0, 0, 1, 0, 0);
transform 和 setTransform 示例
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
const sin = Math.sin(Math.PI / 6);
const cos = Math.cos(Math.PI / 6);
ctx.translate(100, 100);
let c = 0;
for (let i = 0; i <= 12; i++) {
c = Math.floor((255 / 12) * i);
ctx.fillStyle = `rgb(${c} ${c} ${c})`;
ctx.fillRect(0, 0, 100, 10);
ctx.transform(cos, sin, -sin, cos, 0, 0);
}
ctx.setTransform(-1, 0, 0, 1, 100, 100);
ctx.fillStyle = "rgb(255 128 255 / 50%)";
ctx.fillRect(0, 50, 100, 100);
}