fix(ui): 拖动松手从手指位置平滑契合到目标 tab

- 拖动状态与位置状态分离
- 松手先恢复过渡但保留手指位置,下一帧再吸附到目标 tab,避免从旧位置跳变
This commit is contained in:
2026-08-18 23:13:25 +08:00
parent 81d668b996
commit 4646f87797
3 changed files with 33 additions and 8 deletions
+19 -7
View File
@@ -41,6 +41,8 @@ export default function CustomTabBar() {
// 微信自定义 tabBar 不会自动感知 page 路由变化,用 state 保持选中态响应式
const [activeTab, setActiveTab] = useState<string>(currentTabPath)
const [dragPercent, setDragPercent] = useState<number | null>(null)
const [isDragging, setIsDragging] = useState<boolean>(false)
const isDraggingRef = useRef<boolean>(false)
const dragPercentRef = useRef<number | null>(null)
const startXRef = useRef<number>(0)
const barFrameRef = useRef<{ left: number; width: number } | null>(null)
@@ -105,6 +107,8 @@ export default function CustomTabBar() {
const frame = getTabBarFrame(Taro.getSystemInfoSync().windowWidth)
barFrameRef.current = frame
startXRef.current = touch.clientX
isDraggingRef.current = false
setIsDragging(false)
dragPercentRef.current = null
setDragPercent(null)
}
@@ -113,8 +117,10 @@ export default function CustomTabBar() {
const frame = barFrameRef.current
const touch = e.touches && e.touches[0]
if (!frame || !touch) return
if (dragPercentRef.current === null && !hasDragged(startXRef.current, touch.clientX, DRAG_THRESHOLD_PX)) {
return
if (!isDraggingRef.current) {
if (!hasDragged(startXRef.current, touch.clientX, DRAG_THRESHOLD_PX)) return
isDraggingRef.current = true
setIsDragging(true)
}
const nextPercent = clampPercent(((touch.clientX - frame.left) / frame.width) * 100)
dragPercentRef.current = nextPercent
@@ -125,8 +131,14 @@ export default function CustomTabBar() {
const frame = barFrameRef.current
if (!frame) return
let targetIndex: number
if (dragPercentRef.current !== null) {
targetIndex = tabIndexAtPercent(dragPercentRef.current, TABS.length)
if (isDraggingRef.current) {
targetIndex = tabIndexAtPercent(dragPercentRef.current ?? 0, TABS.length)
isDraggingRef.current = false
setIsDragging(false)
Taro.nextTick(() => {
dragPercentRef.current = null
setDragPercent(null)
})
} else {
const touch = e.changedTouches && e.changedTouches[0]
const x = touch ? touch.clientX : startXRef.current
@@ -136,13 +148,13 @@ export default function CustomTabBar() {
if (target && target.pagePath !== activeTab) switchTab(target.pagePath)
barFrameRef.current = null
startXRef.current = 0
dragPercentRef.current = null
setDragPercent(null)
}
const cancelDrag = () => {
barFrameRef.current = null
startXRef.current = 0
isDraggingRef.current = false
setIsDragging(false)
dragPercentRef.current = null
setDragPercent(null)
}
@@ -163,7 +175,7 @@ export default function CustomTabBar() {
<View className='tab-bar-caustic' />
<View className='tab-bar-rim' />
<View
className={`tab-indicator ${dragPercent !== null ? 'dragging' : ''}`}
className={`tab-indicator ${isDragging ? 'dragging' : ''}`}
style={{ left: dragPercent === null ? `${(activeIndex + 0.5) * TAB_CENTER_STEP}%` : `${dragPercent}%` }}
/>
{TABS.map((tab) => {