Frontend Lab
← 回到所有效果
F09A034framer-motion

Tilt 3D Card

3D 傾斜卡片

難度 ★★★★★標籤 hover · 3D · perspective3 min read

效果簡述

游標在卡片上移動時,卡片用 perspective + rotateX/rotateY 做 3D 傾斜。 產品卡、人物卡都好用。一定要 perspective 在父層才有立體感。

移動滑鼠看 3D 傾斜

±12°

預設|傾斜 ±12°

Claude Code Prompt

💬 Claude Code Prompt
參考:A034 tilt 3D card
技術:framer-motion useMotionValue + useTransform + useSpring

具體行為:
- 父層 style={ perspective: 800 }
- 卡片用 motion.div,rotateX/rotateY 從 useMotionValue
- onMouseMove: 算出 normalized x/y in [-0.5, 0.5]
- useTransform: rotateY = x * 14(度數);rotateX = -y * 14
- useSpring 包平滑:stiffness 150 damping 12
- onMouseLeave: 歸零

限制:
- 角度 ±10° ~ ±15° 最自然,超過就鬼
- 父層 perspective 不能省,不然只是 2D skew
- 手機無效(建議降級成靜態)

完整程式碼

Tilt3DCard.tsx
'use client'

import { motion, useMotionValue, useSpring, useTransform } from 'framer-motion'

export function Tilt3DCard() {
const x = useMotionValue(0)
const y = useMotionValue(0)
const sx = useSpring(x, { stiffness: 150, damping: 12 })
const sy = useSpring(y, { stiffness: 150, damping: 12 })
const rotateY = useTransform(sx, [-0.5, 0.5], [-14, 14])
const rotateX = useTransform(sy, [-0.5, 0.5], [14, -14])

function onMove(e: React.MouseEvent<HTMLDivElement>) {
  const r = e.currentTarget.getBoundingClientRect()
  x.set((e.clientX - r.left) / r.width - 0.5)
  y.set((e.clientY - r.top) / r.height - 0.5)
}
function onLeave() {
  x.set(0)
  y.set(0)
}

return (
  <div style={{ perspective: 800 }}>
    <motion.div
      onMouseMove={onMove}
      onMouseLeave={onLeave}
      style={{ rotateX, rotateY, transformStyle: 'preserve-3d' }}
      className="h-48 w-64 rounded-xl border bg-card p-6"
    >
      Tilt me
    </motion.div>
  </div>
)
}

何時用

  • ✅ 商品 / 案例卡(hover 預覽)
  • ✅ 團隊成員卡
  • ✅ 主視覺裡的單一焦點卡

何時別用

  • ❌ 一整面 grid 卡片(會像地震)
  • ❌ 內含 form 的卡片(互動衝突)
  • ❌ 主要受眾用手機

我踩過的坑

  1. 沒 perspective 看起來是 2D skew:父層必須 perspective: 800 左右,子層才有透視感。

  2. rotateX 方向要反:游標往下時 → 卡片頂端往後仰 → rotateX 要負。 直接套 +y 會反向。

  3. 沒 spring 抖到爆:直接綁 motionValue 卡頓感很強。spring stiffness 150 / damping 12 是手感最舒服的範圍。

相關效果

搜尋

按 ⌘K 隨時開啟