F28▸I201▸framer-motion
Image Compare Slider
前後對比拖拉
難度 ★★★★★標籤 drag · compare · clip-path3 min read
效果簡述
左右兩張圖(或設計稿)疊在一起,拖中間分隔線比較 before / after。
攝影、設計改版、AI 修圖展示必備。核心是 clip-path: inset() 把上層
切成不同寬度。
BEFORE
AFTER
⇆
Claude Code Prompt
參考:I201 image compare
技術:clip-path inset + motion value + 事件位置換算
具體行為:
- 父層 ref + overflow-hidden
- useMotionValue(50) 代表分隔線位置(百分比)
- useTransform 把 x 變成 clip-path: inset(0 ${100-v}% 0 0)
- onMouseMove / onTouchMove 算出 (clientX - rect.left) / rect.width * 100
- 限制 0~100,setX
限制:
- 兩張圖尺寸要一樣
- 必須 overflow-hidden 否則 clip-path 看起來像被切走
- 移動裝置用 onTouchMove,且 e.touches[0].clientX完整程式碼
'use client'
import { motion, useMotionValue, useTransform } from 'framer-motion'
import { useRef } from 'react'
export function ImageCompare() {
const ref = useRef<HTMLDivElement>(null)
const x = useMotionValue(50)
const clip = useTransform(x, (v) => `inset(0 ${100 - v}% 0 0)`)
const left = useTransform(x, (v) => `${v}%`)
function move(clientX: number) {
const r = ref.current?.getBoundingClientRect()
if (!r) return
x.set(Math.max(0, Math.min(100, ((clientX - r.left) / r.width) * 100)))
}
return (
<div
ref={ref}
className="relative h-44 cursor-ew-resize overflow-hidden rounded-xl border"
onMouseMove={(e) => move(e.clientX)}
onTouchMove={(e) => move(e.touches[0].clientX)}
>
<div className="absolute inset-0 bg-card">{/* BEFORE */}</div>
<motion.div className="absolute inset-0 bg-orange-500" style={{ clipPath: clip }}>
{/* AFTER */}
</motion.div>
<motion.div className="absolute inset-y-0 w-[2px] -translate-x-1/2 bg-white" style={{ left }}>
<div className="absolute top-1/2 left-1/2 h-8 w-8 -translate-x-1/2 -translate-y-1/2 rounded-full bg-white">⇆</div>
</motion.div>
</div>
)
}何時用
- ✅ 設計改版 before / after
- ✅ AI 修圖 / 上色展示
- ✅ 攝影 retouch demo
何時別用
- ❌ 兩張圖差異太小(看不出來)
- ❌ 內容站(沒對比情境)
- ❌ 主要受眾用螢幕閱讀器(替代方案:兩張並排)
我踩過的坑
-
使用 width % 而不是 clip-path 會 layout reflow:每次 mousemove resize 一個 div 整頁重排版。clip-path 只動 paint,順很多。
-
觸控事件忘記 onTouchMove:mobile 拖不動。要同時綁 mouse + touch。
-
沒 cursor-ew-resize 視覺暗示弱:要明確告訴使用者「可拖」。
相關效果
- G11 Draggable Cards — 進階拖曳