Skip to content

CSS 3D 變形:perspective、三軸旋轉與翻牌效果

CSS 3D 變形:perspective 與三軸旋轉

transform 除了 2D 變形,也能讓元素在三維空間中旋轉與位移。與 2D 最大的差別是多了 Z 軸,也就是螢幕的縱深方向。

方向
X水平,向右為正
Y垂直,向下為正
Z縱深,朝向使用者為正

perspective:立體感的關鍵

perspective 定義觀看者與畫面的距離。沒有它,3D 旋轉看起來只會像被壓扁:數值越小,透視越誇張;越大則越接近平行投影。

css
/* 寫在父層,子元素共用同一個消失點 */
.scene {
  perspective: 800px;
}

.card {
  transform: rotateY(45deg);
}

也可以寫成函式,直接放進元素自己的 transform

css
.card {
  transform: perspective(800px) rotateY(45deg);
}

差別在於:寫成 屬性 時整組子元素共用一個消失點,看起來像在同一個場景;寫成 函式 時每個元素各有各的消失點。

perspective-origin 則可以移動消失點的位置:

css
.scene {
  perspective: 800px;
  perspective-origin: 50% 20%;
}

三軸旋轉

css
.a { transform: rotateX(45deg); }  /* 上下翻 */
.b { transform: rotateY(45deg); }  /* 左右翻 */
.c { transform: rotateZ(45deg); }  /* 平面旋轉,等同 2D 的 rotate */
.d { transform: rotate3d(1, 1, 0, 45deg); }  /* 指定任意軸向 */

旋轉的中心點由 transform-origin 決定,3D 時它可以有第三個值來指定 Z 軸位置。

平移與縮放

css
.a { transform: translateZ(100px); }             /* 朝使用者靠近 */
.b { transform: translate3d(10px, 20px, 30px); } /* 一次寫齊三軸 */
.c { transform: scaleZ(2); }
.d { transform: scale3d(1, 1, 2); }

小提醒

translateZ 要搭配 perspective 才看得出遠近;沒有透視距離時,元素只會維持原樣。

transform-style

父層預設會把子元素壓平在同一個平面,要讓子元素保有各自的三維位置,必須設成 preserve-3d

說明
flat子元素壓平在父層的平面上(預設)
preserve-3d子元素保留自己的 3D 位置

backface-visibility

元素轉過 90 度之後會看到背面,這個屬性決定背面要不要顯示:

說明
visible顯示背面(預設)
hidden背面朝向使用者時隱藏

實作:翻牌效果

把上面幾個屬性組合起來,就是最經典的翻牌:

html
<div class="scene">
  <div class="card">
    <div class="face front">正面</div>
    <div class="face back">背面</div>
  </div>
</div>
css
.scene {
  perspective: 1000px;
}

.card {
  position: relative;
  width: 200px;
  height: 280px;
  transform-style: preserve-3d;
  transition: transform 0.6s;
}

.scene:hover .card {
  transform: rotateY(180deg);
}

.face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
}

.back {
  transform: rotateY(180deg);
}

三個關鍵缺一不可:父層的 perspective 提供透視、卡片的 preserve-3d 讓兩面各自存在、兩面的 backface-visibility 藏起背面。過場則交給 transition

小提醒

transform 的動畫由合成層處理,不會觸發重排,是效能最好的動畫屬性之一。但每個 3D 元素都會建立合成層,數量太多一樣會吃記憶體,別無限制地套用。另外請記得處理 prefers-reduced-motion,尊重使用者關閉動態效果的設定。

常見問題

為什麼旋轉之後看起來沒有立體感?

因為少了 perspective。沒有透視距離時,遠近的邊長不會有差別,rotateY 看起來就只是被水平壓扁。把 perspective 加在父層容器上,立體感才會出現。

perspective 屬性和 perspective 函式差在哪?

屬性寫在父層,子元素會共用同一個消失點,多個元素排在一起時看起來像在同一個場景;函式寫在元素自己的 transform 裡,每個元素各有各的消失點。要讓一組元素透視一致就用屬性。

子元素的 3D 效果為什麼消失了?

父層的 transform-style 預設是 flat,會把子元素壓平在同一個平面。要讓子元素保有各自的三維位置,父層必須設成 preserve-3d

3D 變形會影響效能嗎?

transformopacity 的動畫由合成層處理,不會觸發重排,是效能最好的動畫屬性之一。但每個 3D 元素都會建立合成層,數量太多同樣會吃記憶體,別無限制地套用。

延伸閱讀