透過3D立方體深入理解perspective和translateZ的關係

前端小然子發表於2019-04-11

前言

對於js全景圖嚮往已久,貌似從16年開始看到的。當時自己還是一個小菜雞(雖然現在也不是大佬),由於工作原因前端的很多方面都沒有及時瞭解,現在惡補中。。。

準備工作

瞭解transform中的一些基本概念,比如:

rotate 旋轉

translate 定義 2D 轉換

translateZ 定義 3D 轉換

perspective 為 3D 轉換元素定義透視檢視。

transform-style: preserve-3d; 指定子元素定位在三維空間內。另外,該屬性是非繼承的。

要開始講咯~

有一個立方體的demo放在我的github中,如圖:

透過3D立方體深入理解perspective和translateZ的關係

demo連結在這裡

原始碼連結


立方體原理

立方體我們大家都知道,是由六個的正方形組成的正多面體。如下圖:

透過3D立方體深入理解perspective和translateZ的關係


全景圖

這裡我們用全景圖來舉例,幫助我們理解perspective和translateZ。

全景圖的組成方式有兩種,分別是 立方體、球體(稜柱),我們這裡使用的是立方體來舉例。

這裡我還沒有搞明白球體稜柱的區別, 所以等我理解以後,我們們後面再說


製作立方體

一、製作6個面

因為立方體是由6個正方形組成,所以我們先來製作它的組成部分,最後再進行組裝。

我們這裡用face來當做面,而top、bottom、left、right、after、first來分別代表上、下、左、右、前、後幾個面。

這裡我們的視角正對著first

我們定義了一個視窗stage、透視盒子ctx 和 立方體容器facelist。

我們先將視窗定義為800*800相對頁面垂直居中

我們先將立方體定義為寬高為800px; 接下來將每個面的寬高也定義為800px, 並給予每個面一個不同的顏色用以區分。

程式碼如下:

<!DOCTYPE html>
<html lang="zh">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>立方體全景</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    html,
    body {
      height: 100%;
      overflow: hidden;
    }
    
    .stage {
      width: 800px;
      height: 800px;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
    }
    
    .face {
      width: 800px;
      height: 800px;
    }
    
    .top {
      background: green;
    }

    .bottom {
      background: yellow;
    }

    .after {
      background: red;
    }

    .left {
      background: black;
    }

    .right {
      background: blue;
    }

    .first {
      background: blueviolet;
    }
  </style>
</head>

<body>
  <div class='stage'>
    <div class='ctx'>
      <div class='facelist'>
        <div class='face top'>1</div>
        <div class='face bottom'>2</div>
        <div class='face after'>3</div>
        <div class='face left'>4</div>
        <div class='face right'>5</div>
        <div class='face first'>6</div>
      </div>
    </div>
  </div>

  <script>

  </script>
</body>

</html>
複製程式碼

二、組合

到這裡我們立方體的6個面製作完成,但是他們是一次排列在頁面中,並不是我們想要的;

接下來讓我們將他們先放在一起。給face新增position: absolute;

這樣他們是重合在一起的,我們需要這樣操作:

1. 把它們旋轉到對應的角度
比如:

.top的面我們將它繞X軸旋轉90度;

.left的面我們將它繞Y軸旋轉90度;

···
複製程式碼

透過3D立方體深入理解perspective和translateZ的關係
圖片摘自思否

<!DOCTYPE html>
<html lang="zh">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>立方體全景</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    html,
    body {
      height: 100%;
      overflow: hidden;
    }

    .stage {
      width: 800px;
      height: 800px;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
    }

    .face {
      width: 800px;
      height: 800px;
      position: absolute;
    }

    .top {
      background: green;
      transform: rotateX(90deg);
    }

    .bottom {
      background: yellow;
      transform: rotateX(90deg);
    }

    .after {
      background: red;
      transform: rotateX(0deg);
    }

    .left {
      background: black;
      transform: rotateY(90deg);
    }

    .right {
      background: blue;
      transform: rotateY(90deg);
    }

    .first {
      background: blueviolet;
      transform: rotateY(0deg);
    }
  </style>
</head>

<body>
  <div class='stage'>
    <div class='ctx'>
      <div class='facelist'>
        <div class='face top'>1</div>
        <div class='face bottom'>2</div>
        <div class='face after'>3</div>
        <div class='face left'>4</div>
        <div class='face right'>5</div>
        <div class='face first'>6</div>
      </div>
    </div>
  </div>

  <script>

  </script>
</body>

</html>

複製程式碼

旋轉後的圖形 應該是這樣的

透過3D立方體深入理解perspective和translateZ的關係

這樣看起來好像什麼都沒有變化,我們來看看這個圖:

透過3D立方體深入理解perspective和translateZ的關係

把每個面都重疊在黑色的原點就是他的實際模樣,也就是說現在還不是一個立方體

因為他們的中心都是重合的,如何將它們變成立方體呢?看下面的步驟

2. 將他們按照對應的邊長通過translateZ推開
由於我們這裡是立方體,所以直接就是正方形的邊長  在這裡也就是±400px

給它們設定上對應的translateZ值,讓多個面往不同方向平移,直到組成一個完整的立方體。
複製程式碼
<!DOCTYPE html>
<html lang="zh">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>立方體全景</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    html,
    body {
      height: 100%;
      overflow: hidden;
    }

    .stage {
      width: 800px;
      height: 800px;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
    }

    .face {
      width: 800px;
      height: 800px;
      position: absolute;
    }

    .top {
      background: green;
      transform: rotateX(90deg) translateZ(400px);
    }

    .bottom {
      background: yellow;
      transform: rotateX(90deg) translateZ(-400px);
    }

    .after {
      background: red;
      transform: rotateX(0deg) translateZ(-400px);
    }

    .left {
      background: black;
      transform: rotateY(90deg) translateZ(-400px);
    }

    .right {
      background: blue;
      transform: rotateY(90deg) translateZ(400px);
    }

    .first {
      background: blueviolet;
      transform: rotateY(0deg) translateZ(400px);
    }
  </style>
</head>

<body>
  <div class='stage'>
    <div class='ctx'>
      <div class='facelist'>
        <div class='face top'>1</div>
        <div class='face bottom'>2</div>
        <div class='face after'>3</div>
        <div class='face left'>4</div>
        <div class='face right'>5</div>
        <div class='face first'>6</div>
      </div>
    </div>
  </div>

  <script>

  </script>
</body>

</html>
複製程式碼
3. 加上3D效果

我們給ctx盒子加上3D屬性 讓3D變得立體化

···
    .ctx {
      /* 3d視角 */
      transform-style: preserve-3d;
    }
···
複製程式碼
4.將我們的視角推到中心點

先調整stage的視距,將我們的眼睛(視角)調整到ctx的first面的位置

這個時候我們距離after面有800px的距離 離ctx的面有400px的距離

然後我們將ctx這個透視盒子向外推1/2width的距離 也就是400px,將我們的眼睛(視角)置於中心

不理解的童鞋 請注意看下面的單獨講解;

  .stage {
      width: 800px;
      height: 800px;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      /* 從何處(哪裡)檢視一個元素的角度 */
      /* 從當前視角到對應面的距離  在這裡是 stage到ctx的距離 */
      perspective: 400px;
      /* 調整角度 */
      /* perspective-origin: 50% 100%; */
    }

    .ctx {
      /* 3d視角 */
      transform-style: preserve-3d;

      /* 把視角推到中心 */
      transform: translateZ(400px) rotateY(0deg);
    }
複製程式碼

三、完事

這樣我們已經將一個3D立方體做出來了 並且把我們的視角放在了這個立方體內部的中心點。

我們可以通過 改變 perspective-origin 的值來觀察整個立方體

調整角度,在不同角度看到的立方體,有助於更快的理解perspective。

單獨講解perspective和translateZ

我呢找到了一個幫助我理解的圖片:

透過3D立方體深入理解perspective和translateZ的關係

這裡Z指的就是 translateZ ,d指的是 perspective

這裡我們要知道,Z軸向外為正值。

所以總結下就是:

  • perspective是指 從當前視角到所看平面的距離
  • translateZ指的是 從所看平面到推進視角之間的距離,大白話就是從當前距離 把你看的拉進或者拉遠的距離
  • 人的視角在3D投影效果中是 近大遠小

重中之重

理解 近大遠小眼睛與平面的透視關係 也就是上面那張圖!!!

結語

這些東西對於初學css 3D的人來說可能理解起來比較吃力;

對於大部分人來說,其實只靠讀文章是不可能完全理解的;

所以還有一句名言警句送給大家:

實踐是檢驗真理的唯一標準

希望大家可以一直以實踐為主、閱讀為輔,自己真正理解的才是自己的。謝謝!

相關文章