搜尋

transform 3D 轉換與透視

熟悉 CSS 的 transform 樣式屬性之後,就可以進一步透過 perspective、perspective-origin、transform-style、transform-origin 和 backface-visibility 等樣式屬性,實作 3D 變形效果,這篇教學會介紹如何實作這些非常有趣的 3D 轉換和透視。

請先閱讀「transform 轉換函式」。

快速導覽:

CSS 教學 - transform 3D 轉換與透視

定義 3D 場景與攝影機

如果要使用 transform 實作「3D」轉換,則必須要定義和 3D 息息相關的「場景與攝影機」,在 3D 繪圖領域裡,都會透過特定的「攝影機」拍攝最後所呈現的結果,3D 繪圖的攝影機和一般的相機一樣都可以設定「焦距」,焦距越短發生的透視變形就越大,焦距越長透視變形就越小。

CSS 教學 - transform 3D 轉換與透視 - 焦距越短發生的透視變形就越大,焦距越長透視變形就越小

使用 CSS transform 產生 3D 轉換採用 perspective 代替「焦距」呈現畫面perspectiveW3C 的定義 中表示「相機和元素」的距離的距離,也可稱為「透視距離」,以下圖為例,d 為透視距離,Z 為元素本身在 Z 軸的位移,透過 d 和 Z 計算出最後元素呈現在網頁中的長相。

CSS 教學 - transform 變形轉換 - W3C perspective() 透視距離

由於網頁中不存在「攝影機」元素,必須透網頁結構的方式,產生類似攝影機元素,下方是建構 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 效果。

線上展示:https://codepen.io/oxxo/pen/LEPppxg

<!-- 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>

CSS 教學 - transform 3D 轉換與透視 - perspective 透視距離

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 對於透視的影響,可以注意當透視中心位於元素的背面,看到的文字會是相反的。

線上展示:https://codepen.io/oxxo/pen/gbYaamY

<!-- 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>

CSS 教學 - transform 3D 轉換與透視 - perspective-origin 透視中心點

CSS 教學 - transform 3D 轉換與透視 - perspective-origin 透視中心點

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 模樣。

線上展示:https://codepen.io/oxxo/pen/bNbVVqE

<!-- 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>

CSS 教學 - transform 3D 轉換與透視 - transform-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 的效果,可以注意旋轉的位置,以及當透視中心位於元素的背面,看到的文字會是相反的。

線上展示:https://codepen.io/oxxo/pen/raBOOyY

<!-- 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>

CSS 教學 - transform 3D 轉換與透視 - transform-origin 轉換中心點

CSS 教學 - transform 3D 轉換與透視 - transform-origin 轉換中心點

backface-visibility 背面可視

backface-visibility 可以設定元素翻轉後「是否可以看見背面」,沒有繼承特性,預設值為 visible 表示可以看見背面,設定為 hidden 表示看不見背面 ( 這個元素翻轉成背面後就會消失 ),下方範例會用兩組 div,呈現「看見背面」和「看不見背面」的效果。

線上展示:https://codepen.io/oxxo/pen/LEPppWg

<!-- 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>

CSS 教學 - transform 3D 轉換與透視 - backface-visibility 背面可視

小結

這篇教學所介紹的 3D 轉換樣式,是操作 transform 的常見做法,通常第一次操作時會有點不容易理解,但只要熟悉攝影機、場景和元素的關聯性,就可以很輕鬆的入門 transform 3D 轉換與透視。

意見回饋

如果有任何建議或問題,可傳送「意見表單」給我,謝謝~

CSS 教學

基本介紹

認識 CSS 開始使用 CSS CSS 語法規則 CSS 命名原則

CSS 選擇器

認識 CSS 選擇器 優先順序 ( 權重 ) 樣式繼承與聯集 元素選擇器 ID 和 Class 選擇器 屬性選擇器 文件結構選擇器 虛擬類別選擇器 ( 結構 ) 虛擬類別選擇器 ( 類型 ) 虛擬類別選擇器 ( 輸入 ) 虛擬類別選擇器 ( 行為 ) 虛擬類別選擇器 ( 超連結 ) 虛擬類別選擇器 ( 邏輯 ) 虛擬類別選擇器 ( 其他 ) 虛擬元素選擇器 群組與組合選擇器

數值與單位

關鍵字與文字數值 長度與角度單位 顏色單位 位置名稱與時間單位

變數與內容函式

變數 數學計算 文字與清單計數 生成內容與引號

顏色與濾鏡

色彩模型 漸層色 影像濾鏡

文字樣式

使用通用字型 使用外部字型 @font-face 定義字型 文字尺寸 常用文字樣式 文字換行 文字空白與 Tab 大小 文字行高與縮排 文字水平與垂直對齊 文字書寫方向 特殊文字樣式

元素容器

容器顯示類型 ( display ) 元素 display 對照表 盒子模型 ( Box Model ) 寬度與高度 內邊距 ( padding ) 外邊界 ( margin ) 邊框 ( border ) 邊框圓角 影像邊框 輪廓 ( outline ) 可見性與透明度 內容溢出與裁切

背景與陰影

背景顏色 背景圖 ( 定位、尺寸 ) 背景圖 ( 固定、重複 ) 背景圖 ( 多重背景、混合 ) 背景縮寫 ( background ) 容器陰影 ( box-shadow )

清單與表格

清單樣式 清單計數器 定義計數規則 表格基本樣式 表格邊框樣式 表格內容寬度與對齊

基本排版與定位

元素排版方式 浮動 ( float ) 浮動形狀 定位 ( position )

Flexbox 彈性排版

Flexbox 彈性盒子 Flexbox 對齊方式 Flexbox 彈性伸縮

Grid 網格排版

Grid 網格容器與格線 Grid 網格空間與命名 Grid 網格流向與間距 Grid 排列網格項目 Grid 項目對齊與順序

轉場與動畫

轉場 ( transition ) 轉場觸發事件 動畫 ( animation ) 動畫觸發事件 多重動畫的權重與順序

變形、裁切與遮罩

裁切路徑 ( clip-path ) 影像遮罩 ( mask ) 物件填滿方式與定位 轉換函式 ( transform ) 3D 轉換與透視