最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于Vue3+TypeScript實(shí)現(xiàn)抖音式卡片滑動(dòng)交互功能

 更新時(shí)間:2026年06月03日 09:00:43   作者:龍蝦不加班  
本文介紹如何用 Vue 3 + TypeScript 實(shí)現(xiàn)類似抖音的卡片滑動(dòng)交互,應(yīng)用于單詞記憶場景,完整方案包含手勢(shì)識(shí)別、堆疊動(dòng)畫、實(shí)時(shí)反饋和狀態(tài)管理,需要的朋友可以參考下

效果拆解

目標(biāo)效果:屏幕中央一張主卡片,后方隱約可見兩張堆疊卡片。手指左滑(不認(rèn)識(shí))或右滑(認(rèn)識(shí)),卡片跟隨手指移動(dòng)并旋轉(zhuǎn),松開后飛出屏幕,下一張頂上。

核心需求:

  • 3 張可見卡片(z-index 分層,縮放遞減)
  • Pointer Events 手勢(shì)跟蹤
  • 滑動(dòng)過程實(shí)時(shí)視覺反饋(顏色、圖標(biāo)、標(biāo)簽)
  • 閾值判斷 + 飛出動(dòng)畫 + 隊(duì)列循環(huán)

卡片堆疊布局

三張卡片用 v-for 渲染,通過 index 控制層級(jí)和變形:

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[]>([]) 存儲(chǔ)當(dāng)前隊(duì)列。隊(duì)列用完時(shí)自動(dòng)從后端拉取下一批。

Pointer Events 手勢(shì)跟蹤

用 Pointer Events 而非 Touch Events,因?yàn)?Pointer Events 同時(shí)支持觸屏和鼠標(biāo),方便開發(fā)調(diào)試:

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 {
    // 復(fù)位
    dragX.value = 0
  }
}

關(guān)鍵點(diǎn):setPointerCapture 確保手指滑出元素邊界后依然能跟蹤。不用這個(gè) API 的話,手指移出卡片范圍就會(huì)丟失 tracking。

實(shí)時(shí)視覺反饋

根據(jù)拖拽距離動(dòng)態(tài)計(jì)算顏色和圖標(biāo)透明度:

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})`  // 綠色 = 認(rèn)識(shí)
  } else if (dragX.value < 0) {
    return `rgba(239, 68, 68, ${ratio * 0.3})`  // 紅色 = 不認(rèn)識(shí)
  }
  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
)

拖拽距離與閾值的比值驅(qū)動(dòng)所有視覺過渡。不需要?jiǎng)赢嫀?,computed 全搞定。

飛出動(dòng)畫 + 隊(duì)列推進(jìn)

飛出用 CSS transition + 最終位置計(jì)算:

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

  // 記錄結(jié)果
  const result = direction === 'right' ? 'known' : 'forgotten'
  submitReview(currentWord.value.id, result)

  // 動(dòng)畫結(jié)束后移除當(dāng)前詞,推進(jìn)隊(duì)列
  setTimeout(() => {
    words.value.shift()
    flyX.value = 0
    flyRotation.value = 0
    dragX.value = 0
    // 隊(duì)列快用完時(shí)預(yù)加載
    if (words.value.length < 5) fetchMoreWords()
  }, 350)
}

飛出期間新卡片已經(jīng)在原來的第二張位置就位,視覺上無縫銜接。

四隊(duì)列切換

高頻詞、大綱詞、綜合、錯(cuò)詞鞏固,四種模式對(duì)應(yīng)不同 API 參數(shù):

type QueueMode = 'high' | 'outline' | 'mixed' | 'wrong'

const switchQueue = async (mode: QueueMode) => {
  currentMode.value = mode
  words.value = []
  await fetchWords(mode)
}

錯(cuò)詞隊(duì)列的每個(gè)詞帶有復(fù)習(xí)記錄,后端根據(jù)復(fù)習(xí)次數(shù)和上次復(fù)習(xí)時(shí)間動(dòng)態(tài)計(jì)算下次推送時(shí)機(jī),實(shí)現(xiàn)間隔重復(fù)。

性能注意事項(xiàng)

  • 事件監(jiān)聽用 passive: false:Pointer Events 需要 preventDefault 防止頁面滾動(dòng),所以要顯式設(shè)置 { passive: false }
  • 動(dòng)畫用 transform 而非 left/top:transform 走合成層,不觸發(fā)重排
  • 隊(duì)列預(yù)加載:剩 5 張時(shí)自動(dòng)拉取,滑動(dòng)體驗(yàn)零等待

完整代碼較長,核心邏輯即上面這些。這個(gè)交互方案可以直接復(fù)用到任何需要卡片滑動(dòng)判斷的場景——背單詞、刷題、審閱、相親……

在線體驗(yàn):m.dobell.top,打開就能看到效果。

以上就是基于Vue3+TypeScript實(shí)現(xiàn)抖音式卡片滑動(dòng)交互功能的詳細(xì)內(nèi)容,更多關(guān)于Vue3 TypeScript抖音式卡片滑動(dòng)交互的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • vue樣式穿透 ::v-deep的具體使用

    vue樣式穿透 ::v-deep的具體使用

    這篇文章主要介紹了vue樣式穿透 ::v-deep的具體使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • vue3 $attrs和inheritAttrs的用法說明

    vue3 $attrs和inheritAttrs的用法說明

    這篇文章主要介紹了vue3 $attrs和inheritAttrs的用法說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • vue?input組件如何設(shè)置失焦與聚焦

    vue?input組件如何設(shè)置失焦與聚焦

    這篇文章主要介紹了vue?input組件如何設(shè)置失焦與聚焦,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • Vue+Vant實(shí)現(xiàn)7天日歷展示并在切換日期時(shí)實(shí)時(shí)變換功能

    Vue+Vant實(shí)現(xiàn)7天日歷展示并在切換日期時(shí)實(shí)時(shí)變換功能

    本文介紹了如何利用Vue和Vant框架結(jié)合moment.js插件來實(shí)現(xiàn)一個(gè)7天日歷展示功能,在這個(gè)功能中,用戶可以在切換日期時(shí)看到界面的實(shí)時(shí)變化,此外,文章還提供了代碼實(shí)現(xiàn)和效果測試的詳細(xì)步驟,幫助開發(fā)者能夠順利完成類似的項(xiàng)目開發(fā)
    2024-10-10
  • 使用konva和vue-konva庫實(shí)現(xiàn)拖拽滑塊驗(yàn)證功能

    使用konva和vue-konva庫實(shí)現(xiàn)拖拽滑塊驗(yàn)證功能

    這篇文章主要介紹了使用konva和vue-konva完成前端拖拽滑塊驗(yàn)證功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-04-04
  • elementUI實(shí)現(xiàn)級(jí)聯(lián)選擇器

    elementUI實(shí)現(xiàn)級(jí)聯(lián)選擇器

    這篇文章主要為大家詳細(xì)介紹了elementUI實(shí)現(xiàn)級(jí)聯(lián)選擇器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • Webpack+Vue如何導(dǎo)入Jquery和Jquery的第三方插件

    Webpack+Vue如何導(dǎo)入Jquery和Jquery的第三方插件

    本文主要介紹了Webpack+Vue導(dǎo)入Jquery和Jquery的第三方插件的方法,具有很好的參考價(jià)值,下面跟著小編一起來看下吧
    2017-02-02
  • vue watch監(jiān)聽變量值的實(shí)時(shí)變動(dòng)示例詳解

    vue watch監(jiān)聽變量值的實(shí)時(shí)變動(dòng)示例詳解

    這篇文章主要介紹了vue 監(jiān)聽變量值的實(shí)時(shí)變動(dòng) watch,使用字符串形式的偵聽器函數(shù),還有一種是使用函數(shù)形式的偵聽器函數(shù),本文通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-08-08
  • vue.js+boostrap項(xiàng)目實(shí)踐(案例詳解)

    vue.js+boostrap項(xiàng)目實(shí)踐(案例詳解)

    這篇文章主要介紹了vue.js+boostrap項(xiàng)目實(shí)踐(案例詳解)的相關(guān)資料,本文圖文并茂介紹的非常詳細(xì),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-09-09
  • 詳解@Vue/Cli 3 Invalid Host header 錯(cuò)誤解決辦法

    詳解@Vue/Cli 3 Invalid Host header 錯(cuò)誤解決辦法

    這篇文章主要介紹了詳解@Vue/Cli 3 Invalid Host header 錯(cuò)誤解決辦法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-01-01

最新評(píng)論

福贡县| 布拖县| 德昌县| 内乡县| 行唐县| 明溪县| 齐河县| 府谷县| 开原市| 凤阳县| 三亚市| 德钦县| 九江市| 南通市| 中西区| 雷州市| 佛山市| 陆良县| 兰西县| 斗六市| 伊宁市| 麻栗坡县| 遂溪县| 朝阳市| 双柏县| 图们市| 武城县| 宕昌县| 甘肃省| 惠东县| 丽水市| 茂名市| 广西| 永德县| 沾化县| 克什克腾旗| 兴文县| 静安区| 香格里拉县| 绵竹市| 东乡族自治县|