網站做完功能只是 60 分。剩下 30 分藏在「微互動」裡 — 那些使用者察覺不到但能感受到的小動畫、即時回饋、狀態變化。這篇整理 12 個經典場景,每個附 CSS / JS 範例。
1. Hover:尺寸放大 + 陰影加深
.card {
transition: all 0.25s cubic-bezier(0.22, 1, 0.36, 1);
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 12px 32px rgba(15,15,30,0.08);
}
關鍵是 cubic-bezier(0.22, 1, 0.36, 1)。這條曲線比 ease-out 更自然,前段快、後段緩。Linear 動畫看起來機械,這條曲線看起來「物理感」。
2. 按鈕點擊:scale down 0.97
button {
transition: transform 0.1s;
}
button:active {
transform: scale(0.97);
}
0.1s 才有「真的被按下」的觸覺感。0.3s 太慢、0.05s 太快感覺不到。
3. Loading 狀態:skeleton 取代 spinner
白色閃光從左滑到右,比旋轉的 spinner 更降低焦慮感:
.skeleton {
background: linear-gradient(90deg, #f0f0f0, #e0e0e0, #f0f0f0);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
4. 表單驗證:邊框顏色 + 圖示
不要等 submit 才告訴使用者錯。輸入完離開欄位(blur)就驗證:
input.invalid { border-color: #e11d48; }
input.valid { border-color: #15803d; }
input:focus { outline: 2px solid var(--accent-blue); }
5. Modal 開啟:fade + scale
.modal {
opacity: 0;
transform: scale(0.95);
transition: opacity 0.2s, transform 0.2s;
}
.modal.open {
opacity: 1;
transform: scale(1);
}
scale 從 0.95 到 1 模擬「從遠處 zoom in」的感覺。比直接跳出更有層次。
6. 滾動觸發:IntersectionObserver + 淡入
const io = new IntersectionObserver(entries => {
entries.forEach(e => {
if (e.isIntersecting) e.target.classList.add('in-view');
});
}, { threshold: 0.15 });
document.querySelectorAll('.reveal').forEach(el => io.observe(el));
/* CSS */
.reveal { opacity: 0; transform: translateY(20px); transition: 0.8s; }
.reveal.in-view { opacity: 1; transform: translateY(0); }
7. 數字滾動 (counter)
從 0 滑到目標數字,比直接顯示更有衝擊:
function animateCount(el, target, duration = 1500) {
const start = performance.now();
function step(now) {
const p = Math.min((now - start) / duration, 1);
const eased = 1 - Math.pow(1 - p, 3);
el.textContent = Math.round(eased * target);
if (p < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
8. Toast 通知:從上方滑入 + 自動消失
.toast {
transform: translateY(-100%);
transition: transform 0.3s;
}
.toast.show {
transform: translateY(0);
}
9. 切換主題:crossfade 整頁
html.theme-transitioning * {
transition: background-color 0.3s, color 0.3s, border-color 0.3s !important;
}
暫時加個 class 讓所有元素都有 transition,切換完移除,避免後續操作都很慢。
10. 卡片進場:staggered animation
多個卡片不要同時出現,差 50ms 一個:
.card:nth-child(1) { animation-delay: 0ms; }
.card:nth-child(2) { animation-delay: 50ms; }
.card:nth-child(3) { animation-delay: 100ms; }
創造「依序出現」的節奏感,比一次彈出去自然。
11. 拖曳 (drag):透明度 + 旋轉
拖曳時稍微透明 + 微旋轉,給予「正在拖」的明確感受:
.dragging {
opacity: 0.7;
transform: rotate(-2deg);
cursor: grabbing;
}
12. Form Submit:按鈕 → loading → success
三段狀態切換:
<button class="btn-submit">
<span class="label">提交</span>
<span class="spinner">⌛</span>
<span class="check">✓</span>
</button>
.btn-submit.loading .label { opacity: 0; }
.btn-submit.loading .spinner { opacity: 1; }
.btn-submit.success .check { opacity: 1; }
原則總結
| 原則 | 應用 |
|---|---|
| 動畫不超過 300ms | 使用者注意力短,過久反而煩 |
| 用 cubic-bezier 不用 linear | 自然物理感 |
| 狀態切換要可逆 | 不要只有「開」沒有「關」動畫 |
| 尊重 prefers-reduced-motion | 無障礙考量 |
| 過度動畫 = 無動畫 | 節奏感比花哨重要 |
結語
使用者通常記不住網站視覺,但會記住「感覺好用」。這些微互動就是「感覺好用」的來源。每個都只是 5–10 行 CSS,組合起來決定一個網站的質感。