transform 3D 轉換與透視
熟悉 CSS 的 transform 樣式屬性之後,就可以進一步透過 perspective、perspective-origin、transform-style、transform-origin 和 backface-visibility 等樣式屬性,實作 3D 變形效果,這篇教學會介紹如何實作這些非常有趣的 3D 轉換和透視。
請先閱讀「transform 轉換函式」。
快速導覽:
定義 3D 場景與攝影機
如果要使用 transform
實作「3D」轉換,則必須要定義和 3D 息息相關的「場景與攝影機」,在 3D 繪圖領域裡,都會透過特定的「攝影機」拍攝最後所呈現的結果,3D 繪圖的攝影機和一般的相機一樣都可以設定「焦距」,焦距越短發生的透視變形就越大,焦距越長透視變形就越小。
使用 CSS transform
產生 3D 轉換採用 perspective
代替「焦距」呈現畫面,perspective
在 W3C 的定義 中表示「相機和元素」的距離的距離,也可稱為「透視距離」,以下圖為例,d 為透視距離,Z 為元素本身在 Z 軸的位移,透過 d 和 Z 計算出最後元素呈現在網頁中的長相。
由於網頁中不存在「攝影機」元素,必須透網頁結構的方式,產生類似攝影機元素,下方是建構 CSS 3D 效果常用的網頁結構,將「要變成 3D」的元素放在類似「攝影機」的元素裡,甚至可以額外使用一個「場景」元素放置所有 3D 的子元素,如此一來就可以透過攝影機、場景和元素的操作,實現 CSS 3D 效果。
<!-- 攝影機 + 元素 -->
<div class="camera">
<div>3D 元素</div>
</div>
<!-- 攝影機 + 場景 + 多個元素 -->
<div class="camera">
<div class="space">
<div>3D 元素</div>
<div>3D 元素</div>
<div>3D 元素</div>
</div>
</div>
perspective 透視距離
理解 CSS 的 3D 架構後,接著就要進行「攝影機」的相關設定,雖然 CSS 的轉換沒有辦法設定「焦距」,但透過 perspective
仍然能模擬出不同焦距的「透視變形」,perspective
表示「透視距離」的樣式屬性,沒有繼承特性,通常會套用在「攝影機」元素,數值越小就等同焦距越短 ( 越廣角、透視變形多 ),數值越大則等同焦距越長 ( 越望遠,透視變形少 ),最小值為 1px,如果小於 1px 就會當作 1px,如果不設定數值則會使用預設值 none,表示從無限遠觀察,元素不會發生任何透視變形。
下方範例呈現四種不同的 perspective
效果。
<!-- HTML 程式碼-->
<div class="a camera"><div>oxxo</div></div>
<div class="b camera"><div>oxxo</div></div>
<div class="c camera"><div>oxxo</div></div>
<div class="d camera"><div>oxxo</div></div>
<!-- CSS 程式碼 -->
<style>
div {
width: 100px;
height: 100px;
border: 1px solid #000;
color: white;
}
div[class] {margin: 20px 150px;}
.camera div{transform: rotateX(45deg);}
.a div {background: #f90;}
.b {perspective: 200px;}
.b div {background: #09f;}
.c {perspective: 100px;}
.c div {background: #0a0;}
.d {perspective: 50px;}
.d div {background: #f55;}
</style>
perspective-origin 透視中心點
perspective-origin
表示「透視中心點」樣式屬性,也可以理解為「攝影機中心點」,具有 x 和 y 兩個屬性值,分別代表相對於父元素的 x 座標和 y 座標,0 0
表示位於父元素的左上角,預設值為 50% 50%
,沒有繼承特性,也可使用下列的位置表示法:
位置關鍵字 | 等同數值 |
---|---|
left | 0、0% ( 水平方向 ) |
right | 100% (水平方向 ) |
center | 50% ( 水平或垂直方向 ) |
top | 0、0% ( 垂直方向 ) |
bottom | 100% (垂直方向 ) |
下方範例呈現不同 perspective-origin
對於透視的影響,可以注意當透視中心位於元素的背面,看到的文字會是相反的。
<!-- HTML 程式碼-->
<div class="a camera"><div>hello<br/>OxxO<br/>12345</div></div>
<div class="b camera"><div>hello<br/>OxxO<br/>12345</div></div>
<div class="c camera"><div>hello<br/>OxxO<br/>12345</div></div>
<div class="d camera"><div>hello<br/>OxxO<br/>12345</div></div>
<div class="e camera"><div>hello<br/>OxxO<br/>12345</div></div>
<div class="f camera"><div>hello<br/>OxxO<br/>12345</div></div>
<!-- CSS 程式碼 -->
<style>
div {
width: 100px;
height: 100px;
border: 1px solid #000;
color: white;
font-size: 24px;
}
div[class] {margin: 20px 150px;}
.camera {perspective: 100px;}
.camera div{transform: rotateX(90deg);}
.camera:nth-of-type(n+4) div{transform: rotateY(90deg);}
.a {perspective-origin: 50% 0;}
.a div {background: #f55;}
.b {perspective-origin: 50% 50%;}
.b div {background: #09f;}
.c {perspective-origin: 50% bottom;}
.c div {background: #0a0;}
.d {perspective-origin: left center;}
.d div {background: #f90;}
.e {perspective-origin: 50% 50%;}
.e div {background: #0aa;}
.f {perspective-origin: 100% 50%;}
.f div {background: #a0a;}
</style>
transform-style 變形轉換模式
操作元素轉換時,如果只有單一個元素往往很好處理,但如果有「多個元素」,就可能會發生「互相重疊」或「各自轉換」等不合理的 3D 呈現方式,這時就能透過 transform-style
設定「變形轉換模式」,讓空間裡的「子元素」彼此獨立在各自的空間,或整合為同一個 3D 空間,這個樣式屬性沒有繼承特性,有下列兩種屬性值:
屬性值 | 說明 |
---|---|
flat | 所有子元素位於各自的空間,使用原本元素 HTML z-index 排列 ( 預設值 )。 |
preserve-3d | 所有子元素位於同一個 3D 空間,使用座標系統位置排列。 |
下方範例有兩組 div
,都使用「camera、space 和元素」的架構定義出攝影機、空間和元素,但第一組 div 因為沒有設定 transform-style: preserve-3d
,造成子元素各自獨立在自己的空間中,轉換後就發生不合理的 3D 現象,而第二組 div 因為將所有子元素整合到同一個 3D 空間,就會根據空間中的位置排列組合,變成合理的 3D 模樣。
<!-- HTML 程式碼-->
<div class="camera">
<div>
<div class="a">oxxo</div>
<div class="b">oxxo</div>
<div class="c">oxxo</div>
</div>
</div>
<div class="camera">
<div class="space"> <!-- 有加上 space 類別 -->
<div class="a">oxxo</div>
<div class="b">oxxo</div>
<div class="c">oxxo</div>
</div>
</div>
<!-- CSS 程式碼 -->
<style>
div {
width: 100px;
height: 100px;
position: relative;
}
.camera {
perspective: 200px;
margin: 80px;
}
.space {transform-style: preserve-3d;}
.a, .b, .c{
position: absolute;
width: 100px;
height: 100px;
color: white;
}
.a {
transform: translateZ(10px);
background: #09f;
}
.b {
transform: rotateX(75deg);
background: #f55;
}
.c {
transform: rotateY(75deg);
background: #f90;
}
</style>
transform-origin 轉換中心點
transform-origin
表示元素轉換時的「中心點」,沒有繼承特性,具有「x y
」或「x y z
」兩種寫法,分別表示要參考的 xyz 三軸的位置,z 軸預設值為 0,xy 軸預設值為 50%,xy 軸也可使用下列的位置表示法 ( 不支援 z 軸 )
位置關鍵字 | 等同數值 |
---|---|
left | 0、0% ( 水平方向 ) |
right | 100% (水平方向 ) |
center | 50% ( 水平或垂直方向 ) |
top | 0、0% ( 垂直方向 ) |
bottom | 100% (垂直方向 ) |
下方範例呈現不同 transform-origin
的效果,可以注意旋轉的位置,以及當透視中心位於元素的背面,看到的文字會是相反的。
<!-- HTML 程式碼-->
<div class="a camera"><div>hello<br/>OxxO<br/>12345</div></div>
<div class="b camera"><div>hello<br/>OxxO<br/>12345</div></div>
<div class="c camera"><div>hello<br/>OxxO<br/>12345</div></div>
<div class="d camera"><div>hello<br/>OxxO<br/>12345</div></div>
<div class="e camera"><div>hello<br/>OxxO<br/>12345</div></div>
<div class="f camera"><div>hello<br/>OxxO<br/>12345</div></div>
<!-- CSS 程式碼 -->
<style>
div {
width: 100px;
height: 100px;
border: 1px solid #000;
color: white;
font-size: 24px;
}
div[class]:first-child{margin: 100px 50px 30px;}
div[class] {margin: 30px 50px;}
.camera {perspective: 200px;}
.camera div{transform: rotateX(90deg);}
.camera:nth-of-type(n+4) div{transform: rotateY(90deg);}
.a div {
background: #f55;
transform-origin: 0 0 0; /* 也可單獨寫 0 0 */
}
.b div {
background: #09f;
transform-origin: 0 50% 0; /* 也可單獨寫 0 50% */
}
.c div {
background: #0a0;
transform-origin: 0 100% 0; /* 也可單獨寫 0 100% */
}
.d div {
background: #f90;
transform-origin: 0 0 0; /* 也可單獨寫 0 0 */
}
.e div {
background: #0aa;
transform-origin: 50% 0 0; /* 也可單獨寫 50% 0 */
}
.f div {
background: #a0a;
transform-origin: 100% 0 0; /* 也可單獨寫 100% */
}
</style>
backface-visibility 背面可視
backface-visibility
可以設定元素翻轉後「是否可以看見背面」,沒有繼承特性,預設值為 visible
表示可以看見背面,設定為 hidden
表示看不見背面 ( 這個元素翻轉成背面後就會消失 ),下方範例會用兩組 div,呈現「看見背面」和「看不見背面」的效果。
<!-- HTML 程式碼-->
<div class="camera">
<div class="space">
<div class="a">oxxo<br>1</div>
<div class="b">oxxo<br>2</div>
<div class="c">oxxo<br>3</div>
<div class="d">oxxo<br>4</div>
<div class="e">oxxo<br>5</div>
<div class="f">oxxo<br>6</div>
</div>
</div>
<div class="camera">
<div class="space bv">
<div class="a">oxxo<br>1</div>
<div class="b">oxxo<br>2</div>
<div class="c">oxxo<br>3</div>
<div class="d">oxxo<br>4</div>
<div class="e">oxxo<br>5</div>
<div class="f">oxxo<br>6</div>
</div>
</div>
<!-- CSS 程式碼 -->
<style>
body {background: repeating-linear-gradient(white 0%,white 25px,black 25px,black 50px);}
div {
width: 100px;
height: 100px;
position: relative;
}
.camera {
perspective: 300px;
margin: 100px 50px;
float: left;
}
.space {
transform-style: preserve-3d;
transform: rotateY(45deg) rotateX(45deg);
}
.space div{
position: absolute;
width: 100px;
height: 100px;
border: 1px solid #000;
color: white;
font-size: 40px;
text-align: center;
line-height: 1;
opacity: .7;
}
.bv div {backface-visibility: hidden;}
.a {
transform: translateX(-50px) rotateY(-90deg);
background: #09f;
}
.b {
transform: translateX(50px) rotateY(90deg);
background: #f55;
}
.c {
transform: translateZ(-50px) rotateY(180deg);
background: #f90;
}
.d {
transform: translateZ(50px);
background: #0a0;
}
.e {
transform: translateY(50px) rotateX(-90deg);
background: #c0c;
}
.f {
transform: translateY(-50px) rotateX(90deg);
background: #666;
}
</style>
小結
這篇教學所介紹的 3D 轉換樣式,是操作 transform 的常見做法,通常第一次操作時會有點不容易理解,但只要熟悉攝影機、場景和元素的關聯性,就可以很輕鬆的入門 transform 3D 轉換與透視。
意見回饋
如果有任何建議或問題,可傳送「意見表單」給我,謝謝~