Learning Objectives
- 用 transition 設定要變化的屬性、時間、速度曲線與延遲,讓樣式改變時平順過渡
- 用 transform 的 translate、scale、rotate 移動、縮放、旋轉元素,而不影響其他元素的位置
- 用 @keyframes 與 animation 寫出會自己播放的動畫,並說明為什麼優先對 transform 與 opacity 做動畫
- 用 prefers-reduced-motion 尊重使用者「減少動態」的系統設定
- 用 CSS 自訂屬性(變數)與 var() 管理顏色,並用 prefers-color-scheme 做出深色模式
動態是用來說明變化的
滑鼠移到按鈕上,顏色「啪」一聲換掉,和在 0.2 秒內慢慢變深,給人的感覺不一樣: 後者讓你看得出「這個東西有反應,而且是從原來的樣子變過來的」。 選單滑出來、卡片展開、資料載入中的小點點,都是用動態告訴使用者畫面上發生了什麼變化。
這一課學三件事:
| 工具 | 用途 | 什麼時候播放 |
|---|---|---|
transition(轉場) |
屬性值改變時,在一段時間內慢慢過渡 | 樣式改變時(例如 :hover、加上 class) |
@keyframes + animation(動畫) |
自己定義好幾個關鍵畫面,照順序播放 | 一載入就播,可以重複 |
| 自訂屬性(變數) | 把顏色等數值取個名字,集中管理 | 不是動態,但讓「整頁換色」變得容易 |
動態用得好,畫面比較好懂;用太多會讓人分心,對有前庭疾患的人甚至會造成頭暈不適。 所以這一課也會教你怎麼尊重使用者「減少動態」的設定。
transition:讓變化有過程
transition 寫在元素平常的樣式上,說明「哪個屬性改變時,要花多久過渡」:
.join {
background-color: #3C6F9A;
transition: background-color 0.2s ease-out 0s;
/* 要過渡的屬性 時間 速度曲線 延遲 */
}
.join:hover {
background-color: #1f4a6e; /* 滑鼠移上來時,顏色在 0.2 秒內慢慢變深 */
}
| 部分 | 對應的單一屬性 | 說明 |
|---|---|---|
| 屬性 | transition-property |
例如 background-color、transform;all 表示全部(方便但容易讓不想動的東西也動) |
| 時間 | transition-duration |
0.2s 或 200ms;介面上的小變化通常在零點幾秒內 |
| 速度曲線 | transition-timing-function |
過程中的快慢分配,見下一段 |
| 延遲 | transition-delay |
等多久才開始 |
把 transition 寫在 .join 而不是 .join:hover 上,滑鼠移開時顏色才會同樣慢慢變回去。
要讓兩個屬性各有不同的時間,用逗號分開:transition: background-color 0.2s, transform 0.3s;。
速度曲線(timing function)決定「同樣花 0.3 秒,哪一段快、哪一段慢」:
| 值 | 過程 |
|---|---|
linear |
從頭到尾等速 |
ease(預設) |
開始稍快、結尾慢下來 |
ease-in |
慢慢起步、越來越快 |
ease-out |
一開始快、最後慢慢停下 |
ease-in-out |
慢慢起步、中間快、慢慢停下 |
cubic-bezier(x1, y1, x2, y2) |
用兩個控制點自訂曲線;y 超過 1 會「衝過頭再彈回」 |
下面的工具讓你同時比較幾種曲線。
transform:移動、縮放、旋轉
transform 改變元素畫出來的樣子,但不改變它在版面裡佔的位置,所以旁邊的元素不會被推開:
| 寫法 | 效果 |
|---|---|
transform: translateX(6px) |
往右移 6px(translateY 上下、translate(x, y) 兩個方向) |
transform: scale(1.1) |
放大到 1.1 倍(scale(0.95) 縮小,常用在按下按鈕的回饋) |
transform: rotate(90deg) |
順時針轉 90 度(展開選單時的箭頭) |
transform: translateY(-2px) scale(1.05) |
用空白連寫好幾個,依序套用 |
transform 對 display: inline 的元素(例如一般的 span)沒有效果,要先改成 inline-block 或 block。
為什麼動畫優先用 transform 和 opacity?
瀏覽器把畫面畫出來大致分成幾個階段:算樣式、排版(layout,決定每個元素的位置與大小)、繪製(paint)、合成(composite)。
改 width、top、margin 會讓瀏覽器每一格畫面都重新排版,元素一多就容易卡頓;
transform 和 opacity 通常只需要在最後的合成階段處理,不必重新排版,播放起來比較順。
| 想做的效果 | 建議寫法 | 避免的寫法 |
|---|---|---|
| 往右移動 | transform: translateX(…) |
改 left 或 margin-left |
| 放大 | transform: scale(…) |
改 width、height |
| 淡入淡出 | opacity |
改 display(display 不會過渡) |
@keyframes 與 animation
transition 要等樣式改變才播放;動畫可以一載入就自己播,還能重複。分兩步:
1. 用 @keyframes 定義關鍵畫面(取一個名字):
@keyframes pulse {
from { opacity: 0.3; transform: scale(0.8); } /* 也可以寫 0% */
to { opacity: 1; transform: scale(1); } /* 也可以寫 100% */
}
中間還可以加 50% { … } 這類百分比的畫面。
2. 用 animation 套用到元素上:
.dot {
animation: pulse 0.8s ease-in-out infinite alternate;
/* 名字 時間 速度曲線 重複次數 方向 */
}
.dot:nth-child(2) { animation-delay: 0.2s; } /* 第二個點晚一點開始 */
| 單一屬性 | 常用值 |
|---|---|
animation-name |
@keyframes 的名字 |
animation-duration |
播一次要多久 |
animation-iteration-count |
3、infinite(一直重複) |
animation-direction |
alternate:去程播完播回程,來回擺動 |
animation-delay |
延遲開始 |
animation-fill-mode |
forwards:播完停在最後一格 |
尊重「減少動態」的設定:Windows、macOS、iOS、Android 都有「減少動態」或「移除動畫」的系統設定,
CSS 用媒體查詢 prefers-reduced-motion 讀得到。動畫只是裝飾、不是內容時,在這個設定下把它關掉或換成淡入淡出:
@media (prefers-reduced-motion: reduce) {
.dot { animation: none; }
}
「載入中」這類狀態,動畫關掉之後仍然要有文字說明(例如旁邊的「資料載入中」),資訊才不會跟著消失。
小測驗:轉場與動畫
下面的寫法,滑鼠移開按鈕時會怎樣?
.btn { background-color: #3C6F9A; }
.btn:hover { background-color: #1f4a6e; transition: background-color 0.3s; }
想讓卡片從畫面左邊滑進來,下面哪一種寫法播放起來通常最順?
animation: pulse 1s infinite alternate; 裡的 alternate 是什麼意思?
使用者在系統設定開啟了「減少動態」,網頁要怎麼知道?
CSS 變數與深色模式
同一個品牌藍出現在標題、按鈕、標籤、邊框,想換顏色就要改十幾個地方。 CSS 自訂屬性(custom property,常叫 CSS 變數)讓你把值取個名字,集中寫在一個地方:
:root { /* :root 就是 html 元素,變數會繼承給整頁 */
--accent: #3C6F9A; /* 名字一定以兩個連字號 -- 開頭 */
--bg: #ffffff;
--text: #1f2933;
}
.tag {
background-color: var(--accent); /* 用 var() 取出來 */
color: var(--bg);
}
.note {
border-color: var(--line, #cbd2d9); /* 第二個值是找不到 --line 時的備用值 */
}
變數和一般屬性一樣會繼承,也遵守層疊規則:在某個元素上重新定義,它和它的子元素就用新的值。
深色模式:系統設成深色時,媒體查詢 prefers-color-scheme: dark 會成立。
既然所有顏色都透過變數取用,只要在媒體查詢裡重新定義變數,整頁就跟著換色,其他規則一行都不用改:
@media (prefers-color-scheme: dark) {
:root {
--bg: #1e293b;
--text: #e2e8f0;
--accent: #8ec5ff; /* 深色背景上,藍色要調亮,對比才夠 */
}
}
深色模式不是把顏色全部反過來:純黑底配純白字很刺眼,通常用深藍灰底配淺灰字; 原本的強調色在深色背景上常常不夠亮,要另外挑一個(第 24 課會教你算對比值)。
小測驗:CSS 變數
下面的 .tag 背景是什麼顏色?
:root { --accent: blue; }
.card { --accent: green; }
.tag { background-color: var(--accent); }
<div class="card"><span class="tag">標籤</span></div>
color: var(--brand, #333); 的 #333 是做什麼用的?
所有顏色都已經用變數寫好了。要加上深色模式,最少要改什麼?
重點整理
transition: 屬性 時間 速度曲線 延遲;寫在元素平常的樣式上,進出兩個方向都會過渡。- 速度曲線決定快慢分配:
linear等速、ease-out先快後慢、cubic-bezier()自訂;y 超過 1 會衝過頭。 transform的 translate、scale、rotate 只改畫出來的樣子,不推開旁邊的元素;inline 元素要先改成 inline-block。@keyframes定義關鍵畫面,animation套用;infinite一直重複、alternate來回播。- 動畫優先只改
transform與opacity,通常不必每一格重新排版,播放比較順。 @media (prefers-reduced-motion: reduce)裡把裝飾性的動畫關掉,資訊要用文字留下來。- CSS 變數:
--名字: 值;定義、var(--名字, 備用值)取用;會繼承。深色模式只要在prefers-color-scheme: dark裡重新定義變數。
下一課介紹 Bootstrap:一套把格線、按鈕、表單這些常用樣式先寫好的 CSS 框架,以及它和你學過的 Flexbox、媒體查詢是什麼關係。