繪制狀態(tài)存儲
1. 前言
canvas 中設(shè)置屬性多數(shù)都是全局使用的,我們在操作時難免會遇到某個屬性被修改后,當(dāng)再次使用到這個屬性時必須是默認(rèn)值的情況,或者我們繪制多個矩形,這些矩形的填充顏色和陰影有一定規(guī)律,比如填充顏色為 紅、黃、藍(lán)、黃、紅這樣的順序,陰影也是有對應(yīng)順序,我們應(yīng)該怎么做呢?本小節(jié)我們就來學(xué)習(xí)如何保存某個繪制狀態(tài)和快速地將繪制狀態(tài)恢復(fù)到上一個階段。
2. 存儲繪制狀態(tài)
我們先看一個不使用存儲繪制狀態(tài)的案例,這個案例中,我們需要繪制5個矩形,其中第一個和第五個矩形相同,第二個和第四個矩形相同。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>慕課網(wǎng)Wiki</title>
<style>
#imooc{
border:1px solid #ccc;
}
</style>
</head>
<body>
<canvas id="imooc">您的瀏覽器不支持 HTML5 canvas 標(biāo)簽</canvas>
<script>
const canvas = document.getElementById('imooc');
canvas.width=360;
canvas.height=130;
const ctx = canvas.getContext('2d');
// 繪制第一個矩形
ctx.fillStyle="red"
ctx.shadowBlur=2;
ctx.shadowOffsetX=4;
ctx.shadowOffsetY=4;
ctx.shadowColor="#ccc"
ctx.fillRect(40,40, 40,40)
// 繪制第二個矩形
ctx.fillStyle="yellow"
ctx.shadowBlur=3;
ctx.shadowOffsetX=8;
ctx.shadowOffsetY=8;
ctx.shadowColor="#456795"
ctx.fillRect(100,40, 40,40)
// 繪制第三個矩形
ctx.fillStyle="blue"
ctx.shadowBlur=5;
ctx.shadowOffsetX=5;
ctx.shadowOffsetY=5;
ctx.shadowColor="#222"
ctx.fillRect(160,40, 40,40)
// 繪制第四個矩形
ctx.fillStyle="yellow"
ctx.shadowBlur=3;
ctx.shadowOffsetX=8;
ctx.shadowOffsetY=8;
ctx.shadowColor="#456795"
ctx.fillRect(220,40, 40,40)
// 繪制第五個矩形
ctx.fillStyle="red"
ctx.shadowBlur=2;
ctx.shadowOffsetX=4;
ctx.shadowOffsetY=4;
ctx.shadowColor="#ccc"
ctx.fillRect(280,40, 40,40)
</script>
<body>
</html>
運行結(jié)果:

上面案例中,可以看到,繪制第四個正方形的時候,把所有屬性又寫了一遍,但是這個屬性和我們繪制第二個正方形的屬性一樣,第五個正方形和第一個正方形的屬性一樣,這樣我們不僅浪費時間還增加了代碼維護(hù)成本,維護(hù)成本主要指:假如我們要修改紅色的正方形為其他顏色,我們就得修改兩處代碼。
今天我們就用存儲繪制狀態(tài)的方法來優(yōu)化一下上面代碼,還是上面那個案例,我們換一種寫法。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>慕課網(wǎng)Wiki</title>
<style>
#imooc{
border:1px solid #ccc;
}
</style>
</head>
<body>
<canvas id="imooc">您的瀏覽器不支持 HTML5 canvas 標(biāo)簽</canvas>
<script>
const canvas = document.getElementById('imooc');
canvas.width=360;
canvas.height=130;
const ctx = canvas.getContext('2d');
// 繪制第一個矩形
ctx.fillStyle="red"
ctx.shadowBlur=2;
ctx.shadowOffsetX=4;
ctx.shadowOffsetY=4;
ctx.shadowColor="#ccc"
ctx.save(); // 這里把當(dāng)前畫布的屬性做了一個標(biāo)記,我們稱為:標(biāo)記一
ctx.fillRect(40,40, 40,40)
// 繪制第二個矩形
ctx.fillStyle="yellow"
ctx.shadowBlur=3;
ctx.shadowOffsetX=8;
ctx.shadowOffsetY=8;
ctx.shadowColor="#456795"
ctx.save(); // 這里把當(dāng)前畫布的屬性做了第二個標(biāo)記,我們稱為:標(biāo)記二
ctx.fillRect(100,40, 40,40)
// 繪制第三個矩形
ctx.fillStyle="blue"
ctx.shadowBlur=5;
ctx.shadowOffsetX=5;
ctx.shadowOffsetY=5;
ctx.shadowColor="#222"
ctx.fillRect(160,40, 40,40)
// 繪制第四個矩形
ctx.restore() // 這里我們讀取了最新的一個標(biāo)記,也就是讀區(qū)了標(biāo)記二的狀態(tài),標(biāo)記二被讀取后就消失了。
ctx.fillRect(220,40, 40,40)
// 繪制第五個矩形
ctx.restore(); // 這里我們讀取了最新的一個標(biāo)記,也就是讀區(qū)了標(biāo)記一的狀態(tài),因為標(biāo)記二已經(jīng)消失了
ctx.fillRect(280,40, 40,40)
</script>
<body>
</html>
運行結(jié)果:

我們可以看到,運行結(jié)果是一樣的,我們把主要代碼拆分講解一下。
-
設(shè)置繪制矩形的相關(guān)屬性,并調(diào)用 save 方法保存一個繪制狀態(tài)。
ctx.fillStyle="red" ctx.shadowBlur=2; ctx.shadowOffsetX=4; ctx.shadowOffsetY=4; ctx.shadowColor="#ccc" ctx.save(); // 這里把當(dāng)前畫布的屬性做了一個標(biāo)記,我們稱為:標(biāo)記一 ctx.fillRect(40,40, 40,40)
-
設(shè)置第二個矩形的相關(guān)屬性,并調(diào)用 save 方法保存一個繪制狀態(tài),這個狀態(tài)會堆放到上一個狀態(tài)的上面,該狀態(tài)的存儲符合“?!钡奶匦裕合冗M(jìn)后出,也就是最先放進(jìn)去的最后才被拿走。
我們看一個形象的圖片,小孩的玩具。
最下面的大圈是最先放進(jìn)去的,再取出時它是最后一個被拿出來的。
ctx.fillStyle="yellow" ctx.shadowBlur=3; ctx.shadowOffsetX=8; ctx.shadowOffsetY=8; ctx.shadowColor="#456795" ctx.save(); // 這里把當(dāng)前畫布的屬性做了第二個標(biāo)記,我們稱為:標(biāo)記二 ctx.fillRect(100,40, 40,40)
-
繪制第四個矩形,我們在繪制前先調(diào)用
ctx.restore
方法取出一個狀態(tài),取出的狀態(tài)會應(yīng)用到當(dāng)前畫布上。當(dāng)前存儲的所有狀態(tài)中,最上面的是標(biāo)簽二,也就是第二個矩形的狀態(tài),所以這里取出來的就是第二個矩形的狀態(tài)屬性。ctx.restore() // 取出狀態(tài) ctx.fillRect(220,40, 40,40)
-
繪制第五個矩形,同樣調(diào)用
ctx.restore
方法取出一個狀態(tài),當(dāng)前存儲的狀態(tài)中只有標(biāo)簽一,也就是第一個矩形的狀態(tài),所以這里取出來的就是第一個矩形的狀態(tài)屬性。ctx.restore(); // 取出狀態(tài) ctx.fillRect(280,40, 40,40)
特別注意 當(dāng)存儲的狀態(tài)被取完以后,再去取一個狀態(tài),此時會出問題。解決辦法就是:讀取狀態(tài)
restore
的次數(shù)只能小于等于存儲save
的次數(shù)。
3. 方法整理
本小節(jié)我們學(xué)習(xí)了 save
方法和 restore
方法,它們的主要作用是存儲某一個階段的屬性狀態(tài),后面可以快速恢復(fù)存儲的狀態(tài)。
3.1 存儲狀態(tài) save
save 說明
- save 方法主要目的是存儲 canvas 當(dāng)前全部狀態(tài),此方法可以多次執(zhí)行,每次執(zhí)行都會存儲一次當(dāng)前狀態(tài)。
語法:
ctx.save();
變量說明:
沒有參數(shù)。
3.2 存儲狀態(tài) restore
restore 說明
- restore 方法主要是用于取出存儲在 canvas 中的最新狀態(tài),此方法可以多次執(zhí)行,但是不能超過已存儲狀態(tài)的次數(shù)。
語法:
ctx.restore();
變量說明:
沒有參數(shù)。
4. 總結(jié)
本小節(jié)我們主要學(xué)習(xí)了利用 save
和 restore
存儲和拿取畫布狀態(tài)的方法,存儲狀態(tài)的目的是后面可以快速恢復(fù)到上一個繪制狀態(tài)。本小節(jié)我們只是簡單體會了存儲和拿取的過程,后面小節(jié)我們學(xué)習(xí)了旋轉(zhuǎn)變形的操作,會頻繁地用到這個操作。