關於CSS3 3D Transform是變形屬性裡面的戰鬥機,就像立體幾何的學習要在平面幾何之後類似,在關於所有的2D的屬性摸個七七八八之後,終於開始進階3D變形了。先來一張SVG的背景圖。炎炎夏日,清涼一下,即使不能行萬里路,至少還有心在遠方。
對於2D方向的移動,已經乏善可陳了,一個transform:translateX() 或者transform:translateY() 就能輕鬆實現水平和垂直方向的移動。那我們都知道,3D空間多了一個Y軸維度,4D空間多了一個時間維度,5D空間多了……就此打住,本篇不討論科幻。(此處插播一本小說《平面國》,2D維度的世界,也比較有趣。)言歸正傳,螢幕展現了二維平面,那把Z軸想象成你和螢幕之間的一個距離軸線。既然如此,那transform:translateZ()是不是就能實現Z軸維度的移動了呢?你想多了。畢竟從2D到3D也是一個量變,豈能辣麼簡單?
1.關於容器和透視屬性
先簡單來說一下瀏覽器實現3D效果的原理,各種座標軸什麼的就不班門弄斧了,畢竟一搜一大堆。SVG向瀏覽器宣告“我要使用3D效果啦,做好準備”就要傳遞一個訊號,只有translateZ是不夠的,瀏覽器需要有一個準則,就是沿Z軸移動的比例是什麼?換句話說,需要設定一個透視點perspective來配置3D空間,也就是CSS的perspective屬性,再直白點說,就是我們的眼睛距離螢幕(Z軸)的距離。那perspective屬性應該對誰定義呢?舞臺,或者說是SVG的父容器。比如說,我定義了沿Z軸移動的動畫cubic如下:
@keyframes cubic{
0% {transform: translateZ(0)}
100% {transform: translateZ(-300px)}
}
.cubic {animation:cubic 2s ease;}複製程式碼
作為圖片而存在的SVG是沒有perspective屬性的,它的父容器是什麼?我們都知道,SVG裡面的標籤,<path>
也好,<circle>
也好,都是一些繪製方法,<g>
也只是進行了組合,並非真正的父容器,這時,純SVG有些無力了,那我們就把它放到一個<div>
裡,來實現這種3D效果。既然“在我地盤這兒那就得聽我的”,下面就來定義這個霸氣的地盤(或者稱之為舞臺)透視屬性。
.stage {
perspective: 200px;
background:#e5fffb; /*給舞臺定義一個淺綠背景色*/
}複製程式碼
<body>
程式碼部分就簡單多了,只需要<div>
裡巢狀這個<SVG>
就OK了。
<div class="stage">
<svg class="cubic" xmlns="http://www.w3.org/2000/svg" width="800" height="600">
<g>
…此處省略若干組成SVG圖形的程式碼
</g>
</svg>
</div>複製程式碼
來檢驗一下效果如何
是不是有些失望?這明明就是縮放效果好不啦?我要你?明明一個transform:scale()就能搞定!掀桌子,抗議,停停停,說過Z軸是眼睛和螢幕間的軸線,沿Z軸移動可不就是縮放嘛。人家可是用二維的表現來營造觀察者離螢幕移動的的場景啊(你還想怎樣,難不成讓圖片從螢幕裡出來,像貞子一樣?)。
2.透視屬性與變化
這裡關於perspective屬性值我們先定義幾個極值看一下,第一個,我定義perspective:1000px,效果如下:
在同樣translateZ情況下,感覺變化很小。
第二個,我定義perspective:1px,效果如下:
在同樣translateZ情況下,變化灰常劇烈,縮成了一個點。
為了好理解,我們先假定地心說,宇宙萬物繞著地球轉。請看下圖(原諒我不會3D建模,好憂桑)
左邊那個場景我們想象成一個小人以45度角仰望天空,一輪明月懸空高掛,右邊那個場景我們想象成一個小人頭上不遠的地方掛了個大南瓜吧。在視覺上,明月和南瓜是一樣大小的。此時,當太陽向小人方向移動2米,小人完全看不到這種移動,但南瓜往腦袋方向移動2米,只見越來越大,我的媽呀,這是要砸下來了。所以同樣的移動距離,由於視距不同,產生了差異。正所謂近大遠小,也就是說perspective值越大,代表觀察者離螢幕越遠,那同樣translateZ的移動距離,在視覺上呈現的效果越明顯。
因為我把translateZ定義成了負值,就是背離眼睛方向(眼睛→螢幕),所以越來越小,那當我改一下,transform: translateZ(300px),讓Z軸移動為背離螢幕情況下又是怎樣的呢?
噴薄欲出,這就是南瓜砸腦袋的效果吧。
再延伸一下,當perspective為0時,會是什麼效果呢,沿Z軸正向移動時,大到無極限,沿Z軸負向移動時,小到看不見?不,此極值相當於眼睛貼到了螢幕上,恭喜你,這是進入了二維世界,所以Z軸的移動對於二維平面來說,是沒有變化的。這也就是為什麼我們perspective不定義(預設為0)時,沒有效果的原因。
3.關於透視原點perspective-origin
所有的變形動畫都有一個原點(不定義,不代表沒有,通常是預設值),這在tranform:rotate選擇動畫中表現最為明顯,我們會經常定義變形原點tranform-origin來確定元素動畫效果的基準點。對於我們3D動畫屬性來說,就要增加一個類似的概念,透視原點perspective-origin。如果說perspective值代表眼睛離螢幕的垂直距離,那perspective-origin則代表了上下左右晃動腦袋後再去觀察,此動作是二維平面的動作,因此perspective-origin屬性包括兩個值,一個與水平X相關,一個與垂直y相關當預設時,預設為(center center)還是上面的Z軸移動效果,來試一下改變perspective-origin屬性值會發生什麼。如下,我給父容器增加一個透視原點的屬性值
.stage {
perspective: 200px;
background:#e5fffb;
perspective-origin: left top;
}複製程式碼
沿Z軸移動就變成了下面這個樣子:
其他屬性值就不再一一嘗試了,大概也能猜個七七八八,這裡除了left right top bottom center這種直接寫法,還支援百分比的表示方法。感興趣可以自行嘗試。
4. 3D旋轉動畫
沿Z軸移動這種效果,別說眾位看官沒有興趣,我也覺索然無味,單純的移動可以靠2D的一些屬性來完成,接下來試個稍微有趣點的效果,3D空間的旋轉。在2D平面上的旋轉transform:rotate(),是點旋轉,這裡的點,是指在平面上繞的點,而3D空間的旋轉,則是軸旋轉。繞X軸或Y軸,所以我們要把屬性寫成transform:rotateX()或transform:rotateY()這樣子。
現在重新定義一下動畫屬性,先來個繞X軸旋轉45度:
.cubic {animation:cubic 2s ease}
@keyframes cubic{
0% {transform: rotateX(0)}
100% {transform: rotateX(45deg)}
}複製程式碼
得到的動畫效果:
從效果裡可以明顯的看出,首先,旋轉方向的定義,正值是向螢幕內部旋轉,如果這麼說沒有空間感的話,也可以理解成在Y和Z組成的平面(即與X軸垂直的平面)以笛卡爾座標系X軸正值方向觀察為順時針旋轉。算了,太彆扭了,還是來張3D座標系的圖解釋一下。
從動畫效果裡我們會發現一個奇怪的現象,就是SVG底圖——變!虛!了!說好的向量圖形無限放大呢?因為不懂演算法渲染什麼的,個人猜測瀏覽器在處理這類3D效果時,先繪製了一張點陣圖,然後以點陣圖為藍本進行處理,好吧,我在胡扯,具體原因等查到相關資料再更正。
改幾個角度看一下,先改成-90deg,效果如下:
再來個180度翻轉的:
以及360度繞軸一圈的(先打住,沒意思了啊,猜都猜出效果了),這裡呢,關於透視原點perspective-origin因為沒做定義,所以旋轉的中規中舉,我隨便改一下,把父容器的透視原點屬性值改成perspective-origin: right bottom,也就是右下角,得到的效果是下面這樣的:
繞完X軸的看過一遍之後,繞Y軸的閉著眼就能想象。隨便來一個吧,現在我把繞Y軸的角度定義成120度
@keyframes cubic{
0% {transform: rotateY(0)}
100% {transform: rotateY(120deg)}
}複製程式碼
有沒有興趣看看繞Z軸比如transform: rotateY(120deg)的效果:
其實想想也是了,對於2D平面的圖形,Z軸就是一個點,所以繞Z軸旋轉就變成了普通的旋轉動畫效果。
還是3D的玩起來更是花樣百出。興趣指數,五顆星!
3D動畫一旦玩起來,是很可怕的,前面雖然各種屬性設定來過一遍,但說到底,我們用的SVG底圖還是2D的,一張平面的圖轉來繞去的能有什麼意思?總歸是缺乏3D應有的使用場景。運用在多面體上的方法和效果下次再更。