本章主要知識點:
- save() restore() 保存和恢復狀態(tài)
- translate(x, y), rotate(deg), scale(sx, sy)圖形變換
- transform(a, b, c, d, e, f), setTransform(a, b, c, d, e, f) 二維矩陣
圖形變換一般指translate, rotate, scale, transform等方法,對圖形進行控制。
通常繪制圖形一般會采用先繪制基本圖形,然后將基本圖形進行圖形變換得到自己想要的效果。
1.重構
上一章中講到繪制星星的圖形效果(星空效果),我們可以將其重構為,先繪制基本輪廓,然后再進行圖形變換操作。
重構為:
// 繪制一個標準的星星
// 小圓半徑是大圓的1/2
function starPath(ctx) {
ctx.beginPath();
for (var i = 0; i < 5; i++) {
ctx.lineTo(
Math.cos((18 + i * 72 - rotation) / 180 * Math.PI),
-Math.sin((18 + i * 72 - rotation) / 180 * Math.PI)
);
ctx.lineTo(
Math.cos((54 + i * 72 - rotation) / 180 * Math.PI) * 0.5,
-Math.sin((54 + i * 72 - rotation) / 180 * Math.PI) * 0.5
);
}
ctx.closePath();
}
function drawStar(ctx, x, y, R, rotation) {
// 圖形變換操作
// 繪制基本輪廓
starPath(ctx);
}
2.translate, rotate, scale
- translate(x, y): 移動圖形
- rotate(deg): 旋轉多大弧度
- scale(sx, sy): 縮放,要注意這個函數會產生副作用, 比如比例的平移,或增加描邊的寬度等,使用時需要注意
值得注意的是: 圖形變換是疊加的,比如:
// 假設圖形原坐標是(0, 0)
ctx.translate(100, 100); // 移動到(100, 100)
ctx.fillRect(0, 0, 400, 400); // 繪制一個矩形
// 再次移動
ctx.translate(200, 200);
# 此時的圖形坐標變?yōu)?100+200, 100+200) => (300, 300)
ctx.fillRect(0, 0, 400, 400)
一般我們移動繪制之后,再將其坐標移回去:
ctx.translate(100, 100); // 移動到(100, 100)
ctx.fillRect(0, 0, 400, 400); // 繪制一個矩形
# 繪制之后移動回去
ctx.translate(-100, -100);
// 再次移動
ctx.translate(200, 200);
# 因為上面進行了還原,此時的圖形坐標為(200, 200)
ctx.fillRect(0, 0, 400, 400)
但是這樣做顯得很繁瑣,因此canvas有保存狀態(tài),再恢復狀態(tài)的函數save(), restore()
3.save() | restore()
一般這2個函數成對出現
上面的例子可以寫為:
# 先保存之前的狀態(tài)
ctx.save();
ctx.translate(100, 100); // (100, 100)
ctx.fillRect(0, 0, 400, 400);
# 恢復到保存的狀態(tài)
ctx.restore();
ctx.save();
ctx.translate(200, 200);
ctx.fillRect(0, 0, 400, 400);
ctx.restore()
4.完成星空
可以在1.重構 的基礎上利用圖形變換完成星空圖:
// 獲取隨機顏色
function getRandomColor() {
return '#' + ( '00000' + (Math.random() * 0x10000000<<2) ).toString(16).slice(-6);
}
function drawStar(ctx, x, y, R, rotation) {
ctx.save();
// 進行圖形變換
ctx.translate(x, y);
ctx.rotate(rotation / 180 * Math.PI);
ctx.scale(R, R);
// 然后繪制基本的輪廓
starPath(ctx);
ctx.fillStyle = getRandomColor();
ctx.fill();
ctx.restore();
}
for (var i = 0; i < 100; i++) {
var ran = Math.random();
var x = ran * canvas.width;
# 這里有個技巧
# 想要得到畫布的65%,則添加一個系數0.65
var y = ran * canvas.height * 0.65;
var R = ran * 2 * 10;
var rotation = ran * 360;
drawStar(ctx, x, y, R, rotation);
}
tips: 想要得到星星分布在畫布高度的65%,則添加一個系數0.65
具體效果:圖形變換繪制星空
5.transform 變換矩陣
圖形學變換矩陣是都頂點坐標進行重計算的一種方式,對于二維系統,則是 3x3 的矩陣,對于三維系統則是 4x4 的矩陣。
對于3x3 的矩陣:

其中 a, c, b, d 負責對圖形進行scale, rotate, 對稱,skew 等變換, a, d負責圖形的縮放scale, e, f 負責對圖形進行平移(translate)變換。第3行也可以進行其他操作,二維矩陣 詳情。
對上圖中的默認值情況,即不進行變換時,該矩陣為單位矩陣。
對于transform函數:
transform(a, b, c, d, e, f)
所有:
translate(100, 100) =>
transform(1, 0, 0, 1, 100, 100)
# a為-1, 則產生對Y軸的鏡像效果
transform(-1, 0, 0, 1, 0, 0)
# 當然d的值為負數,則可以產生對x軸的鏡像翻轉效果
scale(1.5, 1.5) =>
transform(1.5, 0, 0, 1.5, 0, 0)
skew則改變b, c的值 =>
transform(1, 0.2, 0, 1, 0, 0)
setTransform()
同樣,transform操作是疊加的,我們可以使用setTransform來設置變換效果,使之前的transform失效:
ctx.save()
ctx.transform(1, 0, 0, 1, 50, 100)
ctx.transform(2, 0, 0, 1.5, 0, 0)
# 使上面的transform失效,得到新的transform效果
ctx.setTransform(1, 0, 0, 1, 100, 100)
總結
本章的知識和css中的基礎知識類似,學起來也比較容易理解,對于二維矩陣則需要多花功夫去了解。