大學沒學過數學也要理解 CSS3 transform 中的 matrix
來自專欄 挨踢路透社大學沒學過數學也要理解 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);}
查看demo
Matrix 的中文是矩陣,是一個數學術語,在計算機科學中,會用矩陣來對象量進行變換,在 CSS3 的 transform 屬性中,可以使用矩陣對圖像進行變換。
矩陣長什麼樣子?
矩陣可以分為一個形容詞+一個名字,矩是形容詞,陣是名詞。
如果你喜歡看戰爭片,不管是古代戰爭還是現代戰爭,都需要有陣勢,打仗沒陣型,等於耍流氓;或者是開一局農藥,可能也要考慮各個英雄的站位,各種球類運動、各種棋類都需要有陣型。
陣型中的每一個個體對整體的都會產生影響。比如打王者榮耀射手時候,射手應該猥瑣在一個位置輸出,站錯位置,輸掉整個遊戲。
那,其實矩陣就是一些列的數字按照矩形排列。
在數學中,矩陣用方括弧包裹起來。
上圖就是一個矩陣。
CSS3 里的 matrix 如何和矩陣對應呢?
為什麼要用矩陣來表示轉換呢?因為在計算機科學中,矩陣可以對向量進行轉換。矩陣中的每一個數字,對向量的轉換都會產生影響。
CSS3 裡面可以用矩陣表示 2D 和 3D 轉換,這裡只講 2D。
selector { transform: matrix(a, b, c, d, e, f);}
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 計算就是下面的樣子了。
// 弧度和角度的轉換公式:弧度=π/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°
注意 y 對應的是 c,x 對應的是 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)方向以及非圖形方向前端。
郵件聯繫 mingfei.fmf@alibaba-inc.com
推薦閱讀:
※矩陣和向量求導
※gal2mat:將gal權重文件轉成n-by-n矩陣
※矩陣論筆記(2)
※語料庫語言學基礎知識:矩陣(Haskell版)