WebGL 3D 入門與實踐: CSS 中的 3D 屬性

本節內容來自於小冊 WebGL 入門與實踐javascript

前面介紹了 3D 變換的原理和算法實現,並經過一些簡單的 demo 演示了變換效果,但這些 demo 都是使用 WebGL 技術渲染。本節咱們暫時不使用 WebGL,而是改用前端同窗最熟悉的 CSS 技術來實現 3D 效果,並進一步瞭解 CSS 中的 3D 屬性和 WebGL 中 3D 概念的異同之處。css

CSS 中的 3D 屬性

下面是 CSS3 中的幾個很重要的 3D 屬性:前端

  • transform:對 DOM 進行變換,至關於 WebGL 中對模型進行的變換。
  • transform-origin:設置變換的中心點。
  • perspective-origin:視點,至關於 WebGL 中攝像機的 X、Y 軸座標。
  • perspective:視距,啓用該屬性至關於在 WebGL 中設置攝像機和 DOM 元素之間在 Z 軸方向上的距離,設置該屬性不等於 0 時會自動啓用透視投影效果。
  • transform-style:是否啓用 3D 變換。
  • backface-visibility:背面是否可見。

本節咱們主要講述 CSS 中的變換屬性transform,變換分爲基本變換矩陣變換,基本變換你們都比較熟悉了,本節不作過多介紹,咱們主要介紹矩陣變換組合變換java

變換:transform

transform 是你們最經常使用的一個屬性,咱們常常會使用它實現一像素的邊框和以及容器或者內容的水平、垂直居中,又或者利用它實現強制 GPU 渲染,提高動畫性能。算法

transform 分爲 2D 和 3D 變換,3D 變換隻是在 2D 的基礎上增長了 Z 軸方向的變換。瀏覽器

通常狀況下,若是對一個 DOM 施加變換,那麼變換的中心每每是 DOM 的中心位置,類比到 WebGL 中,也就是模型的中心,咱們能夠把 CSS 中的 DOM 看作 WebGL 中的模型。bash

transform 包含四個基本變換屬性值:translaterotateskewscale,對應的 3D 變換屬性值爲 translate3drotate3dscale3dapp

注意,skew 沒有對應的 3D 變換設置。dom

基本變換你們應該都很熟悉了,後面重點要講解的是 matrixmatrix3d的計算與使用,以及組合變換的使用技巧。性能

固然,下面咱們仍是先回顧一下 transform 的基本用法。

平移

平移的使用方法:

  • translate(tx, ty)
  • translate3d(tx, ty, tz)
  • translateX(tx)
  • translateY(ty)
  • translateZ(tz)

將 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);
複製代碼

旋轉

旋轉用法也比較簡單,在此不作過多描述。

  • rotate(angle),繞 Z 軸旋轉。
  • rotate3d(x, y, z, angle)。繞軸axis = {x:x, y:y, z:z}旋轉指定角度angle
  • rotateX(angle),繞 X 軸旋轉。
  • rotateY(angle),繞 Y 軸旋轉。
  • rotateZ(angle),繞 Z 軸旋轉。
/*繞 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);
    }
}
複製代碼

變換參照點 transform-origin

根據上圖的例子,你會發現,默認的旋轉是繞着模型的中心位置進行的,這個位置是瀏覽器默認的。但事實上,CSS 仍然提供了對變換中心的設置功能,經過設置 transform-origin 來實現。

  • transform-origin 包含 X、Y、Z 軸座標的設置。
  • transform-origin 接收百分比數值時,是以自身尺寸爲基準的。

好比,咱們讓一個 DOM 元素沿着上邊沿進行進行旋轉,只須要將 transform-origin 的 Y 軸份量設置爲 0% 或者 0 便可。

transform-origin: 50% 0%;
transform: rotateX(90deg);
複製代碼

這個概念比較簡單,可是很靈活。利用它咱們能實現不少有意思的 3D 效果,好比 CSS 版的魔方

縮放

縮放的使用方法也很簡單。

  • scale(sx, sy),沿 X 軸方向縮放 sx 倍,沿 Y 軸方向縮放 sy 倍。
  • scale(sx),沿 X 軸方向和 Y 軸方向縮放 sx 倍。
  • scale3d(sx, sy, sz),分別沿 X、Y、Z 軸方向縮放 sx、sy、sz 倍。
  • scaleX(sx),沿 X 軸方向縮放 sx 倍。
  • scaleY(sy),沿 Y 軸方向縮放 sy 倍。

代碼示例:

/* 沿 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);
複製代碼

斜切

斜切的使用方法:

  • skew(xAngle, yAngle),DOM 元素沿着 X 方向切變 xAngle 度,沿 Y 軸方向切變 yAngle 度。
  • skewX(xAngle),沿 X 軸方向切變 xAngle 度。
  • skewY(yAngle),沿 Y 軸方向切變 yAngle 度。

斜切能夠理解爲將 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 元素最開始的座標軸(能夠理解爲世界座標系)進行,也稱爲靜態座標系變換

至於多個變換是基於動態座標系進行構思,仍是基於靜態的世界座標系進行構思,取決於每一個人的理解習慣,但最終的變換效果都是同樣的。

在歐拉角章節咱們也講過了多個矩陣相乘時,從前日後和從後往前理解變換所基於的座標系是不一樣的。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 節點(本例爲圖片)最開始的座標系,即圖中的白色座標軸。

  • 首先沿着 X 軸平移 200 像素。
  • 接着繞世界座標系的 Y 軸旋轉 60 度。

能夠看出,不管咱們按照哪一種座標系理解,最終變換效果都是同樣的,看下圖:

這就是組合變換要注意的地方,瞭解了組合變換的規律,咱們才能作出頗有意思的特效,好比照片牆:

照片牆的核心原理就是先將圖片沿着 Z 軸方向移動必定距離,以後繞世界座標系的Y 軸旋轉指定角度。

transform: rotateY(30deg) translateX(200px);
複製代碼

固然,這只是核心原理,事實上咱們還須要作以下幾步:

  • 讓圖片可以顯示出 3D 效果,這一步須要讓圖片父容器的 transform-style 屬性設置爲 preserve-3d
  • 爲父容器加上透視屬性perspective,設置爲透視投影,並調整合適的視距 ,這樣才能實現近大遠小的效果。

這幾個屬性咱們下節細講,先貼下照片牆的效果:

具體的實現咱們在講視點和視距屬性時再分析。

又好比 3D 盒效果:

這是一個立方體盒子,每一個面上都對應一張圖片,固然,你也能夠根據本身的須要往各個面上放置本身的內容。

立方體盒子的原理也是利用組合變換實現的:

  • 前面:沿着 Z 軸往前(朝向屏幕外)平移指定像素。
  • 後面,沿着 Z 軸日後(朝向屏幕裏)平移指定像素。
  • 上面,沿着 Y 軸往上平移,接着繞 X 軸旋轉 90 度。
  • 下面,沿着 Y 軸往下平移,接着繞 X 軸旋轉 90 度。
  • 左面,沿着 X 軸往左平移,接着繞 Y 軸旋轉 90 度。
  • 右面,沿着 X 軸往右平移,接着繞 Y 軸旋轉 90 度。

固然,爲了實現 3D 效果,也要在圖片的父容器上設置 transform-stylepreserve-3d 才能夠。

這裏主要展現組合變換順序的理解與強大,不對實現作分析,實現過程留在下一節和視點以及視距一塊兒介紹。

咱們仍是先把變換講完,接下來介紹 transform 的另外一個重要用法,matrixmatrix3d

matrix

transform 除了提供一些基本變換,還提供了 matrixmatrix3d 屬性值,這兩個屬性值是作什麼的呢?

matrix 是矩陣的意思,transform 是變換屬性,因此 matrix 就是對 DOM 執行變換的矩陣,這和咱們前面講的 WebGL 的變換矩陣概念相同。

根據前面的學習,咱們知道 2 維平面的變換矩陣,是一個 3 階矩陣,包含 9 個數字:

\begin{aligned}
\begin{pmatrix}
x0 & y0 & tx \\\
x1 & y1 & ty \\\
0 & 0 & 1
\end{pmatrix}
\end{aligned}

按照咱們以前章節座標系變換的原理分析:

  • x0、x1 表明變換以後的 X 軸基向量在原座標系中的表示。
  • y0、y1 表明變換以後的 Y 軸基向量在原座標系中的表示。
  • tx、ty 表明座標原點的偏移量。

若是沒有看以前章節的話,可能不太理解基向量的含義以及座標系的概念,你們能夠去看一下,理解一下座標系變換的原理。

你會看到第三行的數值是固定的0 0 1,因此,瀏覽器爲了簡化賦值,規定 transform 中的 matrix 只接受 3 階矩陣的前兩行參數,共 6 個數字。

請記住,matrix 的參數順序對應上面的矩陣元素以下:

transform: matrix(x0, x1, y0, y1, tx, ty);
複製代碼

前面章節咱們推導過基本變換的矩陣表示:

  • 平移

沿 X 軸平移 tx 像素,沿 Y 軸平移 ty 像素:

\begin{aligned}
\begin{pmatrix}
1 & 0 & tx \\\
0 & 1 & ty \\\
0 & 0 & 1
\end{pmatrix}
\end{aligned}
  • 縮放

沿 X 軸方向縮放 sx 倍, 沿 Y 軸縮放 sy 倍:

\begin{aligned}
\begin{pmatrix}
sx & 0 & 0 \\\
0 & sy & 0 \\\
0 & 0 & 1
\end{pmatrix}
\end{aligned}

也就是說基本變換咱們均可以用 matrix 來表示。

  • 斜切 沿着 X 軸傾斜 α 度,沿着 Y 軸傾斜 θ 度:
\begin{aligned}
\begin{pmatrix}
1 & tan\alpha & 0 \\\
tan\theta & 1 & 0 \\\
0 & 0 & 1
\end{pmatrix}
\end{aligned}
  • 旋轉

繞 Z 軸旋轉 θ 角度:

\begin{aligned}
\begin{pmatrix}
cos\theta & -sin\theta & 0 \\\
sin\theta & cos\theta & 0 \\\
0 & 0 & 1
\end{pmatrix}
\end{aligned}
  • 繞 Z 軸旋轉 60 度。

用 rotateZ 表示上面的旋轉很簡單,咱們看下用 matrix 如何表示:

cos(60) = 0.5

sin60 = \frac{\sqrt3}{2} = 0.866(約等)

將這兩個數字代入 matrix 公式,得出變換樣式爲:

transform: matrix(0.5, 0.866, -0.866, 0.5, 0, 0);
複製代碼

你會發現不管咱們是用 rotateZ 表示,仍是用 matrix 表示,變換效果都是同樣的。

matrix3d

matrix3d,顧名思義,表明 3 維變換,它是一個 4 階矩陣,須要 16 個數字來表示。

\begin{aligned}
\begin{pmatrix}
x0 & y0 & z0 & tx \\\
x1 & y1 & z1 & ty \\\
x2 & y2 & z2 & tz \\\
0 & 0 & 0 & 1
\end{pmatrix}
\end{aligned}

能夠看到,每個基向量都增長了一個 Z 軸方向份量 x二、y二、z二、tz。

事實上,3D 的基本變換均可以用 matrix 或者 matrix3d 來表示,可是一些複雜變換隻能使用 matrix 或者 matrix3d 來實現。

matrix 對咱們來講有什麼用呢?

這是一個很關鍵的問題,你們會以爲,基本變換的用法更容易理解,更方便書寫,matrix 須要的參數太多,並且參數值須要計算,更糟糕的是,咱們每每不知道怎麼計算,那 matrix 有什麼用呢?

這是個好問題,但我想說的是 matrix 能完成基本變換不能完成的變換,能作出基本變換完成不了的效果。

這時你就該考慮使用 matrix 了。

緊跟而來的問題是,matrix 如何求得呢?

咱們前面章節講述了 matrix 矩陣的求法,一旦你肯定了須要的變換,你就能夠計算變換後的基向量,而後將基向量的各個份量代入矩陣的各個位置便可求出變換矩陣,有了變換矩陣,也就有了 matrix 所須要的各個元素,將矩陣轉化成 matrix 或者 matrix3d 所須要的字符串就垂手可得了。

  • 鏡像效果

鏡像效果,採用基本變換是實現不了的,只能藉助於矩陣。 好比左右鏡像,左右鏡像無非就是 Y 軸基向量不變,X 軸座標對調,原座標與新座標關係以下:

\begin{aligned}
x^{'} &= -x \\\
y^{'} &= y
\end{aligned}

因此有:

\begin{aligned}
x0 &= -1 \\\
x1 &= 0 \\\
y0 &= 0 \\\
y1 &= 1
\end{aligned}

將這些值,代入 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 開發上更近一步~

相關文章
相關標籤/搜索