Vue 3 实现抖音式卡片滑动交互:从零到完整方案 本文介绍如何用 Vue 3 TypeScript 实现类似抖音的卡片滑动交互应用于单词记忆场景。完整方案包含手势识别、堆叠动画、实时反馈和状态管理。在线体验m.dobell.top手机浏览器打开。效果拆解目标效果屏幕中央一张主卡片后方隐约可见两张堆叠卡片。手指左滑不认识或右滑认识卡片跟随手指移动并旋转松开后飞出屏幕下一张顶上。核心需求3 张可见卡片z-index 分层缩放递减Pointer Events 手势跟踪滑动过程实时视觉反馈颜色、图标、标签阈值判断 飞出动画 队列循环卡片堆叠布局三张卡片用 v-for 渲染通过 index 控制层级和变形conststackStyle(stackIndex:number){if(stackIndex3)return{display:none}constscaleMap[1,0.95,0.90]// 越靠后越小constyOffsetMap[0,12,24]// 越靠后越向下constzIndexMap[30,29,28]// 越靠前越高return{zIndex:zIndexMap[stackIndex],transform:scale(${scaleMap[stackIndex]}) translateY(${yOffsetMap[stackIndex]}px),transition:stackIndex0isDragging.value?none// 拖拽中不要过渡:transform 0.35s ease}}数组用refWord[]([])存储当前队列。队列用完时自动从后端拉取下一批。Pointer Events 手势跟踪用 Pointer Events 而非 Touch Events因为 Pointer Events 同时支持触屏和鼠标方便开发调试constSWIPE_THRESHOLD72// px超过此值触发飞出constisDraggingref(false)constdragXref(0)constonPointerDown(e:PointerEvent){isDragging.valuetruestartX.valuee.clientX;(e.targetasHTMLElement).setPointerCapture(e.pointerId)}constonPointerMove(e:PointerEvent){if(!isDragging.value)returndragX.valuee.clientX-startX.value}constonPointerUp(){if(!isDragging.value)returnisDragging.valuefalseif(Math.abs(dragX.value)SWIPE_THRESHOLD){// 触发飞出constdirectiondragX.value0?right:leftflyAway(direction)}else{// 复位dragX.value0}}关键点setPointerCapture确保手指滑出元素边界后依然能跟踪。不用这个 API 的话手指移出卡片范围就会丢失 tracking。实时视觉反馈根据拖拽距离动态计算颜色和图标透明度constfeedbackColorcomputed((){constratioMath.min(Math.abs(dragX.value)/SWIPE_THRESHOLD,1)if(dragX.value0){returnrgba(34, 197, 94,${ratio*0.3})// 绿色 认识}elseif(dragX.value0){returnrgba(239, 68, 68,${ratio*0.3})// 红色 不认识}returntransparent})constcheckIconOpacitycomputed(()dragX.value0?Math.min(dragX.value/SWIPE_THRESHOLD,1):0)constcrossIconOpacitycomputed(()dragX.value0?Math.min(-dragX.value/SWIPE_THRESHOLD,1):0)拖拽距离与阈值的比值驱动所有视觉过渡。不需要动画库computed 全搞定。飞出动画 队列推进飞出用 CSS transition 最终位置计算constflyAway(direction:left|right){consttargetXdirectionright?window.innerWidth*1.5:-window.innerWidth*1.5flyX.valuetargetX flyRotation.valuedirectionright?20:-20// 记录结果constresultdirectionright?known:forgottensubmitReview(currentWord.value.id,result)// 动画结束后移除当前词推进队列setTimeout((){words.value.shift()flyX.value0flyRotation.value0dragX.value0// 队列快用完时预加载if(words.value.length5)fetchMoreWords()},350)}飞出期间新卡片已经在原来的第二张位置就位视觉上无缝衔接。四队列切换高频词、大纲词、综合、错词巩固四种模式对应不同 API 参数typeQueueModehigh|outline|mixed|wrongconstswitchQueueasync(mode:QueueMode){currentMode.valuemode words.value[]awaitfetchWords(mode)}错词队列的每个词带有复习记录后端根据复习次数和上次复习时间动态计算下次推送时机实现间隔重复。性能注意事项事件监听用 passive: falsePointer Events 需要preventDefault防止页面滚动所以要显式设置{ passive: false }动画用 transform 而非 left/toptransform 走合成层不触发重排队列预加载剩 5 张时自动拉取滑动体验零等待完整代码较长核心逻辑即上面这些。这个交互方案可以直接复用到任何需要卡片滑动判断的场景——背单词、刷题、审阅、相亲……在线体验m.dobell.top打开就能看到效果。注册即送 3 天会员月卡 29 元。