基于Vue3+TypeScript實現(xiàn)抖音式卡片滑動交互功能
效果拆解
目標效果:屏幕中央一張主卡片,后方隱約可見兩張堆疊卡片。手指左滑(不認識)或右滑(認識),卡片跟隨手指移動并旋轉,松開后飛出屏幕,下一張頂上。
核心需求:
- 3 張可見卡片(z-index 分層,縮放遞減)
- Pointer Events 手勢跟蹤
- 滑動過程實時視覺反饋(顏色、圖標、標簽)
- 閾值判斷 + 飛出動畫 + 隊列循環(huán)
卡片堆疊布局
三張卡片用 v-for 渲染,通過 index 控制層級和變形:
const stackStyle = (stackIndex: number) => {
if (stackIndex >= 3) return { display: 'none' }
const scaleMap = [1, 0.95, 0.90] // 越靠后越小
const yOffsetMap = [0, 12, 24] // 越靠后越向下
const zIndexMap = [30, 29, 28] // 越靠前越高
return {
zIndex: zIndexMap[stackIndex],
transform: `scale(${scaleMap[stackIndex]}) translateY(${yOffsetMap[stackIndex]}px)`,
transition: stackIndex === 0 && isDragging.value
? 'none' // 拖拽中不要過渡
: 'transform 0.35s ease'
}
}
數(shù)組用 ref<Word[]>([]) 存儲當前隊列。隊列用完時自動從后端拉取下一批。
Pointer Events 手勢跟蹤
用 Pointer Events 而非 Touch Events,因為 Pointer Events 同時支持觸屏和鼠標,方便開發(fā)調試:
const SWIPE_THRESHOLD = 72 // px,超過此值觸發(fā)飛出
const isDragging = ref(false)
const dragX = ref(0)
const onPointerDown = (e: PointerEvent) => {
isDragging.value = true
startX.value = e.clientX
;(e.target as HTMLElement).setPointerCapture(e.pointerId)
}
const onPointerMove = (e: PointerEvent) => {
if (!isDragging.value) return
dragX.value = e.clientX - startX.value
}
const onPointerUp = () => {
if (!isDragging.value) return
isDragging.value = false
if (Math.abs(dragX.value) > SWIPE_THRESHOLD) {
// 觸發(fā)飛出
const direction = dragX.value > 0 ? 'right' : 'left'
flyAway(direction)
} else {
// 復位
dragX.value = 0
}
}
關鍵點:setPointerCapture 確保手指滑出元素邊界后依然能跟蹤。不用這個 API 的話,手指移出卡片范圍就會丟失 tracking。
實時視覺反饋
根據拖拽距離動態(tài)計算顏色和圖標透明度:
const feedbackColor = computed(() => {
const ratio = Math.min(Math.abs(dragX.value) / SWIPE_THRESHOLD, 1)
if (dragX.value > 0) {
return `rgba(34, 197, 94, ${ratio * 0.3})` // 綠色 = 認識
} else if (dragX.value < 0) {
return `rgba(239, 68, 68, ${ratio * 0.3})` // 紅色 = 不認識
}
return 'transparent'
})
const checkIconOpacity = computed(() =>
dragX.value > 0 ? Math.min(dragX.value / SWIPE_THRESHOLD, 1) : 0
)
const crossIconOpacity = computed(() =>
dragX.value < 0 ? Math.min(-dragX.value / SWIPE_THRESHOLD, 1) : 0
)
拖拽距離與閾值的比值驅動所有視覺過渡。不需要動畫庫,computed 全搞定。
飛出動畫 + 隊列推進
飛出用 CSS transition + 最終位置計算:
const flyAway = (direction: 'left' | 'right') => {
const targetX = direction === 'right' ? window.innerWidth * 1.5 : -window.innerWidth * 1.5
flyX.value = targetX
flyRotation.value = direction === 'right' ? 20 : -20
// 記錄結果
const result = direction === 'right' ? 'known' : 'forgotten'
submitReview(currentWord.value.id, result)
// 動畫結束后移除當前詞,推進隊列
setTimeout(() => {
words.value.shift()
flyX.value = 0
flyRotation.value = 0
dragX.value = 0
// 隊列快用完時預加載
if (words.value.length < 5) fetchMoreWords()
}, 350)
}
飛出期間新卡片已經在原來的第二張位置就位,視覺上無縫銜接。
四隊列切換
高頻詞、大綱詞、綜合、錯詞鞏固,四種模式對應不同 API 參數(shù):
type QueueMode = 'high' | 'outline' | 'mixed' | 'wrong'
const switchQueue = async (mode: QueueMode) => {
currentMode.value = mode
words.value = []
await fetchWords(mode)
}
錯詞隊列的每個詞帶有復習記錄,后端根據復習次數(shù)和上次復習時間動態(tài)計算下次推送時機,實現(xiàn)間隔重復。
性能注意事項
- 事件監(jiān)聽用 passive: false:Pointer Events 需要
preventDefault防止頁面滾動,所以要顯式設置{ passive: false } - 動畫用 transform 而非 left/top:transform 走合成層,不觸發(fā)重排
- 隊列預加載:剩 5 張時自動拉取,滑動體驗零等待
完整代碼較長,核心邏輯即上面這些。這個交互方案可以直接復用到任何需要卡片滑動判斷的場景——背單詞、刷題、審閱、相親……
在線體驗:m.dobell.top,打開就能看到效果。
以上就是基于Vue3+TypeScript實現(xiàn)抖音式卡片滑動交互功能的詳細內容,更多關于Vue3 TypeScript抖音式卡片滑動交互的資料請關注腳本之家其它相關文章!
相關文章
Vue+Vant實現(xiàn)7天日歷展示并在切換日期時實時變換功能
本文介紹了如何利用Vue和Vant框架結合moment.js插件來實現(xiàn)一個7天日歷展示功能,在這個功能中,用戶可以在切換日期時看到界面的實時變化,此外,文章還提供了代碼實現(xiàn)和效果測試的詳細步驟,幫助開發(fā)者能夠順利完成類似的項目開發(fā)2024-10-10
使用konva和vue-konva庫實現(xiàn)拖拽滑塊驗證功能
這篇文章主要介紹了使用konva和vue-konva完成前端拖拽滑塊驗證功能,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-04-04
Webpack+Vue如何導入Jquery和Jquery的第三方插件
本文主要介紹了Webpack+Vue導入Jquery和Jquery的第三方插件的方法,具有很好的參考價值,下面跟著小編一起來看下吧2017-02-02
詳解@Vue/Cli 3 Invalid Host header 錯誤解決辦法
這篇文章主要介紹了詳解@Vue/Cli 3 Invalid Host header 錯誤解決辦法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-01-01

