前端中的變換矩陣

在一票教你如何製做立方體的教程以後,在張鑫旭生動的類比講解12以後,我還想重提一次前端中的變換矩陣——它被挖掘得遠遠不夠。
本文裏,讓咱們從W3C標準和瀏覽器等新角度來從新理解變換矩陣。javascript

變換矩陣的綜合應用

在開始前,咱們不妨看動畫庫bouncejs來熱身一下。css

一個動畫若是要給人帶來愉悅、動人甚至是驚豔的感受,它首先要足夠貼近咱們的經驗,不然咱們理解不了動畫過程;其次,它還要有充沛的細節,不然會顯得單調乏味。
bouncejs這個庫就同時作到了這兩點。html

圖片描述

咱們知道CSS3中的時間函數實際上是很是殘缺的,它最複雜也不過是生成一個有四個參數的三次貝塞爾曲線,還遠遠不夠咱們對於動畫細膩程度的追求。若是細窺bouncejs的實現,咱們會發現它用到了線性的時間函數,而在keyframes中表達動畫細節,用到了一大堆matrix3d:前端

@keyframes animation{
    /* ... */
    21.32% { transform: matrix3d(2.196, 0, 0, 0, 0, 2.069, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }
    24.32% { transform: matrix3d(2.151, 0, 0, 0, 0, 1.96, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }
    /* ... */
}

如何實現:咱們想要的動畫中的回彈、硬直等效果如何拆解爲這些matrix3d的呢?
背後機制:這些matrix3d是如何組合成咱們想要的動畫效果的呢?java

變換矩陣的用法

先來看看變換矩陣在各處的表現形式吧。css3

CSS中的變換矩陣

也許是咱們第一次接觸變換矩陣的地方。git

.selector {
    transform: matrix(a, b, c, d, e, f);
    transform: matrix3d(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44);
}

SVG中的變換矩陣

SVG做爲可縮放矢量圖形,它侷限於2D座標中,所以它只有二維變換:github

<g transform="matrix(a, b, c, d, e, f)"></g>

注意,因爲SVG沒有transform-origin屬性,所以須要本身用translate來模擬。
可參考:web

DEMO #1
SVG相對中心翻轉和相對中心旋轉的實現
實現方式:三個transform的嵌套。
圖片描述canvas

CANVAS中的變換矩陣

首先是2d Context中的變換矩陣。

var context = canvas.getContext("2d");
context.transform() /*與以前的矩陣值累乘*/
context.setTransform() /*不與以前的矩陣值累乘*/

它跟SVGMatrix接口同樣,所以也不支持transform-origin,須要用translate模擬。
可參考:

DEMO #2
Canvas相對中心翻轉和相對中心旋轉的實現
實現方式:三個transform的嵌套。
圖片描述

IE中的filter變換矩陣

一個遺留的接口,不作多介紹。
可見:張鑫旭:IE矩陣濾鏡Matrix旋轉與縮放及結合transform的拓展

JS中的變換矩陣

DOM接口

咱們能夠用getComputedStyle來獲取到相應的transform參數,得到的值是一個字符串。

window.getComputedStyle(dom).transform
window.getComputedStyle(dom).webkitTransform

矩陣包裝器

webkit瀏覽器和IE曾經支持私有的包裝器(WebkitCSSMatrixMSCSSMatrix),能夠在獲得字符串之後協助咱們作一些矩陣運算,但目前已被瀏覽器廢棄。
咱們能夠本身實現一個相似的包裝器,可參考github: CSSMatrix
此外,在THREE.JS中也存在包裝器THREE.Matrix4

數學中的變換矩陣

變換矩陣的實質爲一組線性變換的係數矩陣。變換的目標爲座標。
係數矩陣的應用方式,在於其用於左乘點向量的齊次座標。
當算出的齊次座標值不爲1的時候,須要完成齊次除法 homogeneous divide,使得第四個值爲1,以算出最終的座標值。

計算過程以下圖:

圖片描述

最後,一個變換矩陣不只僅是描述座標點變換,其實也描述了座標系變換(基變換)。

變換矩陣的乘法

須要注意的是,變換矩陣的乘法是不符合乘法交換律的。可參考:

DEMO #3
從新排序變換矩陣/函數
測試方法:改變上方或下方的transform中的rotateY和rotateX的順序,比較他們的矩陣和顯示效果
圖片描述
可見,交換律是不符合的。

一些特殊狀況下,矩陣A乘以矩陣B正好等於矩陣B乘以矩陣A。

常規的例子是單位矩陣E左乘或右乘A,都將獲得A。
一個很簡單的理解:單位矩陣其實表明了x'=xy'=y...的方程組的係數矩陣。

變換矩陣的侷限性

再怎麼變換,變換矩陣都是一個線性變換,沒法將直線變成曲線。因此魚眼之類的效果不能簡單的用變換矩陣來完成。

標準中的變換矩陣

影響變換矩陣的屬性

變換中心點:transform-origin

上文已有DEMO實例。變換中心點功能是由變換矩陣左乘座標位移矩陣P,和右乘座標位移矩陣的逆矩陣P-1,來影響結果的:

圖片描述

變換透視:perspectiveperspective-origin

從CSSTricks裏面借一張圖:

圖片描述

上圖中眼睛位置相關的三個座標能夠藉由這些屬性調整:

  • p:perspective

  • 眼睛的x和y:perspective-origin

父子座標系共享:transform-style

圖片描述

變換:transform

2d

  • translate

  • scale

  • rotate

  • skew

  • matrix

3d

  • translate3d

  • scale3d

  • rotate3d

  • perspective

  • matrix3d

如何算得最終變換矩陣

  1. 計算變換矩陣

    1. 從單位矩陣開始

    2. 乘以transform-origin的座標系變換矩陣

    3. 按照聲明順序,乘以每個transform function其對應矩陣

    4. 乘以transform-origin的逆座標系變換矩陣

  2. 計算累計變換矩陣

    1. 對於該元素和3D渲染上下文根元素的每個包含塊,從單位矩陣開始

    2. 乘以其包含塊上的perspective矩陣

    3. 乘以當前塊相對其包含塊的水平、垂直位移矩陣

  3. 將累積變換矩陣乘以變換矩陣,獲得最終變換矩陣

變換矩陣的動畫

日常動畫過程當中,有兩個概念:

  1. Timing function 時間函數:由時間函數f獲得插值比例。
    t=f(tNow, tTotal)
    tNow:通過時間
    tTotal:總時間

  2. Interpolation:插值函數:由插值函數g獲得最終的值。(多半是線性插值)
    s=g(t, xStart, xEnd)
    t:插值比例
    xStart:開始值
    xEnd:結束值

對於變換矩陣,會採用線性的插值方式嗎?請參考:

DEMO #4
矩陣的插值實驗
實驗方式:左邊爲用js實現的線性插值,右邊爲用animation實現的方法,起止皆爲matrix。
測試方法:點擊「combine」,而後再點擊「play」。
圖片描述
能夠由輪廓的不重合之處發現,線性的插值是不許確的插值方式。

根據標準,矩陣插值的方式是這樣的

  1. Decomposing:將矩陣分解爲多個子變換,並求得對應的向量。

    1. perspective

    2. translate

    3. quaternion(四元數)

    4. skew

    5. scale

  2. 分別對各個子變換進行插值計算

    1. 四元數向量的插值方法爲球面線性插值spherical linear interpolation

    2. 其它的子變換都經過簡單的線性插值計算

  3. Recomposing:插值結束之後,將各個子矩陣按順序相乘,獲得最終的矩陣。

    1. 單位矩陣

    2. 乘以perspective matrix

    3. 乘以translation matrix

    4. 乘以rotation matrix

    5. 乘以skew matrix

    6. 乘以scale matrix

瀏覽器中的變換矩陣

benchmark: transform matrix和transform function哪一個更快

to be continued

Chrome中的變換矩陣

瞭解了數學中的相關概念,咱們便可參考源碼中變換矩陣的實現方式了。

SK_API::SkMatrix44

包含了對矩陣自己的定義、矩陣相關的數據類型和矩陣的基礎計算。
這個類是矩陣相關的最基礎的4*4的矩陣數據結構。

源碼:

包含:

  1. 一個SkMScalar類,根據編譯選項可用於表示float或double;

  2. set*()爲設置當前矩陣爲某變換對應的矩陣,而pre*()post*()則爲在某項變換以後或以前的變換操做,也能夠理解爲某個變換矩陣的左乘或右乘;

  3. determinant()爲求矩陣的行列式;

  4. invert()求逆矩陣;

  5. transpose()轉置矩陣;

  6. computeTypeMask()方法展現瞭如何經過計算獲得一個變換矩陣是否包含以下變換:

    • 位移:translate

    • 伸縮:scale

    • 仿射:affine,包含旋轉 rotate 和斜切 skew

    • 透視:perspective

gfx::transform

這個類映射到CSS中的transform聲明。

源碼:

包含:

  1. 2d、3d的構造函數、拷貝構造函數、一些運算符重載

  2. 針對CSS中的各種聲明方式,獲得對應的變換。包括但不限於:
    Translate();
    Translate3d();
    Scale();
    Scale3d();
    RotateAboutXAxis();
    RotateAbout();

  3. 應用透視 perspective
    ApplyPerspectiveDepth()

  4. 左乘和右乘
    PreconcatTransform();
    ConcatTransform();

  5. 對矩陣性質的判斷
    IsScale2d()
    IsApproximatelyIdentityOrTranslation()

  6. 其餘矩陣操做,如轉置和獲得逆變換
    GetInverse()
    Transpose();

  7. 矩陣插值操做
    Blend()

gfx::transform_util

包含變換相關的一些數學計算功能、除了矩陣之外的數據類型定義。

源碼:

包含:

  1. 點類Point和矩形類Rect

  2. DecomposedTransform矩陣插值過程當中的類,包含各個變換相關的特徵值向量:

    • 位移向量:translate[3]

    • 縮放向量:scale[3]

    • 斜切向量:skew[3]

    • 透視向量:perspective[4]

    • 四元數向量:quaternion[4],用於旋轉。不用常規的三變量表示方法是爲了不歐拉鎖問題。

  3. 一些輔助方法

    1. 由矩陣拆解爲DecomposedTransform的方法DecomposeTransform()

    2. 方法BlendDecomposedTransforms(),由DecomposeTransform參與的插值方法

    3. 方法Slerp(),用於四元數的球面插值方法

    4. 獲得三元向量的長度Length3()

    5. 向量點乘Dot()

    6. 將矩陣歸一化Normalize()

    7. 應用變換中心的方法TransformAboutPivot

相關文章
相關標籤/搜索