F12▸S003▸framer-motion
whileInView Trigger
視口觸發
難度 ★★★★★標籤 滾動 · viewport · whileInView3 min read
效果簡述
不是動畫本身,是「動畫的觸發方式」。framer-motion 的 whileInView
是 60% 的時候你應該用的觸發器——不是 useScroll、不是 IntersectionObserver
手寫。
進入視口才觸發
once: true · amount: 0.5
Claude Code Prompt
參考:S003 whileInView trigger
技術:Framer Motion 的 whileInView + viewport 選項
具體行為:
- whileInView 取代 animate,當元素進入視口才執行
- viewport: { once: true }(重要!)→ 只觸發一次
- viewport: { amount: 0.4 }→ 元素 40% 進入視口才觸發
- 配合 initial 設定起始狀態
限制:
- once: true 是預設應該加的,少數情境才會想反覆觸發
- amount 0.3 - 0.5 最自然;太低會還沒看到就動完,太高會等很久完整程式碼
'use client'
import { motion } from 'framer-motion'
export function WhileInViewTrigger() {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, amount: 0.5 }}
transition={{ duration: 0.5, ease: [0.4, 0, 0.2, 1] }}
>
進入視口才觸發
</motion.div>
)
}何時用
- ✅ 任何「滾動到才出現」的進場動畫
- ✅ 章節分隔、卡片列表、figure 圖片
- ✅ 取代手寫 IntersectionObserver
何時別用
- ❌ 頁面載入立刻要動的元素(用 animate 不要用 whileInView)
- ❌ 已經在 viewport 內的元素也想要進場(會立刻觸發,看不到動畫) → 解法:先 setTimeout 一下再 mount,或用 useEffect 切換 state
我踩過的坑
-
viewport once 沒加 = 災難:使用者滾上滾下會一直觸發動畫, 像鬼打牆。一律
viewport={{ once: true }},除非你「真的」需要 重複觸發(極少數)。 -
手機 amount 要降一點:手機畫面高度小,amount: 0.5 可能整個元素 都進來才觸發,感覺很慢。手機建議 0.3。可以判斷
window.innerHeight切換。 -
動畫到一半離開視口會凍在那裡:因為 once: true 後 framer 不會「回退」。解法:要嘛接受這個行為,要嘛 once: false 但這樣 就會反覆觸發——所以最好的辦法是讓 duration 短一點(< 0.7s)。
相關效果
- F01 Fade Up — 最常見的 whileInView 應用
- F11 Scroll Progress Bar — useScroll 版本的滾動互動