只使用 CSS!拆解寶可夢 3D 閃卡特效
在網路上看到 pokemon-cards-css 把寶可夢卡牌的 3D 及閃膜用純 CSS 做出來,真的超帥!這篇整理我理解原始碼並自己實作的過程。
1. 卡片骨架
卡片的 DOM 結構只有三層 div:
.cardContainer:卡片的容器,用來置中卡片並設定比例。.card:卡片本身,負責建立 3D 空間。.rotator:實際執行 3D 旋轉的元素
<div className='cardContainer'> <div className='card'> <div className='rotator'> <img src='圖片網址' /> </div> </div></div>搭配 CSS 讓卡片擁有 3D 空間:
.cardContainer { display: flex; justify-content: center; align-items: center;}
.card { /* 透視深度,數值越小,透視變形的效果越強 */ perspective: 600px; /* 讓裡面的子元素都位於 3D 空間 */ transform-style: preserve-3d;}
.rotator { position: relative; transform-style: preserve-3d; transform-origin: center; /* 利用 CSS 變數控制轉動,初始值為 0 */ transform: rotateY(var(--rotate-y, 0)) rotateX(var(--rotate-x, 0));}這樣一來,我們的卡片就已經具備了 3D 的基礎,雖然現在看起來還不會動。

2. 卡片隨滑鼠傾斜
接著,我們需要用到 JavaScript 讓卡片可以偵測到滑鼠的位置,並跟著傾斜。
在 .card 上綁定 onMouseMove 事件:
const onMouseMove = (e) => { const rect = e.currentTarget.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; const xp = Math.round((x / rect.width) * 100); const yp = Math.round((y / rect.height) * 100); const dx = xp - 50; const dy = yp - 50;
setCardVars({ '--pointer-x': `${xp}%`, '--pointer-y': `${yp}%`, '--rotate-x': `${dy / 2}deg`, // Y 軸偏移量拿來轉 X 軸 '--rotate-y': `${-dx / 3.5}deg`, // X 軸偏移量拿來轉 Y 軸 '--rotator-transition': '120ms', });};程式碼的邏輯很單純:
- 取得滑鼠在卡片內的座標 (x, y)
- 將座標轉換成百分比 (xp, yp)
- 計算滑鼠位置與卡片中心的偏移量 (dx, dy)
- 根據偏移量去設定
--rotate-x、--rotate-y等 CSS 變數,卡片就會跟著旋轉了!
記得再透過 setCardVars 設定 CSS 變數,卡片就會神奇地動起來了!
如何使用 JS 設定 CSS 變數?
const cardRef = useRef(null);const pointerStateRef = useRef({ x: 50, y: 50 });
const setCardVars = (vars) => {const node = cardRef.current;if (!node) return;
for (const [k, v] of Object.entries(vars)) {node.style.setProperty(k, v);
if (k === '--pointer-x') { const n = parseFloat(v); if (!Number.isNaN(n)) pointerStateRef.current.x = n; } if (k === '--pointer-y') { const n = parseFloat(v); if (!Number.isNaN(n)) pointerStateRef.current.y = n; }
}};當滑鼠移開時,也要記得把卡片轉回原位:
const onMouseLeave = () => { setCardVars({ '--rotate-x': '0deg', '--rotate-y': '0deg', '--rotator-transition': '600ms', });};
3. 加上眩光效果
最後,來為卡片加上閃亮的眩光效果吧!
先在 .rotator 裡面加上一個 .glare 元素:
<div className='rotator'> <img src='圖片網址' /> <div className='glare' /></div>.glare 的樣式是利用 radial-gradient 做出來的:
.glare { position: absolute; inset: 0; mix-blend-mode: overlay; /* 眩光本體 */ background-image: radial-gradient( farthest-corner circle at var(--pointer-x, 50%) var(--pointer-y, 50%), hsla(0, 0%, 100%, 0.8) 10%, hsla(0, 0%, 100%, 0.65) 20%, hsla(0, 0%, 0%, 0.5) 90% ); opacity: var(--glare-opacity, 0); transition: opacity var(--glare-transition, 300ms) ease-out; pointer-events: none; /* 讓滑鼠可以穿透這層 */}記得一樣要在 setCardVars 裡面設定 --glare-opacity 跟 --glare-transition,這樣當滑鼠移進卡片時,眩光才會出現:
const onMouseMove = (e) => { ... setCardVars({ ... '--glare-opacity': '1', '--glare-transition': '180ms', });};
const onMouseLeave = () => { setCardVars({ ... '--glare-opacity': '0', '--glare-transition': '600ms', });};
完整程式碼
這篇會再持續學習並更新其他閃卡特效~