G12▸L101▸gsap
Flip Layout Animation
Flip 排版變換
難度 ★★★★★標籤 flip · layout · FLIP3 min read
效果簡述
從 grid 變 list、或位置互換時,元素「平滑滑過去」而不是瞬移。 GSAP 的 Flip plugin 做的就是經典的 FLIP(First/Last/Invert/Play) 技術。Framer 的 layout 動畫 + AnimatePresence 是同樣思路的另一實作。
A
B
C
D
E
F
Claude Code Prompt
參考:L101 flip layout
技術:gsap Flip plugin
具體行為:
- gsap.registerPlugin(Flip)
- 先拍:const state = Flip.getState(items)
- 改 DOM / state(grid → list)
- 下一幀:Flip.from(state, { duration: 0.6, ease: 'power2.inOut' })
限制:
- React 要在 setState 後等 commit,用 requestAnimationFrame 包
- Flip plugin 是免費的(不像 SplitText)
- 元素數量大於 30 開始卡,要分批完整程式碼
'use client'
import gsap from 'gsap'
import { Flip } from 'gsap/Flip'
import { useEffect, useRef, useState } from 'react'
export function FlipLayout() {
const root = useRef<HTMLDivElement>(null)
const [grid, setGrid] = useState(true)
useEffect(() => {
gsap.registerPlugin(Flip)
}, [])
function toggle() {
const items = root.current?.querySelectorAll('.item')
if (!items) return
const state = Flip.getState(items)
setGrid((g) => !g)
requestAnimationFrame(() => {
Flip.from(state, { duration: 0.6, ease: 'power2.inOut' })
})
}
return (
<>
<div ref={root} className={grid ? 'grid grid-cols-3 gap-3' : 'flex flex-col gap-2'}>
{['A','B','C','D','E','F'].map((x) => (
<div key={x} className="item bg-orange-500/20 p-4">{x}</div>
))}
</div>
<button onClick={toggle}>切換</button>
</>
)
}何時用
- ✅ Grid / List 切換
- ✅ 排序變化(拖曳重排)
- ✅ 篩選後位置重排
何時別用
- ❌ 元素 > 30(會卡)
- ❌ 內含 form / iframe(state 抓不到)
- ❌ 互動需要立即回饋(Flip 600ms 期間別人 hover 會錯位)
我踩過的坑
-
沒 requestAnimationFrame setState 前後位置一樣:React state 還沒 commit,DOM 沒變,Flip.from 對比不到差異。用
requestAnimationFrame等下一幀。 -
transition: all 干擾 Flip:CSS 本身也有過渡時,會跟 Flip 打架。 Flip 動畫的目標屬性別在 CSS 寫 transition。
-
Framer layout 是同一概念:如果整站已經用 Framer,不必引 GSAP,
<motion.div layout>達到 80% 同效果。
相關效果
- G05 Timeline Sequence — 配 Flip 做複合動畫