沒學過線代也能讀懂的CSS3 matrix

發表於2018-05-29

前言

CSS3 中使用 transform 可以對元素進行變換。其中包含:位移、旋轉、偏移、縮放。 transform 可以使用 translate/rotate/skew/scale 的方式來控制元素變換,也可以使用 matrix 的方式來控制元素變換。

比如:

通過transform屬性進行變換。

首先演示使用 translate/rotate/skew/scale 的方式:

也可以使用 matrix 的方式:

檢視demo

Matrix 的中文是矩陣,是一個數學術語,在電腦科學中,會用矩陣來物件量進行變換,在 CSS3 的 transform 屬性中,可以使用矩陣對影象進行變換。

矩陣長什麼樣子?

矩陣可以分為一個形容詞+一個名字,矩是形容詞,陣是名詞。

如果你喜歡看戰爭片,不管是古代戰爭還是現代戰爭,都需要有陣勢,打仗沒陣型,等於耍流氓;或者是開一局農藥,可能也要考慮各個英雄的站位,各種球類運動、各種棋類都需要有陣型。

陣型中的每一個個體對整體的都會產生影響。比如打王者榮耀射手時候,射手應該猥瑣在一個位置輸出,站錯位置,輸掉整個遊戲。

那,其實矩陣就是一些列的數字按照矩形排列。

在數學中,矩陣用方括號包裹起來。

上圖就是一個矩陣。

CSS3 裡的 matrix 如何和矩陣對應呢?

為什麼要用矩陣來表示轉換呢?因為在電腦科學中,矩陣可以對向量進行轉換。矩陣中的每一個數字,對向量的轉換都會產生影響。

CSS3 裡面可以用矩陣表示 2D 和 3D 轉換,這裡只講 2D。

2D 的轉換是由一個 3*3 的矩陣表示的,前兩行代表轉換的值,分別是 a b c d e f,要注意是豎著排的,第一行代表 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=x d=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 計算就是下面的樣子了。

這樣我們算出了 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°

注意 y 對應的是 c,x 對應的是 b。

transform: matrix(a, tan(30deg), tan(20deg), d, e, f)

使用 JS 來算出 tan20 和 tan30

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電影,其實都和數學息息相關,學好這些知識,才能真正的成為發明者,即便不成為發明者,在應用層理解這些,讓我們能做的事情更多。

本文錯誤的地方,歡迎斧正。

相關文章