CSS Transforms能夠對一個元素進行二維平面或三維空間的變換,如translate, rotate, scale和skew等變換。css
下面是對W3C官網CSS Transforms模塊的部分摘譯,爲了理解的連貫性,調整了W3C規範中相關章節的順序。css3
用戶瀏覽器(UAs)可能不總能渲染出三維變換,那麼它們就只能支持該規範的一個二維子集。在這種狀況下,三維變換和transform-style、perspective、perspective-origin以及backface-visibility屬性將不被支持。三維相關的變換渲染也不會起做用。git
對於二維變換狀況,矩陣分解採用Graphics Gems II(Jim Arvo著)書中unmatrix算法的二維簡化版本。下面是一個二維的3x3變換矩陣,其中6個參數a~f,分別對應二維變換函數matrix(a, b, c, d, e, f)的6個參數。github
圖1 二維變換3x3矩陣算法
開發者能夠很簡單的爲不支持三維變換的瀏覽器提供備選變換方案。下面的例子中,transform屬性有兩次賦值定義。第一次定義包括了兩個二維變換函數,第二次定義包括一個二維變換和一個三維變換。瀏覽器
div { transform: scale(2) rotate(45deg); transform: scale(2) rotate3d(0, 0, 1, 45deg); }複製代碼
當瀏覽器支持三維變換時,第二次屬性定義將覆蓋第一次的定義,當不支持三維變換時,第二次定義將會失效,瀏覽器會使用第一種定義。函數
當爲元素的transform屬性指定了一個非none屬性值時,就會在該元素上創建了一個本地座標系(local coordinate system)。從元素最初始的渲染(未指定transform屬性值)到本地座標系的映射由該元素的變換矩陣(transformation matrix)給定。變換是可累積的,也就是說,元素是在它們祖先的座標系中創建本身的本地座標系。從用戶的角度來看,就是一個元素會累積應用全部它祖先‘transform’屬性設置的變換,以及自身"transform"屬性設置的變換,規範中將這些變換的累積稱爲元素當前變換矩陣(current transformation matrix, CTM)。ui
座標空間是一個有兩個軸的座標系:X軸是水平向右增長,Y軸是垂直向下增長。三維變換函數將這個座標空間擴展到三維,增長了垂直於屏幕平面一個Z軸,而且指向觀察者。spa
圖2 初始座標空間示例
變換矩陣是基於'transform'和'transform-origin'屬性,按照如下步驟計算而來:
注意,變換隻是影響了元素的顯示,不影響元素自身CSS的佈局。意味着變化不會影響getClientRects()及getBoundingClientRect()的值。對於基於CSS盒模型佈局定位的元素,若是該元素的transform屬性爲非none,則該元素將成爲其全部fixed定位的後代元素的包含塊(Containing Block)。
transform中一些變換函數的效果能夠經過更具體的變換函數來實現,好比translate的一些操做能夠用translateX來實現。此時稱translate爲元變換,translateX爲派生變換。
下面列出了全部二維和三維的元變換以及相應的派生變換。
元變換 | 派生變換 |
---|---|
translate() | translateX(), translateY(), translate() |
rotate()帶三個參數 | rotate()帶一個或三個參數 |
scale() | scaleX(), scaleY(),scale() |
元變換 | 派生變換 |
---|---|
translate3d() | translateX(), translateY(), translateZ(), translate() |
scale3d() | scaleX(), scaleY(), scaleZ(), scale() |
rotate3d() | rotate(), roateX(), rotateY(), rotateZ() |
對於同時具備二維和三維元變換的派生變換,具體是使用二維元變換或三維的元變換,是由上下文環境來決定。
兩個具備相同數量參數的變換函數,會直接進行數值的插值計算,而不須要轉換爲相同的元變換。插值計算的結果便是帶有相同參數的相同變換。對於rotate3d(), matrix(), matrix3d(), perspective()有特殊的插值計算規則。
例如,對於變換函數translate(0)和translate(100px),就是兩個具備相同數量參數的相同變換,因此它們會直接進行數值上的插值計算。可是對於變換函數translateX(100px)和translate(100px, 0),兩個變換既不是相同的變換,使用的參數數量也不一樣,因此它們就須要先轉換爲元變換函數,而後才能進行數值上的插值計算。
兩個不一樣的變換,但都是從相同的元變換派生出來的(即相同的派生變換),或者相同的變換,但使用了不一樣數量的參數,此時兩個變換能夠進行數值插值計算。須要先將兩種變換轉換爲相同的元變換,而後才能進行數值插值計算。插值計算的結果至關於使用了相同數量參數的相同元變換。
下面的例子,當div發生鼠標hover事件時,會發生從translateX(100px)到translateY(100px)的3秒過渡變換。此時兩個變換都是從相同的元變換translate()派生的,因此須要先將兩個變換轉換爲translate()元變換,而後才能進行數值插值計算。
div { transform: translateX(100px); } div:hover { transform: translateY(100px); transition: transform 3s; }複製代碼
當發生3秒的transition時,translateX(100px)將會轉化爲translate(100px, 0),translateY(100px)會轉化爲translate(0, 100px),而後兩個變換才能進行數值的插值計算。
若是兩個變換均可以從同一個二維元變換派生,則都會轉換爲二維元變換。若是其中一個是或者都是三維變換,則會都轉換爲三維元變換。
下面的例子中,一個二維變換函數通過3秒過渡變換到三維變換函數。兩個變換函數的公共元變換爲translate3d()。
div { transform: translateX(100px); } div:hover { transform: translateZ(100px); transition: transform 3s; }複製代碼
當發生3s的transition時,translateX(100px)會轉化爲translate3d(100px, 0, 0),translateZ(100px)會轉化爲translate3d(0, 0, 100px),而後兩個變換才能進行數值的插值計算。
對於matrix(), matrix3d(), perspective()三種變換將會首先被轉化爲4x4的矩陣,而後進行矩陣的插值計算。
對於rotate3d()的插值計算,首先會獲得兩個變換的單位方向向量,若是相等,則能夠直接進行數值的插值計算;不然,就須要先將兩種變換轉化爲4x4的矩陣,而後對矩陣進行插值計算。
當變換函數之間發生過渡時(好比對transforms施加transition屬性),就須要對變換函數進行插值計算。從一個初始的變換(from-transform)到一個結束的變換(to-transform),如何進行插值計算須要遵循下面的規則。
此時沒有必要進行計算,保持原值。
值爲none的那個將被替換爲恆等變化(Identity Transform functions),而後繼續按照下面的規則進行插值計算。
恆等變換(Identity Transform functions) 就是標準裏給出的一系列特殊的變換函數,相似線性代數裏面的單位矩陣(Identity Matrix),不管怎麼施加多少次變換,都不會改變原有的變換,標準裏面給出的恆等變換有translate(0)、translate3d(0, 0, 0)、translateX(0)、translateY(0)、translateZ(0)、scale(1)、scaleX(1)、scaleY(1)、scaleZ(1)、rotate(0)、rotate3d(1, 1, 1, 0)、rotateX(0)、rotateY(0)、rotateZ(0)、skew(0, 0)、skewX(0)、skewY(0)、matrix(1, 0, 0, 1, 0, 0)和matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)。一種特殊的狀況是透視(perspective): perspective(infinity),此時M34的值變得無窮小,所以假定它等於單位矩陣。
例如,from-transform爲scale(2),to-transform爲none, 則none將會被替換爲scale(1),而後繼續按照下面的規則進行插值。相似的,若是from-transform爲none,to-transform爲scale(2) rotate(50deg),則from-transform會被替換爲scale(1) rotate(0)。
按照元變換函數和派生變換函數的插值裏面的步驟,對from-transform和to-transform對應的變換進行插值。計算出的結果做爲最終的變換。
例如,from-transform爲scale(1) translate(0),to-transform爲translate(100px) scale(2),雖然都是用了scale和translate變換,可是對應的變換既不相同也不是從相同的元變換派生出來的,此時就不能按照本條規則來進行插值計算。
from-transform和to-transform中使用的變換都會被轉化爲4x4的矩陣,並進行相應的矩陣插值計算。若是from-transform和to-transform對應的變換矩陣均可以表示成3x2矩陣或者matrix3d矩陣,則元素就按照相應插值計算的矩陣進行變換渲染。在某些特殊的狀況下,變化矩陣多是一個奇異或不可逆矩陣,則元素將不會被渲染。
當對兩個矩陣進行插值時,首先將矩陣分解爲一系列變換操做的值,好比對應的translation、rotation、scale和skew的變換矩陣,而後對各個變換操做對應的矩陣進行數值插值計算,最後將各個變換矩陣從新組合爲原始矩陣。
下面的例子中,元素初始變換爲rotate(45deg),當發生hover時,將會在X軸和Y軸移動100像素,並旋轉1170度。若是設計人員給出下面的寫法,極可能是指望看到元素會順時針旋轉3.5圈(1170度)。
<style> div { transform: rotate(45deg); } div:hover { transform: translate(100px, 100px) rotate(1215deg); transition: transform 3s; } </style> <div></div>複製代碼
初始變換‘rotate(45deg)’和目標變換'translate(100px, 100px) rotate(1125deg)'徹底不一樣,按照*變換的插值 最後一條規則,兩個變換都須要進行矩陣的插值計算。但須要注意,將變換轉化爲矩陣的過程當中,旋轉3圈的信息將會丟失掉,因此最終看到的效果只順時針旋轉了半圈(90度)。
爲了達到指望的效果,只須要更新上述變換的寫法,使先後兩個變換知足變換的插值* 的第三條規則。初始變換改成‘translate(0, 0) rotate(45deg)’**,此時就會進行數值的插值計算,從而不會丟失旋轉信息,達到指望的效果。
具體的decomposing和recomposing矩陣的算法,見Graphics Gems II。
譯 by Hong