以下內容根據官方規范翻譯,沒有翻譯關於SVG變換的內容和關於矩陣計算的內容。
一般情況下,元素在一個無景深無立體感的平面(flat plane)上渲染,這個平面就是其包含塊所處的平面。同時,頁面上的其他元素也共享這個平面。2D變換函數雖然能改變元素的表現,但是這個被改變的元素仍然是在其包含塊所處的平面內被渲染。
3D變換會產生一個變換矩陣,該變換矩陣在Z軸上的分量不為0。結果是把元素渲染到一個不同於其包含塊所處的平面內。這將影響到通常情況下的“后來居上”的渲染規則:變換元素可能會和其相鄰的其他元素相互交叉。
例子
這個例子演示了3D變換對一個元素的影響。
1 <style> 2 div { 3 height: 150px; 4 width: 150px; 5 } 6 .container { 7 border: 1px solid black; 8 } 9 .transformed { 10 transform: rotateY(50deg); 11 } 12 </style> 13 14 <div class="container"> 15 <div class="transformed"></div> 16 </div>
例子中藍色div進行了一個繞着Y軸旋轉50deg的旋轉變換,從結果來看,藍色div僅僅是變窄了,並沒有3D效果,因為變換還是在無景深無立體感的2D平面上進行的,還需接着往下看。
Perspective 透視
perspective和perspective-origin屬性能給舞台(scene,變換元素所處的空間)添加縱深感,結果就是元素距離觀看者越近就表現得越大,越遠就表現得越小(通過變換可以改變元素在Z軸上的位置)。
perspective屬性指定觀看者的眼睛(假設的)與屏幕 (drawing plane)之間的距離。如果將perspective屬性的值設為d,則元素的縮放比例就等於d/(d − z),z是元素在Z軸上的位置,更准確的說是變換前元素所在的與Z軸垂直的平面在Z軸上的坐標位置。
圖中演示了元素如何根據perspective屬性和z position進行縮放。
圖的上半部分,z是d的一半。從假設的眼睛的位置看,為了讓drawing surface上的黑色實線圓看起來好像在圖中虛線圓的位置,黑色實線圓被放大了2倍,結果就是drawing surface上的藍色圓。圖的下半部分,黑色實線圓被縮小為原來的1/3,讓它看起來好像在drawing surface后面的虛線位置。
默認情況下,觀看者的眼睛正對着的位置在drawing(surface)的中心。然而可以通過perspective-origin屬性改變這個位置(for example, if a web page contains multiple drawings that should share a common perspective property,這句英語是理解perspective與perspective()區別的關鍵)。
圖中演示了perspective origin上移后對元素的影響。
透視變換矩陣(perspective matrix)根據下列規則計算:
- 以單位矩陣(identity matrix)開始
- 計算perspective-origin的X值和Y值並按計算值進行平移(translate)
- 乘以變換函數perspective()所用的矩陣,其中長度值由perspective屬性提供。矩陣如圖所示:
- 用perspective-origin的X值和Y值的相反數進行平移(translate)
步驟3用到的矩陣:
例子
這個例子演示了應用perspective屬性后可以讓3D變換看起來更真實。
1 <style> 2 div { 3 height: 150px; 4 width: 150px; 5 } 6 .container { 7 perspective: 500px; 8 border: 1px solid black; 9 } 10 .transformed { 11 transform: rotateY(50deg); 12 } 13 </style> 14 15 <div class="container"> 16 <div class="transformed"></div> 17 </div>
藍色div與前面例子中的藍色div進行了相同的3D變換,但是這個例子中的藍色div的渲染受到其父元素的perspective屬性值的影響。考慮到景深,perspective屬性使在Z軸上有正坐標值的點在X軸和Y軸上放大了(距離觀看者更近了),在Z軸上有負坐標的點在X軸和Y軸上縮小了,距離觀看者更遠了。
補充:
perspective屬性
可取值:none | <length>
該屬性能應用於可變換的元素(transformable elements)。
<length>只能為正值,是觀看者與z=0平面的距離,使具有3D變換的元素產生透視效果(當值為0或負值時,無透視效果,變換元素表現為扁平化)。
值為none時,無透視效果,元素在畫布上扁平化呈現。
perspective屬性值不為none的元素,創建一個層疊上下文和一個包含塊(規范上說和相對定位有點類似,和transform很像)。
perspective和perspective-origin屬性的值被用於計算透視矩陣(perspective matrix)。
perspective-origin屬性
perspective-origin用於創建perspective屬性的起始點。事實上,該屬性設置了觀看者的眼睛在舞台元素上的投影位置。
可取值:<percentage> | <length> | 關鍵字
默認值為:50% 50%
第一個值表示與border box左邊界的距離,第二個值表示與border box上邊界的距離。當只指定一個值時,第二個值作為50%處理。
<percentage>相對於舞台元素(reference box)的border box的尺寸計算。
該屬性的語法:
1 perspective-origin: x-position; /* one-value syntax */ 2 3 perspective-origin: x-position y-position; /* two-value syntax */ 4 5 6 /*當 x-position 和 y-position 為關鍵字時,以下寫法是允許的:*/ 7 8 perspective-origin: y-position x-position;
x-position
- <percentage> 百分比,相對於元素寬度,可為負值。
- <length> 長度值,可為負值。
- left,關鍵字,0值的簡記。
- center,關鍵字,50%的簡記。
- right,關鍵字,100%的簡記。
y-position
- <percentage> 百分比,相對於元素的高度,可為負值。
- <length> 長度值,可為負值。
- top,關鍵字,0值得簡記。
- center,關鍵字,50%的簡記。
- bottom,關鍵字,100%的簡記。
3D rendering context 3D渲染上下文
這部分內容主要講3D變換的渲染模型和transform-style屬性。
一個3D渲染上下文本質上是一個三維坐標系(a common three-dimensional coordinate system),這個三維坐標系由一組具有共同祖先(舞台元素)並且進行3D變換的元素共享。
在3D渲染上下文中的元素在渲染時層次關系由他們在Z軸上的位置決定。如果3D變換使他們相互交叉,那么在渲染時就讓他們交叉着渲染。
首先transform-style屬性值為flat的元素創建一個3D渲染上下文,把這個元素稱為祖先元素(舞台元素)。
其次,如果后代元素的transform-style屬性值為auto或preserve-3d,則該后代元素將其所處的3D渲染上下文(enclosing 3D rendering context)共享給它包含的后代元素。
再次,如果一個后代元素的transform-style屬性值為flat,它雖然參與到包含他的父3D渲染上下文(containing 3D rendering context)中,但是同時對於它包含的后代元素,它也創建一個新的3D渲染上下文。不過,對於這個新創建的3D渲染上下文,在渲染時不是作為一個三維空間渲染,而是作為一個平面渲染。
注意:3D渲染上下文的概念類似於層疊上下文的概念。一個有明確z-index值的定位元素自身創建一個層疊上下文,但是他還是參與到他所處的祖先元素創建的層疊上下文中。相似地,一個元素能為他的后代元素創建一個3D渲染上下文,但是他自身還是參與到他的祖先元素創建的3D渲染上下文中。就像元素在層疊上下文中按照z-index屬性決定的層次渲染一樣,元素在3D渲染上下文中按照z-depth順序渲染而且可以互相交叉。
一些CSS屬性值使一個元素及其后代元素在渲染時作為一個整體渲染(這些屬性及值在transform-style屬性的介紹中查看)。本質上這些CSS屬性值強制將元素的transform-style屬性的值重設為flat,這些元素被稱為扁平元素(flattening elements)。所以這些元素都會創建一個新的3D渲染上下文。根元素的transform-style屬性的值為flat。
在3D渲染上下文中元素的渲染遵循以下規則(規則中提到的數字步驟參見CSS 2.1, Appendix E, Section E.2 Painting Order):
A、步驟1和2提到的創建元素(establishing element)的background,border和其他的盒子裝飾。
B、按照步驟3—7的順序把創建元素的內容和后代元素中沒有進行3D變換的元素渲染到z = 0的平面內。
C、將3D變換的元素按照各自最終的3D變換矩陣(accumulated 3D transformation matrix)渲染到他們各自所在的平面內。
D、按照Newell’s algorithm渲染步驟B和C導致的不同平面之間的交叉。
E、平面的結果集渲染到步驟A提到的background和其他盒子裝飾的上層。共面的3D變換元素按照painting order渲染。
要注意到的是擁有負的z軸向分量(negative z-component)的變換元素會渲染到創建元素(establishing element)的內容和后代非變換元素的后面(3維空間的后面)。也就是說,3D變換的元素可能會貫穿創建元素的內容和后代非變換元素。
注意:因為3D變換元素在同一個3D渲染上下文中可能按深度排序和相互交叉,所以實際上好像是把它們當做兄弟元素進行渲染。transform-style: perserve-3d可以被看作是將所有3D變換元素提升到了創建元素創建的同一個3D渲染上下文中,但是他們在進行3D變換時還是按照各自最終的3D變換矩陣(accumulated 3D transformation matrix)進行變換。
例子
1 <style> 2 .container { 3 background-color: rgba(0, 0, 0, 0.3); 4 perspective: 500px; 5 } 6 .container > div { 7 position: absolute; 8 left: 0; 9 } 10 .container > :first-child { 11 transform: rotateY(45deg); 12 background-color: orange; 13 top: 10px; 14 height: 135px; 15 } 16 .container > :last-child { 17 transform: translateZ(40px); 18 background-color: rgba(0, 0, 255, 0.6); 19 top: 50px; 20 height: 100px; 21 } 22 </style> 23 24 <div class="container"> 25 <div></div> 26 <div></div> 27 </div>
這個例子演示了在同一個3D渲染上下文中的元素可以相互交叉。容器元素為它本身和它的兩個子元素創建了一個3D渲染上下文。兩個子元素相互交叉,同時橙色的子元素也和容器元素的文字內容交叉。
perspective屬性可以為3D變換上下文中的后代變換元素提供一個共同的透視變換矩陣(perspective matrix),從而被用來確保這些3D變換元素好像處在同一個有深度的三維空間中。這個透視變換矩陣在計算最終的3D矩陣( accumulated 3D matrix computation)時被考慮在內。
默認情況下,perspective屬性值不為none的元素是扁平的(flattening),因此它創建一個3D渲染上下文。然而,把transform-style屬性的值設置為preserve-3d可以讓這個透視元素(perspective element)擴展包含他的3D渲染上下文的范圍至他的后代元素(provided no other grouping property values are in effect)。
例子
1 <style> 2 div { 3 height: 150px; 4 width: 150px; 5 } 6 .container { 7 perspective: 500px; 8 border: 1px solid black; 9 } 10 .transformed { 11 transform: rotateY(50deg); 12 background-color: blue; 13 } 14 .child { 15 transform-origin: top left; 16 transform: rotateX(40deg); 17 background-color: lime; 18 } 19 </style> 20 21 <div class="container"> 22 <div class="transformed"> 23 <div class="child"></div> 24 </div> 25 </div>
這個例子演示了內嵌的3D變換元素是如何渲染的。就像前面的例子一樣,藍色div的渲染受到了他的父元素的perspective屬性值的影響。檸檬色的div同樣也進行了3D變換,繞着X軸旋轉40deg(通過transform-origin屬性,X軸被固定在了頂部)。然而,檸檬的的div被渲染到了他的父元素的平面內,因為他不在他父元素所在的3D渲染上下文中。他的父元素是平的(flattening)。所以檸檬色的div僅僅是看起來短了一些,他沒有從藍色div內“翹出來”。
Transformed element hierarchies 變換元素的層次
默認情況下,變換元素是平的(flattening),因此他們創建一個3D渲染上下文。然而,在同一個三維空間中構造層次結構是很有用的。通過將transform-style屬性的值設為preserve-3d可以使同一三維空間內的變換元素區分各自的層次,同時也允許變換的后代元素共享同一個3D渲染上下文。在3D渲染上下文中,非3D變換的后代元素被渲染到前文步驟C中的平面內,而3D變換的元素會從這個平面內“翹出來”,翹到他們變換后所在的平面內。
例子
1 <style> 2 div { 3 height: 150px; 4 width: 150px; 5 } 6 .container { 7 perspective: 500px; 8 border: 1px solid black; 9 } 10 .transformed { 11 transform-style: preserve-3d; 12 transform: rotateY(50deg); 13 background-color: blue; 14 } 15 .child { 16 transform-origin: top left; 17 transform: rotateX(40deg); 18 background-color: lime; 19 } 20 </style>
這個例子中藍色div的transform-style屬性的值被設為了preserve-3d,其余的代碼和前一個例子完全相同。現在,藍色的div將容器元素的3D渲染上下文的范圍擴展了,藍色div和檸檬色的div共享同一個三維空間。同時受到容器元素perspective屬性的影響,檸檬色的div從他的父元素的平面內翹出來了。
transform-style屬性
該屬性能應用於可變換的元素(transformable elements)。
該屬性要在父元素上設置,對該父元素的子元素(或者說后代元素)起作用。
transform-style的可取值為:auto | flat | preserve-3d
默認值為 auto,不可繼承。
當transform-style的值為“flat”時,元素創建一個層疊上下文(stacking context)和一個3D渲染上下文(3D rendering context)。
transform-style屬性值為“auto”的元素在計算3D渲染上下文時會被忽略。
transform-style屬性值為“preserve-3d”的元素會擴大其所處的3D渲染上下文的范圍,即使transform 或 preserve屬性的值會導致扁平化。同時,transform-style屬性值為“preserve-3d”的元素會創建一個層疊上下文和一個包含塊。
Grouping property values
以下CSS屬性值會導致后代元素(descendant elements)扁平化顯示,也就是說強制父元素transform-style屬性的值轉變為“flat”:
-
opacity: any value less than 1.
-
filter: any value other than none.
-
clip: any value other than auto.
-
clip-path: any value other than none.
-
isolation: used value of isolate.
-
mask-image: any value other than none.
-
mask-border-source: any value other than none.
-
mix-blend-mode: any value other than normal.
以下CSS屬性值會使transform-style的默認值重設為flat:
-
transform: any value other than none.
-
perspective: any value other than none.
Accumulated 3D Transformation Matrix Computation
在3D渲染上下文中,用來渲染一個元素的最終變換值是通過累加accumulated 3D transformation matrix得到的。累加規則如下:
-
Let transform be the identity matrix.
-
Let current element be the transformed element.
-
Let ancestor block be the element that establishes the transformed element’s containing block.
-
While current element is not the element that establishes the transformed element’s 3D rendering context:
-
If current element has a value for transform which is not none, pre-multiply current element’s transformation matrix with the transform.
-
Compute a translation matrix which represents the offset of current element from its ancestor block, and pre-multiply that matrix into the transform.
-
If ancestor block has a value for perspective which is not none, pre-multiply the ancestor block’s perspective matrix into the transform.
-
Let ancestor block be the element that establishes the current element’s containing block.
-
Let current element be the ancestor block.
-
注意:accumulated 3D transformation matrix把視覺格式化模型(visual formatting model)在變換元素上產生的偏移量計算在內,而且也把創建3D渲染上下文的元素(舞台元素)與變換元素之間樹形圖上的元素考慮在內。
Backface Visibility 背面可見性
利用三維變換,使看到變換元素的背面成為可能。在背面可見的情況下,不管是哪一面,3D變換元素都顯示同樣的內容,背面內容是前面內容的鏡像(就好像元素被投影到一面鏡子上)。默認情況下,當元素的背面朝向觀看者時,觀看者可以看到這個背面的內容。事實上,當元素的背面朝向觀看者時,開發者可以通過backface-visibility屬性,讓該元素的內容不可見。
如果一個動畫元素的backface-visibility屬性的值為hidden,那么他的內容是交替可見的。只有當他的前面朝向觀看者時,他的內容才是可見的。
例子
這個例子演示了如何制作一張可通過點擊進行翻轉的卡片。為了避免翻轉時出現扁平化,#card元素的transform-style: preserve-3d屬性是必須的。
1 <style> 2 .body { perspective: 500px; } 3 #card { 4 position: relative; 5 height: 300px; width: 200px; 6 transition: transform 1s; 7 transform-style: preserve-3d; 8 } 9 #card.flipped { 10 transform: rotateY(180deg); 11 } 12 .face { 13 position: absolute; 14 top: 0; left: 0; 15 width: 100%; height: 100%; 16 background-color: silver; 17 border-radius: 40px; 18 backface-visibility: hidden; 19 } 20 .back { 21 transform: rotateY(180deg); 22 } 23 </style> 24 <div id="card" onclick="this.classList.toggle('flipped')"> 25 <div class="front face">Front</div> 26 <div class="back face">Back</div> 27 </div>
backface-visibility屬性
可取值:visible | hidden
默認值:visible
該屬性對2D變換無效。
visible 表示背面可見,允許顯示正面的鏡像。
hidden 表示背面不可見。
The visibility of an element with backface-visibility: hidden is determined as follows:
-
Compute the element’s accumulated 3D transformation matrix.
-
If the component of the matrix in row 3, column 3 is negative, then the element should be hidden. Otherwise it is visible.
3D Transform Functions
MDN上講的比較詳細,不過是英文。地址:transform-function
元素變換時所用的坐標系是局部坐標系
matrix3d()
該函數接受16個參數,這16個參數是一個4*4的矩陣。具體看規范Mathematical Description of Transform Functions。
translate3d()
該函數接受3個參數,前兩個參數可以為<length>,也可以為<percentage>,第三個參數只能為<length>。百分數相對於變換元素的border box的尺寸計算。
三個參數組成一個三維向量,三個值分別表示這個向量在相應坐標軸上的坐標,變換元素根據這個向量進行平移。
translateX()和translateY()在2D變換中已經介紹過了,這里只介紹translateZ()。
translateZ()
該函數只接受一個參數,使變換元素沿着Z軸移動指定的長度,相當於translate3d(0,0,tz),而且只能用在3D變換中。
scale3d()
該函數接受3個<number>參數,分別代表變換元素在X軸,Y軸和Z軸上的縮放比例。如果對應某一坐標軸上的參數值在(-1,1)范圍內,則元素在該坐標軸方向上縮小,超出上述范圍,則元素在該坐標軸方向上放大。如果等於1或-1,元素在該坐標軸上的尺寸不變。另外,負號表示對稱變換。
scaleZ()
該函數只接受一個參數,相當於scale3d(1,1,sz)。
注意:單獨使用3D的縮放變換,除了能在X軸和Y軸上看到效果,Z軸上是看不到效果的。
rotate3d()
該函數可以使元素繞着坐標軸進行3D旋轉變換,正值表示順時針旋轉,負值表示逆時針旋轉。
在3D空間中,旋轉有三個自由度。旋轉軸可以通過一個經過坐標原點(transform-origin指定)的三維向量 [x,y,z] 表示。如果這個向量不是標准化的向量(單位向量),瀏覽器會在內部自動將其轉換為標准化的向量。如果這個向量不能被標准化(比如[0,0,0]),那么元素本次的旋轉變換將無效,而不是將整個transform屬性無效。
注意:與2D的旋轉變換相比,交換同一元素的不同的3D旋轉變換的次序得到的結果是完全不同的,所以對所應用的3D旋轉變換的順序要引起重視。
語法:rotate3d(x, y, z, a)
x 表示旋轉軸在x軸上的坐標
y 表示旋轉軸在y軸上的坐標
z 表示旋轉軸在z軸上的坐標
a 是一個<angle>值,表示旋轉的角度,正值表示順時針旋轉;負值表示逆時針旋轉。
rotateX()
只接受一個<angle>參數,相當於rotate3D(1, 0, 0, a)
rotateY()
只接受一個<angle>參數,相當於rotate3D(0, 1, 0, a)
rotateZ()
只接受一個<angle>參數,相當於rotate3D(0, 0, 1, a)
perspective()
該函數接受一個<length>參數,本質是指定一個透視投影矩陣(perspective projection matrix),使元素進行透視投影變換。這個矩陣根據坐標點的z坐標對x坐標和y坐標進行縮放:放大z坐標為正的點,使該點遠離原點;縮小z坐標為負的點,使該點靠近原點;z=0平面上的點不變。傳入該函數的參數代表的是觀看者的眼睛(假設的)與z=0平面之間的距離。值越小,得到的視錐體越扁平,透視效果越明顯。比如,傳入參數為1000px時,結果是一個適中的透視縮短效果;傳入參數為200px時,結果是一個極端的透視縮短效果。傳入的值必須大於0,否則無效。
總結
要實現3D變換,要用到下面幾個屬性:
屬性 |
描述 |
CSS |
transform |
向元素應用 2D 或 3D 轉換。 |
3 |
transform-origin |
設置變換基點(局部坐標系原點)的位置。 |
3 |
transform-style |
規定被嵌套元素如何在 3D 空間中顯示。 |
3 |
perspective |
規定 3D 元素的透視效果。 |
3 |
perspective-origin |
規定觀看者眼睛的投影位置。 |
3 |
backface-visibility |
定義元素內容在不面對屏幕時是否可見。 |
3 |