1. 程式人生 > >CSS3——CSS3矩陣matrix進行2D變換的數學原理

CSS3——CSS3矩陣matrix進行2D變換的數學原理

css3的2D轉換中matrix接受6個引數,卻可以實現平移、旋轉、放縮、斜切四種效果。它是如何做到的呢? 此處的你有兩個選擇 0 - 對自己有較高要求!老老實實靜下心來將本文看完, 1 - 直接看總結——點我直達,只學習如何使用matrix

預備知識:矩陣相乘和三角函式,fighting! 此處附上矩陣相乘的百度百科定義 在這裡插入圖片描述 我們先將matrix接受的六個引數記為a,b,c,d,e,f,則該變換矩陣記為 在這裡插入圖片描述 二維平面上一個點記為(x,y),為了與向量區分開,我們使用數字1代表點,0代表向量,則二維平面上一個點應記為(x,y,1),為了使該點經過變換後依舊為(x,y,1)的形式,矩陣可以改為 在這裡插入圖片描述 進行相乘變換(自行百度矩陣相乘的公式) 在這裡插入圖片描述

我們得到了一個新的點(ax+cy+e,bx+dy+f,1) 怎麼將這個冷冰冰的點和我們的平移旋轉縮放斜切聯絡到一起呢?

平移

我們知道,對一個點(x,y,1)向x軸正向平移10,向y軸正向平移20,得到的點為(x+10,y+20,1),而經過矩陣變換的點為(ax+cy+e,bx+dy+f,1),對比得到a=1,c=0,e=10,b=0,d=1,f=20 所以變換矩陣為 在這裡插入圖片描述

transform: matrix(1,0,0,1,10,20); // a=1,c=0,e=10,b=0,d=1,f=20

顯然,與平移變換直接相關的引數為e、f引數!! 當我們只進行平移變換時,只需要這樣

transform: matrix(1,0,0,1,e,f); // 相當於 transform: translate(e,f);

放縮

假設我們對一個邊長為10px的正方形放大2倍,如圖所示 在這裡插入圖片描述 對左上角的那個點來說,它由(0,10,1)變為(0,20,1) 對任意一個點(x,y,1)來說,放大後得到的點為(2x,2y,1),而經過矩陣變換的點為(ax+cy+e,bx+dy+f,1),對比得到a=2,c=0,e=0,b=0,d=2,f=0 所以變換矩陣為 在這裡插入圖片描述

transform: matrix(2,0,0,2,0,0); // a=2,c=0,e=0,b=0,d=2,f=0

顯然,與縮放變換直接相關的引數為a、d引數!! 當我們只進行平移變換時,只需要這樣

transform: matrix(a,0,0,d,0,0); // 相當於 transform: scale(a,d)

聰明的你現在肯定猜到了,如果既進行平移,又進行縮放,則transform: matrix(a,0,0,d,e,f);即可,a、d表示縮放,e、f表示平移

旋轉

剩下兩個引數b、c,可是還有旋轉和斜切沒有實現呢!(冒汗 旋轉的話,涉及到旋轉角度的問題啦。記為θ。 複習一波三角函式,以下使用極座標表示 在這裡插入圖片描述 來一波推理 在這裡插入圖片描述 對任意一個點(x,y,1)來說,旋轉θ°後得到的點為(xcosθ-ysinθ,xsinθ+ycosθ,1),而經過矩陣變換的點為(ax+cy+e,bx+dy+f,1),對比得到a=cosθ,b=sinθ,c=-sinθ,d=cosθ,e=0,f=0 所以變換矩陣為 在這裡插入圖片描述 這裡用到了a、b、c、d四個引數,前面我們知道縮放用了a、d兩個引數,如果我們既要旋轉又要縮放怎麼辦呢??

已經證明:任何二維組合變換均可分解為多個基本變換的乘積

故我們只需要分別求出變換矩陣,再相乘即可。

q=DCBAp => q=(DCBA)p=Mp

解釋:p表示當前的點,ABCD表示四種變換,上述式子的變換順序為A->B->C->D

斜切

先沿x軸扭曲 在這裡插入圖片描述 有以下式子 在這裡插入圖片描述 與(ax+cy+e,bx+dy+f,1)對比得出a=1,c=tanθ,e=0,b=0,d=1,f=0 所以變換矩陣為 在這裡插入圖片描述 再看沿y軸扭曲 在這裡插入圖片描述 在這裡插入圖片描述 變換矩陣為 在這裡插入圖片描述 當l種變換同時進行時,有 在這裡插入圖片描述 【思考】我們之前說過,任何二維組合變換均可分解為多個基本變換的乘積,為什麼這裡不是將兩個扭曲矩陣相乘呢?(先沿x軸扭曲,再沿y軸扭曲? emmm,是這樣的,我們這裡的計算都是相對最初始的圖形,如果用兩個矩陣相乘,則是使初始圖形沿x軸扭曲θx度後得到的新圖形再對y軸扭曲θy度!!如果要達到我們想要的效果,就只能重新計算沿y軸的扭曲角度!!

總結】 至此我們已經把2D變換的數學原理挖出來了,寫一個用法總結,便於自己查詢也便於大家檢視

  • 平移,只需要控制最後兩個引數e、f,a、d引數為1
transform: matrix(1,0,0,1,e,f);
// 等同於
transform: translate(e,f);
  • 放縮,只需要控制a、d引數,其它為0
transform: matrix(a,0,0,d,0,0); 
// 等同於
transform: scale(a,d);
  • 旋轉,只需要控制a、b、c、d引數
transform: matrix(cosθ, sinθ, -sinθ, cosθ);
// 等同於
rtransform: rotate(θdeg)
  • 斜切
transform: matrix(1, tanθy, tanθx, 1, 0, 0);
// 等同於
transform: skew(θx, θy);
  • 複合變換 如以下這種型別,先旋轉再縮放再扭曲,如何用矩陣得到呢?
transform: rotate(360deg) scale(2,2) skew(10deg,5deg);

已經證明:任何二維組合變換均可分解為多個基本變換的乘積 故我們先求出旋轉矩陣A,縮放矩陣B和斜切矩陣C 最後得到變換矩陣M = CBA = C(B(A))

【什麼時候我們用矩陣?】 思考下,既然已經有rotate、scale等函式,直接呼叫不就行了嗎??為什麼人類要跟自己過不去,手算矩陣??? 大部分情況下,當然是使用那些基本的rotate、scale函式就好了,下面這種型別的情況呢?

  • 需求說:請把這些點沿著y軸取對稱。 你:簡單 x’ = ax+cy+e = -x,y’ = bx+dy+f = y嘛,一番心算後你得到
    transform: matrix(-1,0,0,1,0,0);
    
  • 需求說:算了,還是對y=x取對稱吧。你:簡單,先整體旋轉逆時針旋轉45°,再對y軸取對稱,再整體順時針旋轉45°(45°)就好了嘛~ 在這裡插入圖片描述 (上圖右側表示逆時針旋轉)
    transform: matrix(0,1,1,0,0,0);
    
    用我們的高中數學來驗證一波,點(a,b)關於y=x軸取對稱,得到的點應該為(b,a) 在這裡插入圖片描述 驗證正確!
  • 需求猶豫了0.5s,哎呀還是對y=kx取對稱吧…
  • 需求搖搖頭說,對y=kx+b取對稱吧…

此處插播高中知識——如何關於一條直線取對稱點呢?

  1. 兩個點關於一條直線對稱,那麼這兩個點的連線的中點必在該直線上
  2. 兩個點的連線所在直線的斜率與該直線斜率的乘積為-1(因為垂直)
  3. 可以列出兩個方程,求出對稱點!
  4. 對稱點都求出來了,矩陣也就出來了~
  • 現在請看官使用rotate、scale、skew、translate實現上述需求~