本節內容來自於小冊 WebGL 入門與實踐。javascript
前面介紹了 3D 變換的原理和算法實現,並經過一些簡單的 demo 演示了變換效果,但這些 demo 都是使用 WebGL 技術渲染。本節咱們暫時不使用 WebGL,而是改用前端同窗最熟悉的 CSS 技術來實現 3D 效果,並進一步瞭解 CSS 中的 3D 屬性和 WebGL 中 3D 概念的異同之處。css
下面是 CSS3 中的幾個很重要的 3D 屬性:前端
本節咱們主要講述 CSS 中的變換屬性transform
,變換分爲基本變換
和矩陣變換
,基本變換你們都比較熟悉了,本節不作過多介紹,咱們主要介紹矩陣變換
和組合變換
。java
transform
是你們最經常使用的一個屬性,咱們常常會使用它實現一像素的邊框和以及容器或者內容的水平、垂直居中,又或者利用它實現強制 GPU 渲染,提高動畫性能。算法
transform 分爲 2D 和 3D 變換,3D 變換隻是在 2D 的基礎上增長了 Z 軸方向的變換。瀏覽器
通常狀況下,若是對一個 DOM 施加變換,那麼變換的中心每每是 DOM 的中心位置,類比到 WebGL 中,也就是模型的中心,咱們能夠把 CSS 中的 DOM 看作 WebGL 中的模型。bash
transform 包含四個基本變換屬性值:translate
、rotate
、skew
、scale
,對應的 3D 變換屬性值爲 translate3d
、rotate3d
、scale3d
。app
注意,skew 沒有對應的 3D 變換設置。dom
基本變換你們應該都很熟悉了,後面重點要講解的是 matrix
、matrix3d
的計算與使用,以及組合變換
的使用技巧。性能
固然,下面咱們仍是先回顧一下 transform
的基本用法。
平移的使用方法:
將 dom 元素分別沿着 X、Y、Z 軸向平移 30 px。
/* 分別沿 X 軸和 Y 軸平移 30 px。*/
transform: translate(30px, 30px);
/* 分別沿 X 軸、 Y 軸、Z 軸平移 30 px。*/
transform: translate3d(30px, 30px, 30px);
/* 沿 X 軸平移 30 px。*/
transform: translateX(30px);
/* 沿 Y 軸平移 30 px。*/
transform: translateY(30px);
/* 沿 Z 軸平移 30 px。*/
transform: translateZ(30px);
複製代碼
旋轉用法也比較簡單,在此不作過多描述。
axis = {x:x, y:y, z:z}
旋轉指定角度angle
。/*繞 X 軸旋轉 45 deg。*/
transform: rotate(45deg);
/* 將第一個參數設置爲 1, 表明繞 X 軸旋轉 45 deg。*/
transform: rotate3d(1, 0, 0, 45deg);
/* 將第二個參數設置爲 1, 表明繞 Y 軸旋轉 45 deg。*/
transform: rotate3d(0, 1, 0, 45deg);
/* 將第三個參數設置爲 1, 表明繞 Y 軸旋轉 45 deg。*/
transform: rotate3d(0, 0, 1, 45deg);
/* 將三個參數都設置爲 1, 表明繞 Y 軸旋轉 45 deg。*/
transform: rotateZ(45deg);
複製代碼
關於旋轉,我想說明一下rotate3d
的使用方式,它接收一個軸向量
和一個角度
,表明繞軸向量
旋轉某個角度
。
舉個例子來講,咱們想讓模型繞軸 axis= {x: 1,y: 1,z: 1}進行旋轉,那麼用rotate3d表示以下:
.box{
animation: rotate 3s infinite linear;
}
@keyframes rotate{
0% {
transform:rotate3d(1, 1, 1, 0deg);
}
100% {
transform:rotate3d(1, 1, 1, 360deg);
}
}
複製代碼
根據上圖的例子,你會發現,默認的旋轉是繞着模型的中心位置進行的,這個位置是瀏覽器默認的。但事實上,CSS 仍然提供了對變換中心的設置功能,經過設置 transform-origin
來實現。
好比,咱們讓一個 DOM 元素沿着上邊沿進行進行旋轉,只須要將 transform-origin 的 Y 軸份量設置爲 0% 或者 0 便可。
transform-origin: 50% 0%;
transform: rotateX(90deg);
複製代碼
這個概念比較簡單,可是很靈活。利用它咱們能實現不少有意思的 3D 效果,好比 CSS 版的魔方
。
縮放的使用方法也很簡單。
代碼示例:
/* 沿 X 軸和 Y 軸 放大兩倍。*/
transform: scale(2);
/* 沿 X 軸方向當大 3 倍,沿 Y 軸方向放大 2 倍。*/
transform: scale(3, 2);
/* 分別在 X 、Y、 Z 軸方向放大 2倍、3倍、4倍。*/
transform: scale3d(2, 3, 4);
/* 在 X 軸方向放大兩倍。*/
transform: scaleX(2);
/* 在 Y 軸方向放大兩倍。*/
transform: scaleY(2);
複製代碼
斜切的使用方法:
斜切能夠理解爲將 DOM 元素沿 X 軸或者 Y 軸拉伸,切變會改變物體的形狀。
代碼示例:
/* 沿 X 軸切變 30 度。*/
transform: skew(30deg);
/* 沿 X 軸切變 30 度,沿 Y 軸切變 40 度。*/
transform: skew(30deg, 40deg);
/* 沿 X 軸方向切變 30 度。*/
transform: skewX(30deg);
/* 沿 Y 軸方向切變 40 度。*/
transform: skewY(40deg);
複製代碼
以上就是 transform
的常見用法,接下來咱們開始講重點了:組合變換
和 matrix
組合變換
就是在 transform 的屬性值中附加多個變換效果,而不僅是單一的變換。
好比下面這個變換樣式:
transform: rotateX(60deg) rotateY(60deg);
複製代碼
這個樣式的做用是先讓 DOM 元素繞着 X 軸旋轉 60 度,注意此時 DOM 元素的座標系改變了,再繞變換後的座標系的 Y 軸旋轉 60 度。
須要謹記的是:
動態座標系變換
。靜態座標系變換
。至於多個變換是基於動態座標系進行構思,仍是基於靜態的世界座標系進行構思,取決於每一個人的理解習慣,但最終的變換效果都是同樣的。
在歐拉角章節咱們也講過了多個矩陣相乘時,從前日後和從後往前理解變換所基於的座標系是不一樣的。transform 多個變換理解順序和前面所講的保持一致。
再舉個比較明顯的例子,咱們先讓 DOM 旋轉 60 度,而後將其沿 X 軸平移 200 像素,你們以爲 DOM 會按照怎樣的軌跡變換?
咱們看一下:
transform: rotateX(60) translateX(200px);
複製代碼
爲了更方便觀察 3D 組合變換的效果,我將圖片外層容器的視點
設置在了右上方:
.imgWrapper{
perspective: 300px;
margin-top:300px;
position: relative;
perspective-origin: 100% -100px;
}
複製代碼
關於視點 perspective-origin
和 視距 perspective
咱們放在下一節講述。
模型座標系
理解:下圖,白色座標軸是圖片默認的座標系,當圖片繞 X 軸旋轉 60 度後,座標系變成紅色座標軸的指向。
接着沿當前圖片座標系的紅色 X' 軸平移 200 像素,此時,應該朝向屏幕裏側和右側移動。
請注意,CSS
中的世界座標系
就是 施加變換的 DOM 節點(本例爲圖片)最開始的座標系,即圖中的白色座標軸。
能夠看出,不管咱們按照哪一種座標系理解,最終變換效果都是同樣的,看下圖:
這就是組合變換
要注意的地方,瞭解了組合變換的規律,咱們才能作出頗有意思的特效,好比照片牆:
照片牆的核心原理就是先將圖片沿着 Z 軸方向移動必定距離,以後繞世界座標系的Y 軸旋轉指定角度。
transform: rotateY(30deg) translateX(200px);
複製代碼
固然,這只是核心原理,事實上咱們還須要作以下幾步:
transform-style
屬性設置爲 preserve-3d
。perspective
,設置爲透視投影,並調整合適的視距 ,這樣才能實現近大遠小的效果。這幾個屬性咱們下節細講,先貼下照片牆的效果:
具體的實現咱們在講視點和視距屬性時再分析。
又好比 3D 盒效果:
這是一個立方體盒子,每一個面上都對應一張圖片,固然,你也能夠根據本身的須要往各個面上放置本身的內容。
立方體盒子的原理也是利用組合變換實現的:
固然,爲了實現 3D 效果,也要在圖片的父容器上設置 transform-style
爲 preserve-3d
才能夠。
這裏主要展現組合變換順序的理解與強大,不對實現作分析,實現過程
留在下一節和視點
以及視距
一塊兒介紹。
咱們仍是先把變換講完,接下來介紹 transform
的另外一個重要用法,matrix
和 matrix3d
。
transform 除了提供一些基本變換,還提供了 matrix
和 matrix3d
屬性值,這兩個屬性值是作什麼的呢?
matrix
是矩陣的意思,transform 是變換屬性,因此 matrix 就是對 DOM 執行變換的矩陣,這和咱們前面講的 WebGL 的變換矩陣概念相同。
根據前面的學習,咱們知道 2 維平面的變換矩陣,是一個 3 階矩陣,包含 9 個數字:
按照咱們以前章節座標系變換的原理分析:
若是沒有看以前章節的話,可能不太理解基向量的含義以及座標系的概念,你們能夠去看一下,理解一下座標系變換的原理。
你會看到第三行的數值是固定的0 0 1
,因此,瀏覽器爲了簡化賦值,規定 transform 中的 matrix 只接受 3 階矩陣的前兩行參數,共 6 個數字。
請記住,matrix 的參數順序對應上面的矩陣元素以下:
transform: matrix(x0, x1, y0, y1, tx, ty);
複製代碼
前面章節咱們推導過基本變換的矩陣表示:
沿 X 軸平移 tx 像素,沿 Y 軸平移 ty 像素:
沿 X 軸方向縮放 sx 倍, 沿 Y 軸縮放 sy 倍:
也就是說基本變換咱們均可以用 matrix
來表示。
繞 Z 軸旋轉 θ 角度:
用 rotateZ 表示上面的旋轉很簡單,咱們看下用 matrix 如何表示:
將這兩個數字代入 matrix 公式,得出變換樣式爲:
transform: matrix(0.5, 0.866, -0.866, 0.5, 0, 0);
複製代碼
你會發現不管咱們是用 rotateZ 表示,仍是用 matrix 表示,變換效果都是同樣的。
matrix3d,顧名思義,表明 3 維變換,它是一個 4 階矩陣,須要 16 個數字來表示。
能夠看到,每個基向量都增長了一個 Z 軸方向份量 x二、y二、z二、tz。
事實上,3D 的基本變換均可以用 matrix 或者 matrix3d 來表示,可是一些複雜變換隻能使用 matrix 或者 matrix3d 來實現。
這是一個很關鍵的問題,你們會以爲,基本變換的用法更容易理解,更方便書寫,matrix 須要的參數太多,並且參數值須要計算,更糟糕的是,咱們每每不知道怎麼計算,那 matrix 有什麼用呢?
這是個好問題,但我想說的是 matrix 能完成基本變換不能完成的變換,能作出基本變換完成不了的效果。
這時你就該考慮使用 matrix 了。
緊跟而來的問題是,matrix 如何求得呢?
咱們前面章節講述了 matrix 矩陣的求法,一旦你肯定了須要的變換,你就能夠計算變換後的基向量,而後將基向量的各個份量代入矩陣的各個位置便可求出變換矩陣,有了變換矩陣,也就有了 matrix 所須要的各個元素,將矩陣轉化成 matrix 或者 matrix3d 所須要的字符串就垂手可得了。
鏡像效果,採用基本變換是實現不了的,只能藉助於矩陣。 好比左右鏡像,左右鏡像無非就是 Y 軸基向量不變,X 軸座標對調,原座標與新座標關係以下:
因此有:
將這些值,代入 matrix公式中,能夠得出變換:
transform: matrix(-1,0,0,1,0,0);
複製代碼
咱們看下效果:
固然你們也能夠觸類旁通,好比上下鏡像:
transfom: matrix(1,0,0,-1,0,0)
複製代碼
繞任意軸的旋轉,除了可使用 rotate3d 來實現,還可使用 matrix3d。這個旋轉矩陣該如何計算呢?
還記得基本變換章節咱們推導出的繞固定軸旋轉的矩陣方法axisRotation
嗎?
axisRotation(axis, angle)
,其中 axis 是一個三維向量,angle 是一個弧度值。
這裏我不許備舉繞基本軸旋轉的例子,由於它們不須要matrix3d 出手,咱們想繞{x:1, y: 1, z: 1}
的傾斜軸旋轉,上面的 axisRotation 方法該出場了,它會爲咱們提供變換矩陣,可是咱們還須要將變換矩陣轉化爲 css 樣式,咱們先封裝一個矩陣轉 css 的方法:
function matrix2css(mt){
var transformStyle = 'matrix(';
if(mt.length == 16){
css = 'matrix3d('
}
for(let i =0; i< mt.length; i++){
transformStyle += mt[i];
if(i !== mt.length - 1){
transformStyle += ','
}else{
transformStyle +=')'
}
}
return transformStyle;
}
複製代碼
接着能夠經過 axisRotation 方法計算出變換矩陣了,好比旋轉 90 度。
let mt = matrix.axisRotation({x:1, y:1, z:1}, Math.PI / 180 * 90)
複製代碼
在這裏我用繞軸向量 axis = {x:1, y:1, z:1} 不停旋轉的動畫演示:
function render(){
if(!playing){
return;
}
angle ++;
mt = matrix.axisRotation({x: 1,y: 1,z: 1}, Math.PI / 180 * angle);
let css = matrix2css(mt);
$$('.box')[0].style.transform = css;
requestAnimationFrame(render);
}
document.body.addEventListener('click',function(){
playing = !playing;
render();
})
複製代碼
是否是很簡單呢?
transform 中的基本變換均可以使用 matrix 和 matrix3d 來表示,只有當基本變換表示不了咱們的變換時,咱們才考慮使用 matrix 或者 matrix3d 。
可見,即便不作 WebGL 開發,咱們以前學到的內容也會對普通開發者大有幫助,掌握變換原理,配合 CSS3 中的 3D 屬性,照樣能夠作出很酷炫的 3D 動畫效果。
本節講述了 CSS 中的 3D 變換,以及它們與 WebGL 變換的相同之處。總的來講,基本原理都是同樣的,以前封裝的數學矩陣庫,不只能夠用在 WebGL 領域,也能夠用在 css 領域。
下一節,咱們講述 CSS 中 3D 變換 的其它幾個重要屬性,變換類型(transform-style)
、視點(perspective-origin)
、視距(perspective)
。
這一系列的內容來自於小冊 WebGL 3D 入門與實踐,若是你們對進階知識感興趣,能夠到小冊中去學習:小冊:WebGL 3D 入門與實踐。
小冊內容除了包含 WebGL 相關的基礎練習,還包括 3D 圖形概念與相關數學的原理與推導,旨在幫助你們創建圖形學的技術輪廓。這部分圖形學知識獨立於 WebGL,除了能夠適用於 WebGL,還適用於 OpenGL 等。
固然,本小冊主要目的仍是幫助 Web 前端同窗學習 3D 技術,除了介紹適用 WebGL 實現 3D 效果之外,還對 CSS3 中的 3D 技術相關屬性進行了深刻剖析,並演示了與數學庫的結合使用,但願可以讓前端同窗不只僅侷限於通常的二維平面開發,也可以在 3D 開發上更近一步~