T05▸3D005▸three
Hover Image Plane
hover 互動平面
難度 ★★★★★標籤 hover · pointer · three3 min read
效果簡述
R3F 物件接受 onPointerOver / onPointerOut 像 DOM 一樣。可以做 hover
放大、變色、旋轉。這 demo 用簡單的 plane,實務上換成貼圖即可。
Claude Code Prompt
參考:3D005 hover image plane
技術:r3f pointer events + useFrame 緩動
具體行為:
- mesh onPointerOver/Out 設 hover state
- useFrame 裡用 lerp 緩動到目標 scale
- 不要用 CSS transition(三維沒有)
限制:
- 行動裝置沒 hover,要 fallback(觸控時直接 active)
- pointer events 需要 mesh 在最上層,沒被遮擋
- raycaster 對 plane 雙面:material 設 side={THREE.DoubleSide} 避免背面點不到完整程式碼
'use client'
import { Canvas, useFrame } from '@react-three/fiber'
import { useRef, useState } from 'react'
import type { Mesh } from 'three'
function ImagePlane() {
const ref = useRef<Mesh>(null)
const [hover, setHover] = useState(false)
useFrame((_, d) => {
const m = ref.current; if (!m) return
const t = hover ? 1.15 : 1
m.scale.x += (t - m.scale.x) * d * 6
m.scale.y += (t - m.scale.y) * d * 6
})
return (
<mesh ref={ref} onPointerOver={() => setHover(true)} onPointerOut={() => setHover(false)}>
<planeGeometry args={[3, 1.7, 32, 32]} />
<meshBasicMaterial color={hover ? '#fb923c' : '#c2410c'} />
</mesh>
)
}
export function HoverImagePlane() {
return (
<Canvas camera={{ position: [0,0,4], fov: 50 }} dpr={[1,2]}>
<ambientLight intensity={1} />
<ImagePlane />
</Canvas>
)
}何時用
- ✅ 3D 卡片網格(hover 突出)
- ✅ 作品縮圖
- ✅ 配 GLTF 模型 hover 互動
何時別用
- ❌ 行動為主用戶
- ❌ 一頁很多 hover 元素(事件衝突)
- ❌ 對 hit test 要求高的精細互動
我踩過的坑
-
沒緩動瞬移:直接 set scale 看起來廉價,lerp 緩動才對味。
-
plane 背面點不到:DoubleSide 才確保 raycaster 能擊中。
-
手機沒 hover:要設定 onPointerDown 作為 fallback。
相關效果
- F08 Magnetic Button — 2D 版 hover 物理感