CSS 3D 變形:perspective、三軸旋轉與翻牌效果
transform 除了 2D 變形,也能讓元素在三維空間中旋轉與位移。與 2D 最大的差別是多了 Z 軸,也就是螢幕的縱深方向。
| 軸 | 方向 |
|---|---|
| X | 水平,向右為正 |
| Y | 垂直,向下為正 |
| Z | 縱深,朝向使用者為正 |
perspective:立體感的關鍵
perspective 定義觀看者與畫面的距離。沒有它,3D 旋轉看起來只會像被壓扁:數值越小,透視越誇張;越大則越接近平行投影。
/* 寫在父層,子元素共用同一個消失點 */
.scene {
perspective: 800px;
}
.card {
transform: rotateY(45deg);
}也可以寫成函式,直接放進元素自己的 transform:
.card {
transform: perspective(800px) rotateY(45deg);
}差別在於:寫成 屬性 時整組子元素共用一個消失點,看起來像在同一個場景;寫成 函式 時每個元素各有各的消失點。
perspective-origin 則可以移動消失點的位置:
.scene {
perspective: 800px;
perspective-origin: 50% 20%;
}三軸旋轉
.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 軸位置。
平移與縮放
.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 | 背面朝向使用者時隱藏 |
實作:翻牌效果
把上面幾個屬性組合起來,就是最經典的翻牌:
<div class="scene">
<div class="card">
<div class="face front">正面</div>
<div class="face back">背面</div>
</div>
</div>.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 變形會影響效能嗎?
transform 與 opacity 的動畫由合成層處理,不會觸發重排,是效能最好的動畫屬性之一。但每個 3D 元素都會建立合成層,數量太多同樣會吃記憶體,別無限制地套用。