想寫寫關于矩陣變換的博文已經想了很久了,今天看到 winter 寫的一篇博客CSS3:transform與transition背后的數學原理,于是就促成了本文。注意,下面的演示內容需要現代瀏覽器支持。比如Chrome/Firefox/Opera。閱讀器中無法看到演示。
矩陣是線性代數中的內容,在計算機圖形學中就拿來做矩陣變換。在以前,對于前端工作來說,幾乎用不到矩陣變換。然而,隨著瀏覽器的進步,HTML5和CSS3的普及,對于前端可以操作的東西越來越多,于是,矩陣變換也出現在視野當中了。
矩陣變換,聽起來是一個挺高級的東西,其實本質上只不過是把一系列簡單的數學運算給包裝一下,賦予一個比較華麗和高深的外表而已。如果你之前沒有接觸過矩陣運算,也不用慌,跳過下面矩陣公式,直接看每條后面的黑體字公式即可。這些公式僅僅涉及高中水平的加減運算和三角函數而已。除了最開始的時候我會搬出那個矩陣,之后的討論我會避開矩陣的公式,直接用容易理解的方式闡述問題。
最早瀏覽器中支持的矩陣變換可能是在SVG的標準中。之后跟圖形帶點邊的CSS 3以及HTML5的Canvas中也有了矩陣變換,當然強大的Flash以及Flex中也有變換矩陣。他們的基本原理都是一樣的。目前2D的矩陣變換已經有不少瀏覽器支持了,而3D的變換還需時日。
說了半天矩陣變換,其實本質上來說,一個元素渲染后就可以得到一張位圖,然后對這個位圖上每一點進行變換,就可以得到新的一張位圖,從而產生平移、縮放、旋轉,切變以及鏡像反射等效果了。
基本公式
目前不論是SVG也好,CSS 3也好,還是Canvas,2D的矩陣變換都提供了6個參數a b c d e f,其使用基本公式是這樣的:
其中,x和y是元素最開始的坐標,x’ 和y’則是通過矩陣變換后得到新的坐標。
通過中間的那個3×3的變換矩陣,對原先的坐標施加變換,就能得到新的坐標了。
注意!a b c d e f幾個參數的排列方式,是豎著排的,網上有不少文章排列方向有誤。
根據矩陣乘法的運算法則,上面的矩陣式子可以化成下面的兩個式子
x’=ax+cy+e
y’=bx+dy+f
也就是說,別看上面有那么一大坨的東西,本質上就是上面這兩條簡簡單單的公式而已。之后的討論中,我將圍繞著上面的兩行來討論,而不再涉及矩陣的內容了。
平移
如果調用時提供參數matrix(1,0,0,1,tx,ty),即a=d=1,b=c=0,那么上面的式子就簡化成
x’ = 1x+0y+tx = x+tx
y’ = 0x+1y+ty = y+ty
很容易看到,這里就是在原先x,y的基礎上進行平移,變成x+tx,y+ty點而已。非常簡單。如果數學上講,tx和ty就好比是Δx和Δy。
x’ = x+Δx
y’ = y+Δy
CSS 3中的transform: translate(tx, ty);就等價于transform: matrix(1,0,0,1,tx,ty);注意,使用matrix的時候不需要單位,默認是px,而translate需要單位,可以是px、em之類的單位。
縮放和拉伸
如果調用時提供參數matrix(Sx,0,0,Sy,0,0),即a或d不等于1,比如a=Sx,d=Sy而b=c=e=f=0,于是公式簡化成
x’ = Sx*x+0y+0 = Sx*x
y’ = 0x+Sy*y+0 = Sy*y
可以想到,這個操作,實際上是讓x的坐標擴大Sx倍,而y的坐標擴大Sy倍。
這主要是用來讓元素進行縮放效果的。如果Sx和Sy大于1,則是放大,而Sx和Sy小于1,就是縮小了,如果等于1,那就是保持原狀了。并且,由于x方向和y方向是相互獨立的,所以可以一個方向放大,另一個方向縮小。
上面的那個例子中,我設置的m和n都是0.5,于是圖形長寬就各縮小了一半。另外,值得注意的是,他是以元素的中心作為縮放的基點的,而不是左上角。
CSS 3中的transform: scale(Sx, Sy);就等價于transform: matrix(Sx,0,0,Sy,0,0);
旋轉
這里用到的就相對高級一些了,需要使用三角函數的一些知識了
如果調用時提供參數matrix(cosθ,sinθ,-sinθ,cosθ,0,0)
x’ = x*cosθ-y*sinθ+0 = x*cosθ-y*sinθ
y’ = x*sinθ+y*cosθ+0 = x*sinθ+y*cosθ
由于計算機圖形學中,通常向右為x軸正方向,向下為y軸正方向,所以這里的θ表示元素繞坐標原點順時針旋轉的角度。而這里的原點不是元素的左上角,而是元素的中心點。
上面的例子中,我把一個div順時針旋轉了37°,cos37°=0.8,sin37°=0.6,所以提供的矩陣的參數就是 matrix(0.8,0.6,-0.6,0.8,0,0)
CSS 3中,transform:rotate(37deg)就等價于我上面的那個變換了。注意CSS 3中的角度必須帶單位 deg。好處是不用自己算sin和cos值了。
切變
切變,就是把一個元素往某一個方向傾斜一定的角度。傳入的參數應當是matrix(1,tan(θy),tan(θx),1,0,0)
x’ = x+y*tan(θx)+0 = x+y*tan(θx)
y’ = x*tan(θy)+y+0 = x*tan(θy)+y
這里的θx和θy分別代表往x正方向和往y正方向傾斜的角度,兩者是相互獨立的。上面的例子中,我把元素往x方向傾斜了45°,因此他的tan(θx)=1
CSS 3中的transform: skew(θx, θy);就等價于transform: matrix(tan(θx),0,0,tan(θy),0,0); 如果使用skew的話,直接使用角度即可,但必須帶單位deg,比如上面的例子用矩陣的話寫成transform: matrix(1,0,1,1,0,0);等價于 transform: skew(45deg, 0);
鏡像反射
鏡像反射就是指元素對某一條直線做鏡像對稱。最基本的情況是可以對經過原點的某條直線進行反射。定義(ux,uy)為直線方向的單位向量。也就是說,如果直線方程是y=kx,那么ux=1/sqrt(1+k^2),uy=k/sqrt(1+k^2)
那么對這種鏡像反射變化時傳入的參數應當是
matrix(2*ux^2-1,2*ux*uy,2*ux*uy,2*uy^2-1,0,0)
于是最終的方程
x’ = (2*ux^2-1)*x+2*ux*uy*y
y’ = 2*ux*uy*x+(2*uy^2-1)*y
上面的例子中,就是對y=2x這條直線進行的鏡像對稱。CSS 3中目前沒有簡化的規則與之對應。
至于如何對一條不過原點的線對稱,則需要設置原點所在的坐標了。由于默認情況下,原點坐標是這個元素的中心,如果改變了原點的坐標,就可以改變對稱的直線,當然也可以改變前面所有效果的呈現方式,CSS 3中使用transform-origin方法修改坐標原點的位置。
最后,如果你想要玩的更High點,買本計算機圖形學的書來看是不可避免的,這篇文章也僅僅是個皮毛而已。希望對你有所幫助。