文章以二維變換爲例說明,三維變換採用類似的操作理解即可。
變換的座標系統
默認的變換座標系統,變換原點爲元素中心,x軸與y軸與web中定義的一致。其中,變換原點可以用transform-origin屬性來設置。transform是以該座標系統爲參考,對變換中圖形上每個點的座標進行更新,來得到變換後的圖形。
線性變換
線性變換需要滿足兩個條件:
- 不改變座標原點
- 每個點的座標在更新時,採用線性函數來更新。用數學中的線性方程表達如下{x′=y′=ax+cybx+dy
用矩陣的形式表示爲[x′y′]=[abcd][xy]
CSS Transform中,除了translate變換外,其他都屬於線性變換。因此,除了translate外,其他變換可以用2*2的矩陣描述如下[abcd],而translate變換需要額外描述,因此CSS Transform的矩陣描述如下⎣⎡ab0cd0ef1⎦⎤其中,[abcdef]其實足夠描述二維變換了,最後一行是爲了矩陣運算而補充的。e和f就是爲了translate而增加的額外描述,e用來描述translateX,f用來描述translateY。
matrix()函數
CSS Transform中的所有變換,實際上都是matrix()函數的封裝,爲了便於記憶和使用。在二維變換中,matrix()函數接收6個參數爲matrix(a, b, c, d, e, f),即爲上文中的矩陣元素。matrix()函數本質上就是上文中的變換矩陣,對於不同的變換,6個參數各不相同。
下列變換中,假設沒有變換時,圖形上點的座標爲(x, y),變換後爲(x’, y’)。爲了方便運算,點的座標額外增加一行爲1;
沒有變換
matrix矩陣爲⎣⎡100010001⎦⎤,爲一個3*3的單位矩陣。因此,所有點的座標沒有任何變化。
translate變換
translate變換隻是給e和f賦值,其中translateX()給e賦值,translateY()給f賦值,此時matrix矩陣爲⎣⎡100010ef1⎦⎤
變換的過程表示爲⎣⎡x′y′1⎦⎤=⎣⎡100010ef1⎦⎤⎣⎡xy1⎦⎤線性方程爲
{x′=y′=x+ey+f
可以看出,translate變換相當於在原來座標系統下,每個點的x座標增加e,y座標增加f,得到了新的座標。
改變座標原點的理解:也可以這樣理解,讓e和f都等於0,此時每個點的座標都不變,而座標系統的原點從(0, 0)移動到了(e, f)。從這個角度來看,translate改變了座標原點的位置。也就是說,原來的圖形座標都不用變,只要把座標原點移動到(e, f)的位置即可。注意:(e, f)這個位置座標是以原座標系統爲參考點的。用這種方式理解的前提是,原來的座標系統用來定位新的座標系統,新的座標系統用來定位圖形的座標。
rotate變換
rotate()變換是指圍繞原點,旋轉某個角度α,正數表示順時針旋轉,負數表示逆時針旋轉。對應於matrix矩陣,指的是⎣⎡cosαsinα0−sinαcosα0001⎦⎤變換的過程表示爲⎣⎡x′y′1⎦⎤=⎣⎡cosαsinα0−sinαcosα0001⎦⎤⎣⎡xy1⎦⎤線性方程爲{x′=y′=xcosα−ysinαxsinα+ycosα
可以看出,旋轉變換是對x和y座標進行三角函數運算
scale變換
scale()變換是指對圖形的縮放,按照某個比例放大或縮小圖形。縮放是對x軸和y軸座標刻度的更改,因此對應於matrix矩陣,指的是⎣⎡a000d0001⎦⎤a表示x軸的縮放比例,d表示y軸的縮放比例。變換的過程爲⎣⎡x′y′1⎦⎤=⎣⎡a000d0001⎦⎤⎣⎡xy1⎦⎤線性方程爲{x′=y′=axdy可以看出,的確是將x座標乘以a,將y座標乘以d。
skew變換
skew()變換指的是將圖形的水平線或垂直線做一定程度的傾斜,即看起來變歪了。由於傾斜涉及到角度,因此matrix矩陣爲⎣⎡1tanθy0tanθx10001⎦⎤其中,θx和θy分別爲skewX()和skewY()的參數,θx表示垂直線傾斜角(正數表示向x軸正半軸傾斜),θy表示水平線傾斜角(正數表示向y軸正半軸傾斜)。這一點從線性方程中可以看出。變換過程爲⎣⎡x′y′1⎦⎤=⎣⎡1tanθy0tanθx10001⎦⎤⎣⎡xy1⎦⎤線性方程爲{x′=y′=x+ytanθxy+xtanθy從中可以看出,x和y座標都是在原來基礎上,增加了一部分由角度決定的量,結果就是變歪了。
複合變換
這裏的複合變換,指的是一個transform屬性中,包含以上幾個變換,由以上幾個變換按順序組成。這裏的順序非常重要,因爲順序不同,最後得到的matrix矩陣會不同。尤其在包含translate變換的情況下,因爲translate的非線性,導致的變換差異很大。
複合變換的規則是,transform屬性中,按從左向右的順序,將代表各個變換的matrix矩陣依次相乘,得到最終的matrix矩陣。由於矩陣乘法不滿足交換律,因此這裏的順序很重要。另外,translate如果放在其他變換的右邊,其他變換就會連translate變換一起變換。聽起來很亂吧,下面用matrix矩陣相乘的法則來演示。
transform: rotate(α) translate(e, f)
以上面的變換爲例,這意味着先進行translate變換,再進行rotate變換。變換過程爲⎣⎡x′y′1⎦⎤=⎣⎡cosαsinα0−sinαcosα0001⎦⎤⎣⎡100010ef1⎦⎤⎣⎡xy1⎦⎤把中間的matrix矩陣計算一下⎣⎡cosαsinα0−sinαcosα0001⎦⎤⎣⎡100010ef1⎦⎤=⎣⎡cosαsinα0−sinαcosα0ecosα−fsinαesinα+fcosα1⎦⎤線性方程爲{x′=y′=xcosα−ysinα+ecosα−fsinαxsinα+ycosα+esinα+fcosα
理解方式1:座標原點不變
從這裏可以看出,變換後的座標相當於對原座標做rotate變換,加上對translate增加的座標做的rotate變換。如果我們寫成{x′=y′=(x+e)cosα−(y+f)sinα(x+e)sinα+(y+f)cosα可以看出,先做translate變換,然後將變換後的座標整體再做rotate變換。我們的座標系統沒有變,座標原點仍然是(0, 0),將translate所做變換後的座標,再一起做rotate變換。對應到圖形上,就是讓圖形先做translate變換,然後變換後的圖形再做rotate變換(座標原點爲(0, 0) )。
理解方式2:座標原點隨translate改變
根據translate變換會更改座標原點的方式,座標原點變爲[ecosα−fsinαesinα+fcosα],此時圖形的座標相對於新的座標系,則線性方程變爲{x′=y′=xcosα−ysinαxsinα+ycosα
此時的操作相當於,對座標原點進行平移translate(e, f),然後再rotate(α),最後再對圖形在新座標原點上做rotate(α)。因此,translate變換如果在其他變換的右邊,則其他變換要加在translate變換上再對座標原點做變換。其實,用座標原點跟隨translate改變的理解方式比較複雜。
因此,複合變換中,translate變換如果出現在其他變換的右邊,那麼該複合變換不是變換之間簡單的線性疊加,而是translate變換會與其他變換進行線性疊加(應用到座標原點,從而得到新的座標原點),再將其他變換線性疊加(直接給圖形在新座標系應用這些變換)
transform: translate(e, f) rotate(α)
作爲對比,來看一下順序相反的情況。計算得到最終的matrix矩陣爲⎣⎡cosαsinα0−sinαcosα0ef1⎦⎤線性方程爲{x′=y′=xcosα−ysinα+exsinα+ycosα+f可以看出,先rotate操作,再直接加上translate(e, f)座標增量即可,不會出現上方的對translate()座標增量也要rotate的操作。
transform: skew(θx, θy) rotate(α)與transform: rotate(α) skew(θx, θy)的對比
由於不涉及translate變換,所以我們用2*2的矩陣表示就可以了。
transform: skew(θx, θy) rotate(α)對應的matrix矩陣爲[1tanθytanθx1][cosαsinα−sinαcosα]=[cosα+sinαtanθxsinα+cosαtanθy−sinα+cosαtanθxcosα−sinαtanθy]
線性方程爲{x′=y′=(xcosα−ysinα)+(xsinα+ycosα)tanθx(xsinα+ycosα)+(xcosα−ysinα)tanθy可以發現,正好是rotate後的座標再進行skew()的過程。如果我們定義xr和yr爲rotate後的座標,可以清晰地發現這一點。rotate後的座標如下{xr=yr=xcosα−ysinαxsinα+ycosα再進行skew的座標如下{x′=y′=xr+yrtanθyyr+xrtanθx
transform: rotate(α) skew(θx, θy)對應的matrix矩陣爲[cosαsinα−sinαcosα][1tanθytanθx1]=[cosα−sinαtanθysinα+cosαtanθy−sinα+cosαtanθxcosα+sinαtanθx]線性方程爲{x′=yr=xr+yrtanθxyr+xrtanθy
這說明,線性變換之間的順序不會引入對座標原點的變換,只要按照順序依次應用變換即可。