CSS3 之 3D 變換

AJie發表於2019-03-29

我們大多數同學接觸的 CSS3: transform 屬性值一般都是 2D 變換,比如 :

  • 平移操作:translateX(),translateY(),亦或是 translate(x,y)。
  • 旋轉操作:rotate()

今天我們就來學習並且深入理解 CSS3 中的 3D 變換!

CSS3 中 3D 的一些基本概念

在討論 3D 之前我們先要對一些基本概念做一些理解。

3D 座標系

話不多說先上圖:

CSS3 之 3D 變換

我們都知道,在瀏覽器中,是以左上角的頂點作為原點,向右、向下為正方向的的視窗。其中橫向為 X 軸,縱向為 Y 軸。而對於三維空間中的 Z 軸,自然是指垂直與該 X 軸與 Y 軸所構建的平面且穿過原點的直線。對於二維檢視而言由於 X 軸與 Y 軸始終確定,所以 Z 軸也是確定的:即穿過視窗左上角且與螢幕垂直的直線。螢幕的前方為正方向。

但是這裡容易產生一個誤解誤以為三維空間中的 Z 軸也是固定的,和二維檢視的 Z 軸是同一根軸線。

Z 軸真正的含義是垂直與 X 軸與 Y 軸所構成的面且穿過原點的直線。那麼當這個面傳送旋轉或平移的時候,Z 軸也是隨之而變的!這裡我用一張圖片進行說明:

CSS3 之 3D 變換

這裡的每根紅軸為 Z 軸,分別垂直於各自的面!

perspective 屬性

perspective 的字面意思如下:

CSS3 之 3D 變換

近大遠小在二維平面上是無法體現的,但是在三維空間中離我們眼球越近的事物是越來越大的。

而 perspective 這個屬性定義的其實是我們離畫面假定的初始畫素距離。

CSS3 之 3D 變換

這張圖費盡了畢生的繪畫天賦。。。

加入我們定義了這麼一個屬性:

perspective: 800px;
複製程式碼

那麼我們就假定我們的眼球這一視點到該屬性所在的元素之間的距離是 800px。(有一個前提:該元素 X 軸與 Y 軸所構成的平面與視窗平面平行。)實際上在這裡人眼到 X 軸與 Y 軸所構成的平面的距離就是 800px(Z 軸方向)。

如果平面發生了旋轉,視點也是會跟著旋轉的

那麼有同學就會問了:這個屬性到底有啥用?先別急嘛!

如果這個元素之後通過三維變換離我們的距離變近了,那麼他們呈現在我們眼前的大小將變大,反之變小。(近大遠小)這一特性並不是表面看的那麼簡單,就拿之前我們假設定義的那個屬性和那張我畫的圖說:意思也就是我的眼球和我看到的元素之間有 800px 的長度,這時候我讓該元素沿著 Z 軸的正方項平移 801px 的距離。平移之後你會發現你看不到這個元素了,究其原因是因為這個圖片在三維空間中,已經跑到你眼球的後面了!所以你當然看不到了。

transform-style: preserve-3d;

這一屬性值的設定就像是一個 3D 控制元件的宣告,告訴瀏覽器的渲染引擎接下來的子元素需要按照 3D 模式進行渲染!(預設 2D 模式)所以該屬性應該設定在父容器之中

注意該元素不具有繼承性!!但是該元素不具有繼承性!!但是該元素不具有繼承性!!(重要的事情說三遍。。。)

perspective-origin 屬性

引用 W3C 的話:perspective-origin 屬性定義 3D 元素所基於的 X 軸和 Y 軸。該屬性允許您改變 3D 元素的底部位置。

當為元素定義 perspective-origin 屬性時,其子元素會獲得透視效果,而不是元素本身。

註釋:該屬性必須與 perspective 屬性一同使用,而且隻影響 3D 轉換元素

用白話翻譯一下,其實也就是:透視點的位置。

CSS3 之 3D 變換

透視點的預設值為 50% 50%,也不具備繼承性。該屬性必須與 perspective 屬性一同使用,而且隻影響 3D 轉換元素。

translateZ(), rotateX(), rotateY(), rotateZ()

有了前面的知識作為鋪墊,平移、旋轉理解起來也就容易了。

  • translateZ():沿著 Z 軸平移
  • rotateX():沿著 X 軸旋轉
  • rotateY():沿著 Y 軸旋轉
  • rotateZ():沿著 Z 軸旋轉

**旋轉的正方向:**網頁視窗都是左手座標系,所以應當用左手判斷旋轉的正方向。

**具體判斷方法:**左手握住拳頭,拇指只想旋轉軸的正方向,四指彎曲的方向就是旋轉的正方向。

CSS3 之 3D 變換

CSS3 中 3D 的一些案例

魔方

效果圖:

CSS3 之 3D 變換

要實現一個正方體可以通過六個面旋轉後沿著各自的 Z 軸進行平移獲得

<div class="cube">
  <div class="cover cover_front"></div>
  <div class="cover cover_back"></div>
  <div class="cover cover_left"></div>
  <div class="cover cover_right"></div>
  <div class="cover cover_top"></div>
  <div class="cover cover_bottom"></div>
</div>
複製程式碼
.cube {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) rotate(0deg);
  width: 250px;
  height: 250px;
  transform-style: preserve-3d;
  perspective: 1000px;
  animation: rotate 5s linear infinite;
}
@keyframes rotate {
  0% {
    transform: translate(-50%, -50%) rotate(0deg);
  }
  100% {
    transform: translate(-50%, -50%) rotate(360deg);
  }
}
.cover {
  position: absolute;
  width: 250px;
  height: 250px;
  box-shadow: 0 0 25px 5px #ffffff inset;
}
.cover_front {
  transform: rotateX(45deg) rotateY(45deg) translateZ(125px);
}
.cover_back {
  transform: rotateX(45deg) rotateY(45deg) translateZ(-125px);
}
.cover_left {
  transform: rotateX(45deg) rotateY(135deg) translateZ(125px);
}
.cover_right {
  transform: rotateX(45deg) rotateY(-45deg) translateZ(125px);
}
.cover_top {
  transform: rotateX(135deg) rotateZ(45deg) translateZ(125px);
}
.cover_bottom {
  transform: rotateX(-45deg) rotateZ(-45deg) translateZ(125px);
}
複製程式碼

炫彩發光球

效果圖:

CSS3 之 3D 變換

通過給每個色斑不同的旋轉角度,並且加以動畫的形式體現。

<div class="ball">
  .spot*100
</div>
複製程式碼

由於 html 頁面中設定了 100 個類名為 spot 的 div,而且每個 sopt 初始的樣式都是不同的,且運動軌跡也不一樣,所以這裡用 css 預編譯語言 stylus 來進行實現。

vendors = official

random(min,max)
  return floor(math(0, 'random')*(max - min + 1) + min)

*
  margin 0
  padding 0

html
body
  width 100vw
  height 100vh
  background-color #000000

.ball
  width 10px
  height 10px
  top 50%
  left 50%
  transform: translate(-50%, -50%)
  position relative
  transform-style: preserve-3d
  perspective: 1000px

.spot
  width 10px
  height 10px
  position absolute
  border-radius: 50%

animation(n)
  animation twinkle+n 20s linear infinite

getdifference()
  transform: rotateX(unit(random(0,360),'deg')) rotateY(unit(random(0,360),'deg')) translateZ(50px)
  background-color rgba(random(0,255),random(0,255),random(0,255),random(0,1))

for num in 1..100
  .spot:nth-child({num})
    $xDeg = random(0,360)
    $yDeg = random(0,360)
    $red = random(0,255)
    $green = random(0,255)
    $blue = random(0,255)
    $alpha = random(0,1)
    transform: rotateX(unit($xDeg,'deg')) rotateY(unit($yDeg,'deg')) translateZ(50px)
    background-color rgba($red,$green,$blue,$alpha)
    animation num
    @keyframes twinkle{num}
      20%
        getdifference()
      40%
        getdifference()
      60%
        getdifference()
      80%
        getdifference()
      100%
        transform: rotateX(unit($xDeg,'deg')) rotateY(unit($yDeg,'deg')) translateZ(50px)
        background-color rgba($red,$green,$blue,$alpha)
複製程式碼

筆者專門在 github 上建立了一個倉庫,用於記錄平時學習全棧開發中的技巧、難點、易錯點,歡迎大家點選下方連結瀏覽。如果覺得還不錯,就請給個小星星吧!?


2019/03/30

AJie

相關文章