大學沒學過數學也要理解CSS3 transform 中的matrix
前言
CSS3 中使用transform 可以對元素進行變換。其中包含:位移、旋轉、偏移、縮放。 transform 可以使用translate/rotate/skew/scale 的方式來控制元素變換,也可以使用matrix 的方式來控制元素變換。
比如:
<div class="box"></div>
通過transform屬性進行變換。
首先演示使用translate/rotate/skew/scale 的方式:
.box { width: 100px; height: 100px; background: #00C487; transform: translate(10px, 20px) rotate(30deg) scale(1.5, 2); }
也可以使用matrix 的方式:
.box { width: 100px; height: 100px; background: #00C487; transform: matrix(0.75, 0.8, -0.8, 1.2, 10, 20); }
Matrix 的中文是矩陣,是一個數學術語,在計算機科學中,會用矩陣來對象量進行變換,在CSS3 的transform 屬性中,可以使用矩陣對圖像進行變換。
矩陣長什麼樣子?
矩陣可以分為一個形容詞+一個名字,矩是形容詞,陣是名詞。
如果你喜歡看戰爭片,不管是古代戰爭還是現代戰爭,都需要有陣勢,打仗沒陣型,等於耍流氓;或者是開一局農藥,可能也要考慮各個英雄的站位,各種球類運動、各種棋類都需要有陣型。
陣型中的每一個個體對整體的都會產生影響。比如打王者榮耀射手時候,射手應該猥瑣在一個位置輸出,站錯位置,輸掉整個遊戲。

那,其實矩陣就是一些列的數字按照矩形排列。
在數學中,矩陣用方括號包裹起來。

上圖就是一個矩陣。
CSS3 裡的matrix 如何和矩陣對應呢?
為什麼要用矩陣來表示轉換呢?因為在計算機科學中,矩陣可以對向量進行轉換。矩陣中的每一個數字,對向量的轉換都會產生影響。
CSS3 裡面可以用矩陣表示2D 和3D 轉換,這裡只講2D。
selector { transform: matrix(a, b, c, d, e, f); }

2D 的轉換是由一個3*3 的矩陣表示的,前兩行代表轉換的值,分別是abcdef,要注意是豎著排的,第一行代表x 軸發生的變化,第二行代表y 軸發生的變化,第三行代表z 軸發生的變化,因為這裡是2D 不涉及z 軸,所以這裡是0 0 1。
假設一個問題
創建一個寬高為200px的div,div裡面有一個紅色的點,位置是{x:181px y:50px}
。
倘若將這個div 向右平移10px,x 軸向下平移20px,旋轉37°,x軸縮放1.5 倍,y 軸縮放2 倍:
transform: translate(10px, 20px) rotate(37deg) scale(1.5, 2);

那麼紅色點的變化後的位置在哪裡呢?
既然我們知道矩陣可以對向量進行轉換那麼我們只要把上面的訊息轉換成矩陣訊息,通過矩陣訊息可以將我們的原始坐標轉換到新的坐標。
縮放scale(x, y)
縮放對應的是矩陣中的a 和d,x 軸的縮放比例對應a,y 軸的縮放比例對應d。
transform: scale(x,y);
a=xd=y
所以scale(1.5, 2) 對應的矩陣是:
transform: matrix(1.5, 0, 0, 2, 0, 0);

如果一個沒有元素沒有被縮放,默認a=1 d=1。
平移translate(10, 20)
平移對應的是矩陣中的e 和f,平移的x 和y 分別對應e 和f。
transform: translate(10, 20)
e=10
f=20
對應: transform: matrix(a, b, c, d10, 20);
結合縮放: transform: matrix(1.5 0, 0, 2, 10, 20);
平移只會影響e 和f 值。
旋轉rotate(θdeg)
旋轉影響的是a/b/c/d四個值,分別是什麼呢?
transform: rotate(θdeg)
a=cosθ
b=sinθ
c=-sinθ
d=cosθ
這個是高中學的哦~
如果要計算30° 的sin值:
首先我們要將30° 轉換為弧度,傳遞給三角函數計算。用JS 計算就是下面的樣子了。
// 弧度和角度的转换公式:弧度=π/180×角度const radian = Math.PI / 180 * 30 // 算出弧度const sin = Math.sin(radian) // 计算sinθ const cos = Math.cos(radian) // 计算cosθ console.log(sin, cos) // 输出≈ 0.5, 0.866
這樣我們算出了sin 和cos,分別是0.5 和0.866
如果我們不考慮縮放和偏移,只旋轉30°,矩陣應該表示為
transform: rotate(30deg)
a=0.866
b=0.5
c=-0.5
d=0.866
transform: matrix(0.866, 0.5, -0.5, 0.866, 0, 0);

偏移skew(20deg, 30deg)
上面的題目中沒有出現出現偏移值,偏移值也是由兩個參數組成,x 軸和y 軸,分別對應矩陣中的c 和b。是x 對應c,y 對應b, 這個對應並不是相等,需要對skew 的x 值和y 值進行tan 運算。
transform: skew(20deg, 30deg);
b=tan30°
c=tan20°
注意x 對應的是c,y 對應的是b。
transform: matrix(a, tan(30deg), tan(20deg), d, e, f)
使用JS 來算出tan20 和tan30
// 先创建一个方法,直接返回角度的tan值function tan (deg) { const radian = Math.PI / 180 * deg return Math.tan(radian) } const b = tan(30) const c = tan(20) console.log(b, c) // 输出≈ 0.577, 0.364
b=0.577 c=0.364
transform: matrix(1, 0.577, 0.364, 1, 0, 0)
旋轉+縮放+偏移+位移怎麼辦?
如果我們既要旋轉又要縮放又要偏移,我們需要將旋轉和縮放和偏移和位移多個矩陣相乘,要按照transform裡面rotate/scale/skew/translate所寫的順序相乘。
這裡我們先考慮旋轉和縮放,需要將旋轉的矩陣和縮放的矩陣相乘
實在是用語言解釋不清楚如何去乘,用一張圖解釋吧:
這裡我用小寫字母代表第一個矩陣中的值,大寫字母代表第二個矩陣裡的值

將我們的已經得到的矩陣帶入到公式

得出:
transform: rotate(30) scale(1.5 2);
轉換為matrix 表示為:
transform: matrix(1.299, 0.75, -1, 1.732, 0, 0);
找到這次轉換的矩陣
div 的transform 值如下
transform: translate(10px, 20px) rotate(37deg) scale(1.5, 2);
translate(10px, 20px)
x 平移10px,y 平移20px,所以e=10,f=20。

rotate(37deg)
sin37° ≈ 0.6
cos37° ≈ 0.8
根據a 對應cos b,對應sin,c 對應-sin,d 對應cos 的值
得到:
a=0.8,b=0.6,c=-0.6,d=0.8

scale(1.5, 2)
x 軸縮放1.5,y 軸縮放2,所以a=1.5,d=2

結合
transform: translate(10px, 20px) rotate(37deg) scale(1.5, 2);
我們使用位移矩陣旋轉矩陣縮放矩陣(根據transform中的變換類型書寫的順序)
可以使用矩陣計算器進行計算
從左往右依次計算

所以最終得到矩陣

matrix(1.2, 0.9, -1.2 1.6, 10, 20)
transform: matrix(1.2, 0.9, -1.2 1.6, 10, 20)
和
transform: translate(10px, 20px) rotate(37deg) scale(1.5, 2);
效果是一樣的
如何對一個坐標進行矩陣變換
我們已經知道了這個矩陣,如何通過矩陣對一個坐標進行變化,找到這個坐標變化後的位置呢?
我們用之前得出的變換矩陣去乘以這一個坐標組成的3*1(三排一列)矩陣。

上面已經介紹過如何進行矩陣乘法了,這裡在介紹一遍

上圖中左右兩個矩陣顏色相同的位置相乘後相加,每一行都進行這樣的計算:

得到一個3*1的矩陣,第一行是轉換後的x 值,第二行是轉換後的y 值,第三行是轉換後的z 值(2d不考慮z值)。
前面講到,矩陣的第一行影響x,第二行影響y,也體現在這個地方。
假設我們的坐標是(50, 80),這裡還沒有針對我們提出的問題上面的點進行計算。
我們把坐標寫成矩陣的形式,設置z 軸是1:

然後進行乘法計算:

通過我們計算出來的矩陣變換得到新的位置(46, 172)
繼續剛剛問題
坐標是需要基於一個坐標系存在的,我們需要找到正確的坐標系才能算出準確的坐標。在CSS transform中,有個屬性是transform-origin,來設置變換所基於的點,默認是transform-origin: 50% 50%
,基於中間元素的中心點。我們需要以這個點建立坐標系。
在網頁中,坐標係是x 軸向右,y 軸向下。
轉換前:

轉換後:

根據題目我們知道,這個點相對於綠色div左上角的坐標是(181, 50) 綠色div的寬高為200 基於綠色div中心點建立的坐標系,這個點的坐標是(81, -50)
將坐標代入公式進行計算:

得到坐標約為(167, 13)
再將這個坐標轉換成頁面坐標系(267,113)
最終我們得到了這個點在經過轉換後的坐標
總結
矩陣在計算機圖形學中運用非常多,就像我們經常用的PhotoShop,雖然是設計軟件,但它的圖形也是依賴各種數學能力進行計算後展現的。我們玩的遊戲、看的3D電影,其實都和數學息息相關,學好這些知識,才能真正的成為發明者,即便不成為發明者,在應用層理解這些,讓我們能做的事情更多。
本文錯誤的地方,歡迎斧正。
阿里巴巴手機淘寶招前端,圖形(WebGL)方向以及非圖形方向前端。
郵件聯繫[email protected]