之前三節(jié)所說的坐標變換的三種方式——平移translate(),縮放scale(),以及旋轉(zhuǎn)rotate()都可以通過transform()做到。
在介紹矩陣變換transform()前,我們來說一說什么是變換矩陣。

以上是Canvas中transform()方法所對應(yīng)的變換矩陣。而此方法正是傳入圖中所示的六個參數(shù),具體為context.transform(a,b,c,d,e,f)。
各參數(shù)意義對應(yīng)如下表:
| 參數(shù) | 意義 |
|---|---|
| a | 水平縮放(1) |
| b | 水平傾斜(0) |
| c | 垂直傾斜(0) |
| d | 垂直縮放(1) |
| e | 水平位移(0) |
| f | 垂直位移(0) |
當我們把對應(yīng)的0或1代入進矩陣,可以發(fā)現(xiàn)這是一個單位矩陣(水平和垂直縮放默認值是1,代表縮放1倍,即不縮放)。該方法使用一個新的變化矩陣與當前變換矩陣進行乘法運算,然后得到各種變化的效果。
這里簡單說一下,當我們想對一個圖形進行變換的時候,只要對變換矩陣相應(yīng)的參數(shù)進行操作,操作之后,對圖形的各個定點的坐標分別乘以這個矩陣,就能得到新的定點的坐標。
而Canvas繪圖中,就給咱們提供了一個函數(shù)來改變這個變換矩陣,那就是transform()。
transform()注:以下三個變換的轉(zhuǎn)換結(jié)論轉(zhuǎn)自張雯的博客,童鞋們可以戳進去看看詳情。

如上圖所示:
x’ = x + dx,
y’ = y + dy。
也即是說可以使用
context.transform (1,0,0,1,dx,dy)代替context.translate(dx,dy)。
也可以使用
context.transform(0,1,1,0,dx,dy)代替。
同理可以使用
context.transform(sx,0,0,sy,0,0)代替context.scale(sx, sy);
也可以使用context.transform(0,sy,sx,0,0,0);
如上圖圖所示:
B點是通過A點逆時針旋轉(zhuǎn)θ得到,
x = r * cosa,
y = r * sina
即
x’=r*cos(a+θ)=x*cosθ-y*sinθ,
y’=r*sin(a+θ)=x*sinθ+y*cosθ
也即是

綜上:
context.transform(Math.cos(θ*Math.PI/180),Math.sin(θ*Math.PI/180), -Math.sin(θ*Math.PI/180),Math.cos(θ*Math.PI/180),0,0)可以替代context.rotate(θ)。
也可以使用
context.transform(-Math.sin(θ*Math.PI/180),Math.cos(θ*Math.PI/180), Math.cos(θ*Math.PI/180),Math.sin(θ*Math.PI/180),0,0)替代。
以上結(jié)論引自張雯的博客,很好的說明了矩陣變換的過程,但是結(jié)論過于復(fù)雜,建議使用transform()的時候,可以在如下幾個情況下使用:
context.transform (1,0,0,1,dx,dy)代替context.translate(dx,dy)context.transform(sx,0,0,sy,0,0)代替context.scale(sx, sy)context.transform(0,b,c,0,0,0)來實現(xiàn)傾斜效果(最實用)。不用再使用它去實現(xiàn)旋轉(zhuǎn)了,另外也沒有也不用全記這些結(jié)論,直接記下abcdef六個參數(shù)的意義,效果是一樣的。
下面我們看一個代碼,熟悉一下:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>矩陣變換</title>
<style>
body { background: url("./images/bg3.jpg") repeat; }
#canvas { border: 1px solid #aaaaaa; display: block; margin: 50px auto; }
</style>
</head>
<body>
<div id="canvas-warp">
<canvas id="canvas">
你的瀏覽器居然不支持Canvas?!趕快換一個吧??!
</canvas>
</div>
<script>
window.onload = function(){
var canvas = document.getElementById("canvas");
canvas.width = 800;
canvas.height = 600;
var context = canvas.getContext("2d");
context.fillStyle = "#FFF";
context.fillRect(0,0,800,600);
context.fillStyle = "yellow";
context.strokeStyle = "#00AAAA";
context.lineWidth = 5;
context.save();
//平移至(300,200)
context.transform(1,0,0,1,300,200);
//水平方向放大2倍,垂直方向放大1.5倍
context.transform(2,0,0,1.5,0,0);
//水平方向向右傾斜寬度10%的距離,垂直方向向上傾斜高度10%的距離
context.transform(1,-0.1,0.1,1,0,0);
context.fillRect(0,0,200,200);
context.strokeRect(0,0,200,200);
context.restore();
};
</script>
</body>
</html>
運行結(jié)果:

setTransform()方法transform()方法的行為相對于由 rotate(),scale(), translate(), or transform() 完成的其他變換。例如:如果我們已經(jīng)將繪圖設(shè)置為放到兩倍,則 transform() 方法會把繪圖放大兩倍,那么我們的繪圖最終將放大四倍。這一點和之前的變換是一樣的。
但是setTransform()不會相對于其他變換來發(fā)生行為。它的參數(shù)也是六個,context.setTransform(a,b,c,d,e,f),與transform()一樣。
這里我們通過一個例子來說明:
繪制一個矩形,通過 setTransform() 重置并創(chuàng)建新的變換矩陣,再次繪制矩形,重置并創(chuàng)建新的變換矩陣,然后再次繪制矩形。
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>矩陣變換</title>
<style>
body { background: url("./images/bg3.jpg") repeat; }
#canvas { border: 1px solid #aaaaaa; display: block; margin: 50px auto; }
</style>
</head>
<body>
<div id="canvas-warp">
<canvas id="canvas">
你的瀏覽器居然不支持Canvas?!趕快換一個吧?。? </canvas>
</div>
<script>
window.onload = function(){
var canvas = document.getElementById("canvas");
canvas.width = 800;
canvas.height = 600;
var context = canvas.getContext("2d");
context.fillStyle = "#FFF";
context.fillRect(0,0,800,600);
context.fillStyle="yellow";
context.fillRect(200,100,250,100)
context.setTransform(1,0.5,-0.5,1,30,10);
context.fillStyle="red";
context.fillRect(200,100,250,100);
context.setTransform(1,0.5,-0.5,1,30,10);
context.fillStyle="blue";
context.fillRect(200,100,250,100);
};
</script>
</body>
</html>
運行結(jié)果:

解釋一下過程:每當我們調(diào)用 setTransform() 時,它都會重置前一個變換矩陣然后構(gòu)建新的矩陣,因此在下面的例子中,不會顯示紅色矩形,因為它在藍色矩形下面。
這一節(jié)的內(nèi)容有些難和多,但是除了傾斜其他并不實用,所以童鞋們盡量了解一下就好,能夠掌握就更好啦!至此,所有的圖形變換我們已經(jīng)上完了,基本的三大變化——平移變換、旋轉(zhuǎn)變換、縮放變換,以及萬能的矩陣變換。大家都以及完全掌握了嗎?如果不熟練,要回頭多看看,并且多加練習(xí)。稍作休息,下一節(jié)開始,就是新的航程!我們繼續(xù)前進!