Framer Motion 的拖拽與手勢系統讓實現復雜交互變得異常簡單。本文將深入解析核心 API,并通過實戰案例演示如何創造自然流暢的交互體驗。
🧲 拖拽動畫基礎
1. 啟用拖拽
使用 drag
屬性即可開啟拖拽能力。支持的值有:true
(全方向拖拽)、"x"
(僅允許橫向拖動)、"y"
(僅允許縱向拖動)。
import { motion } from 'motion/react';<motion.div drag style={{ width: 100, height: 100, background: '#09f', borderRadius: 8 }}>拖我一下
</motion.div>
2. 限制拖拽范圍
通過 dragConstraints
限制組件拖拽的邊界。它接受一個對象或一個 DOM 元素的 ref
。
// 使用對象定義邊界:左 0,右 100,上 0,下 50
<motion.div drag dragConstraints={{ left: 0, right: 100, top: 0, bottom: 50 }} />// 使用容器 DOM 作為邊界
const constraintsRef = useRef(null);<div ref={constraintsRef} style={{ width: 300, height: 200, border: '1px solid #ccc' }}><motion.div drag dragConstraints={constraintsRef} />
</div>
3. 拖拽彈性
dragElastic
控制拖拽超出邊界后的回彈力度,值越大表示越“有彈性”。
<motion.div drag dragConstraints={{ left: 0, right: 100 }} dragElastic={0.8} />
4. 拖拽釋放動量與過渡
dragTransition
用于定制拖拽釋放后的過渡動畫。它支持以下參數:
bounceStiffness
: 彈性剛度,值越大彈跳速度越快。bounceDamping
: 彈性阻尼,值越大表示越“穩重”、回彈越慢。power
: 控制釋放時速度對最終距離的影響。timeConstant
: 控制速度衰減(當power
不設時有效)。modifyTarget
: 自定義拖動釋放的目標值。
<motion.divdragdragConstraints={{ left: 0, right: 300 }}dragTransition={{ bounceStiffness: 300, bounceDamping: 20 }}style={{ width: 100, height: 100, background: '#f09', borderRadius: 16 }}
/>
合理配置 dragTransition
能夠創造更自然的拖拽釋放體驗,特別適合彈性卡片、吸附動畫等場景。
🖱? 用戶交互動畫
1. whileHover
與 whileTap
通過 whileHover
與 whileTap
可以快速定義懸停和點擊動畫,常用于按鈕、卡片等組件交互反饋。
<motion.button whileHover={{ scale: 1.1, rotate: -2 }} whileTap={{ scale: 0.95, rotate: 0 }} className=" rounded-4xl bg-amber-400 w-[200px] h-[60px] text-amber-100 font-bold">點我</motion.button>
2. 組合手勢動畫 + 回調事件
你也可以組合 variants
、whileHover
和 whileTap
實現更細膩的交互體驗,并結合 onTap
, onHoverStart
, onHoverEnd
處理業務邏輯。
<motion.divvariants={cardVariants}whileHover="hover"whileTap="tap"onTap={() => alert("點擊事件觸發")}onHoverStart={() => console.log("懸停開始")}onHoverEnd={() => console.log("懸停結束")}className="bg-blue-500 px-4 py-2 rounded-2xl text-white font-bold">交互卡片</motion.div>
📦 實戰示例
示例一:拖拽式卡片組件
通過簡單配置實現卡片的拖拽、懸停、點擊縮放等交互。
const Card = () => (<motion.divdragdragElastic={0.6}whileHover={{ scale: 1.05, boxShadow: '0px 4px 10px rgba(0,0,0,0.15)' }}whileTap={{ scale: 0.95 }}style={{width: 150,height: 100,background: '#ccc',borderRadius: 12,display: 'flex',alignItems: 'center',justifyContent: 'center',}}>拖我!</motion.div>
);
示例二:交互式卡片反饋系統
const InteractiveCard = () => {const [isDragging, setIsDragging] = useState(false);return (<motion.divdragonDragStart={() => setIsDragging(true)}onDragEnd={() => setIsDragging(false)}whileHover={{ scale: 1.05, zIndex: 1 }}whileTap={{ scale: 0.95 }}animate={{scale: isDragging ? 1.1 : 1,boxShadow: isDragging? '0px 20px 40px rgba(0,0,0,0.3)': '0px 5px 15px rgba(0,0,0,0.1)'}}transition={{ type: 'spring', stiffness: 400 }}style={{ width: 160, height: 120, background: '#fff', borderRadius: 12 }}/>);
};
示例三:卡片縮放與排序反饋(預告)
通過 drag + layout + motionValue 實現卡片重新排序,將在后續《布局動畫》一節詳細講解。
<motion.div layout drag dragConstraints={{ left: 0, right: 0 }} />
🚀 性能與調試建議
為什么要這樣做?
拖拽動畫通常會頻繁觸發 DOM 更新,如果不加以優化,可能會出現掉幀、延遲等現象。以下方式可以幫助你保持動畫流暢:
拖拽性能優化技巧
<motion.divdragdragMomentum={false} // 禁用動量滾動,避免多余動畫計算dragElastic={0} // 禁用彈性回彈,提高拖拽響應速度style={{willChange: 'transform', // 提前通知瀏覽器該元素將變形,觸發硬件加速touchAction: 'none' // 避免移動端默認滾動行為}}
/>
拖拽事件監控
使用 onDragStart
、onDrag
、onDragEnd
可精確捕捉用戶交互過程,便于調試或聯動狀態管理:
<motion.divdragonDragStart={() => console.log('拖拽開始')}onDrag={(e, info) => console.log('當前坐標:', info.point)}onDragEnd={() => console.log('拖拽結束')}
/>
可視化調試邊界
使用邊框輔助線或背景色可快速確認組件拖拽區域是否設置正確:
<motion.divdragdragConstraints={{ left: -100, right: 100 }}style={{border: '2px dashed #e74c3c',position: 'relative'}}
/>
? 最佳實踐小結
- 拖拽元素建議提升
z-index
,避免被遮擋 layout
屬性可自動處理拖拽后的回彈與布局更新- 懸停動效建議在 200-300ms,點擊反饋不超過 100ms
- 注意跨平臺適配(桌面與移動)
- 注意無障礙支持(如添加
aria-label
)
<motion.buttondragwhileTap={{ scale: 0.95 }}aria-label="可拖拽按鈕"
/>
掌握這些技巧后,Framer Motion 的拖拽與交互系統將不再神秘,你可以為產品帶來更自然、更細膩的動態體驗。在下一篇《📘 第 5 篇:布局動畫與卡片排序》中,我們將探索 layout 布局動畫與動態排序邏輯,敬請期待。