在一票教你如何製做立方體的教程以後,在張鑫旭生動的類比講解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
也許是咱們第一次接觸變換矩陣的地方。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做爲可縮放矢量圖形,它侷限於2D座標中,所以它只有二維變換:github
<g transform="matrix(a, b, c, d, e, f)"></g>
注意,因爲SVG沒有transform-origin屬性,所以須要本身用translate來模擬。
可參考:web
DEMO #1
SVG相對中心翻轉和相對中心旋轉的實現
實現方式:三個transform的嵌套。canvas
首先是2d Context中的變換矩陣。
var context = canvas.getContext("2d"); context.transform() /*與以前的矩陣值累乘*/ context.setTransform() /*不與以前的矩陣值累乘*/
它跟SVGMatrix接口同樣,所以也不支持transform-origin,須要用translate模擬。
可參考:
DEMO #2
Canvas相對中心翻轉和相對中心旋轉的實現
實現方式:三個transform的嵌套。
一個遺留的接口,不作多介紹。
可見:張鑫旭:IE矩陣濾鏡Matrix旋轉與縮放及結合transform的拓展。
咱們能夠用getComputedStyle
來獲取到相應的transform參數,得到的值是一個字符串。
window.getComputedStyle(dom).transform window.getComputedStyle(dom).webkitTransform
webkit瀏覽器和IE曾經支持私有的包裝器(WebkitCSSMatrix
和MSCSSMatrix
),能夠在獲得字符串之後協助咱們作一些矩陣運算,但目前已被瀏覽器廢棄。
咱們能夠本身實現一個相似的包裝器,可參考github: CSSMatrix。
此外,在THREE.JS中也存在包裝器THREE.Matrix4
。
變換矩陣的實質爲一組線性變換的係數矩陣。變換的目標爲座標。
係數矩陣的應用方式,在於其用於左乘點向量的齊次座標。
當算出的齊次座標值不爲1的時候,須要完成齊次除法 homogeneous divide,使得第四個值爲1,以算出最終的座標值。
計算過程以下圖:
最後,一個變換矩陣不只僅是描述座標點變換,其實也描述了座標系變換(基變換)。
須要注意的是,變換矩陣的乘法是不符合乘法交換律的。可參考:
DEMO #3
從新排序變換矩陣/函數。
測試方法:改變上方或下方的transform中的rotateY和rotateX的順序,比較他們的矩陣和顯示效果
可見,交換律是不符合的。
一些特殊狀況下,矩陣A乘以矩陣B正好等於矩陣B乘以矩陣A。
常規的例子是單位矩陣E左乘或右乘A,都將獲得A。
一個很簡單的理解:單位矩陣其實表明了x'=x
、y'=y
...的方程組的係數矩陣。
再怎麼變換,變換矩陣都是一個線性變換,沒法將直線變成曲線。因此魚眼之類的效果不能簡單的用變換矩陣來完成。
transform-origin
上文已有DEMO實例。變換中心點功能是由變換矩陣左乘座標位移矩陣P,和右乘座標位移矩陣的逆矩陣P-1,來影響結果的:
perspective
、perspective-origin
從CSSTricks裏面借一張圖:
上圖中眼睛位置相關的三個座標能夠藉由這些屬性調整:
p:perspective
眼睛的x和y:perspective-origin
transform-style
transform
translate
scale
rotate
skew
matrix
translate3d
scale3d
rotate3d
perspective
matrix3d
計算變換矩陣
從單位矩陣開始
乘以transform-origin的座標系變換矩陣
按照聲明順序,乘以每個transform function其對應矩陣
乘以transform-origin的逆座標系變換矩陣
計算累計變換矩陣
對於該元素和3D渲染上下文根元素的每個包含塊,從單位矩陣開始
乘以其包含塊上的perspective矩陣
乘以當前塊相對其包含塊的水平、垂直位移矩陣
將累積變換矩陣乘以變換矩陣,獲得最終變換矩陣
日常動畫過程當中,有兩個概念:
Timing function 時間函數:由時間函數f獲得插值比例。t=f(tNow, tTotal)
tNow:通過時間
tTotal:總時間
Interpolation:插值函數:由插值函數g獲得最終的值。(多半是線性插值)s=g(t, xStart, xEnd)
t:插值比例
xStart:開始值
xEnd:結束值
對於變換矩陣,會採用線性的插值方式嗎?請參考:
DEMO #4
矩陣的插值實驗
實驗方式:左邊爲用js實現的線性插值,右邊爲用animation實現的方法,起止皆爲matrix。
測試方法:點擊「combine」,而後再點擊「play」。
能夠由輪廓的不重合之處發現,線性的插值是不許確的插值方式。
根據標準,矩陣插值的方式是這樣的:
Decomposing:將矩陣分解爲多個子變換,並求得對應的向量。
perspective
translate
quaternion(四元數)
skew
scale
分別對各個子變換進行插值計算
四元數向量的插值方法爲球面線性插值spherical linear interpolation
其它的子變換都經過簡單的線性插值計算
Recomposing:插值結束之後,將各個子矩陣按順序相乘,獲得最終的矩陣。
單位矩陣
乘以perspective matrix
乘以translation matrix
乘以rotation matrix
乘以skew matrix
乘以scale matrix
to be continued
瞭解了數學中的相關概念,咱們便可參考源碼中變換矩陣的實現方式了。
包含了對矩陣自己的定義、矩陣相關的數據類型和矩陣的基礎計算。
這個類是矩陣相關的最基礎的4*4的矩陣數據結構。
源碼:
包含:
一個SkMScalar
類,根據編譯選項可用於表示float或double;
set*()
爲設置當前矩陣爲某變換對應的矩陣,而pre*()
和post*()
則爲在某項變換以後或以前的變換操做,也能夠理解爲某個變換矩陣的左乘或右乘;
determinant()
爲求矩陣的行列式;
invert()
求逆矩陣;
transpose()
轉置矩陣;
computeTypeMask()
方法展現瞭如何經過計算獲得一個變換矩陣是否包含以下變換:
位移:translate
伸縮:scale
仿射:affine,包含旋轉 rotate 和斜切 skew
透視:perspective
這個類映射到CSS中的transform聲明。
源碼:
包含:
2d、3d的構造函數、拷貝構造函數、一些運算符重載
針對CSS中的各種聲明方式,獲得對應的變換。包括但不限於:Translate();
Translate3d();
Scale();
Scale3d();
RotateAboutXAxis();
RotateAbout();
應用透視 perspectiveApplyPerspectiveDepth()
左乘和右乘PreconcatTransform();
ConcatTransform();
對矩陣性質的判斷IsScale2d()
IsApproximatelyIdentityOrTranslation()
其餘矩陣操做,如轉置和獲得逆變換GetInverse()
Transpose();
矩陣插值操做Blend()
包含變換相關的一些數學計算功能、除了矩陣之外的數據類型定義。
源碼:
包含:
點類Point
和矩形類Rect
DecomposedTransform
矩陣插值過程當中的類,包含各個變換相關的特徵值向量:
位移向量:translate[3]
縮放向量:scale[3]
斜切向量:skew[3]
透視向量:perspective[4]
四元數向量:quaternion[4]
,用於旋轉。不用常規的三變量表示方法是爲了不歐拉鎖問題。
一些輔助方法
由矩陣拆解爲DecomposedTransform
的方法DecomposeTransform()
方法BlendDecomposedTransforms()
,由DecomposeTransform
參與的插值方法
方法Slerp()
,用於四元數的球面插值方法
獲得三元向量的長度Length3()
向量點乘Dot()
將矩陣歸一化Normalize()
應用變換中心的方法TransformAboutPivot