Canvas是HTML5規格中的一部份,它允許你可以用程式來繪製一些2D圖形或是點陣圖,通常也被拿來製作HTML5遊戲之用,而本文主要介紹Canvas API中最常被初學者混淆的一組函式,它們分別為:「save()」和「restore()」,簡單來說~ 「save()」主要是用來保存目前Canvas的狀態,例如:style, lineWidth, font等,透過「save()」函式的呼叫它會將目前Canvas的狀態「Push」到「Stack」之中,而「restore()」函式就是從「Stack」來「Pop」出上一個Canvas的狀態,讀到這邊如果您已經了解「save/restore」的話,那非常恭禧你已經掌握要領了,反之~ 如果仍然有所疑惑的話,請看下面的範例介紹:
實作範例
本文將透過兩種不同的方式來實作出上圖的範例,以方便理解為何需要「save/restore」函式,使用它有何好處?
範例1 - 不採用save/restore
範例1完整的程式碼如下:(筆者假設您已經有基本的Canvas概念,所以會跳過一些基本的解說)
<!DOCTYPE html>
<html><head><meta charset="utf-8"></head><body>
<canvas id="a_canvas" width="200" height="150"></canvas>
<script type="text/javascript">
var canvas = document.getElementById("a_canvas");
var ctx = canvas.getContext("2d");
function drawRect(context, color) {
context.fillStyle = color;
context.fillRect(0, 0, 100, 30);
}
function rotateDeg(context, deg) {
var rad = deg * Math.PI / 180;
context.rotate(rad);
}
drawRect(ctx, "red");
ctx.translate(100,30);
rotateDeg(ctx, 45);
drawRect(ctx, "blue");
rotateDeg(ctx, 45);
ctx.translate(20, -100);
drawRect(ctx, "green");
</script>
</body></html>
P.S. 下述圖形的虛線都作為解說之用,並非繪製在Canvas上的線條
drawRect(ctx, "red");
透過自訂的「drawRect()」函式,然後在Canvas上繪製一個從座標點(0,0)的位置畫一個長為100px寬為30px的紅色矩形。
ctx.translate(100,30);
將目前Canvas的基準點位置設為(100,30),如上圖所示。
P.S. Canvas基準點位置預設為左上角,和Flash座標系統一致。
rotateDeg(ctx, 45);
drawRect(ctx, "blue");
透過自訂的「rotateDeg()」函式將Canvas往順時針旋轉45度,並繪製一個藍色矩形。
rotateDeg(ctx, 45);
接著再進行一次「rotateDeg()」函式將Canvas再往順時針旋轉45度。(此時已旋轉90度)
ctx.translate(20, -100);
到這邊的程式碼就是重點了,理論上如果以基準點為(0,0)的位置來說,目前要設定的相對基準點位置就是位於(200,50)的位置,但是在沒有運用「save/restore」的方式之下,我們必須自己在腦海裡進行一下矩陣轉換,同時根據上一次的基準點位置(100,30)以重設目前基準點位置(20,-100),最後再透過「drawRect(ctx, "green");」繪製出綠色的矩形出來。
範例2 - 採用save/restore
範例2完整的程式碼如下:
<!DOCTYPE html>
<html><head><meta charset="utf-8"></head><body>
<canvas id="a_canvas" width="200" height="150"></canvas>
<script type="text/javascript">
var canvas = document.getElementById("a_canvas");
var ctx = canvas.getContext("2d");
function drawRect(context, color) {
context.fillStyle = color;
context.fillRect(0, 0, 100, 30);
}
function rotateDeg(context, deg) {
var rad = deg * Math.PI / 180;
context.rotate(rad);
}
drawRect(ctx, "red");
ctx.save();
ctx.translate(100,30);
rotateDeg(ctx, 45);
drawRect(ctx, "blue");
ctx.restore();
ctx.translate(200,50);
rotateDeg(ctx, 90);
drawRect(ctx, "green");
</script>
</body></html>
drawRect(ctx, "red");
ctx.save();
除了在Canvas上繪製一個紅色的矩形外,這邊還呼叫了「save()」函式,意指為將目前的基準點位置(0,0)狀態「Push」到「Stack」中,以方便待會取回。
ctx.translate(100,30);
rotateDeg(ctx, 45);
drawRect(ctx, "blue");
將目前Canvas的基準點位置設為(100,30),同時旋轉45度並繪製上一個藍色矩形。
ctx.restore();
這裡呼叫「restore()」函式的重點就是要將剛剛「Push」到「Stack」的Canvas狀態取回,也就是重設基準點位置回到(0,0),並忘掉「save()」和「restore()」之間所設定的Canvas狀態,如:translate()、rotate()。
ctx.translate(200,50);
rotateDeg(ctx, 90);
drawRect(ctx, "green");
最後再重設基準點位置為(200,50),並順時針旋轉90度,然後畫上綠色矩形即大功告成,到這邊可以發現端倪了嗎?假設我們不採用「save/restore」函式來輔助的話,我們需要去進行矩陣轉換,而這只是個小範例~ 如果更複雜的圖形那勢必會轉到頭昏眼花... Orz,而透過「save/restore」函式的輔助,不僅讓程式碼的可讀性大大地增加,而且維護上也較為容易些嚕。
參考資源
Understanding save() and restore() for the Canvas Context