Skip to content

Commit d677d58

Browse files
authored
fix(FloatingPanel): fix stuck during indetermine direction (#631)
* fix(FloatingPanel): unexpected stuck at horizontal * revert: unexpected stuck at horizontal * fix: ignore if direction is indetermine * chore: use scroll utils for compatiable * feat(useTouch): use nullable firstMove * fix: remove direction check * fix: useTouch firstMove * feat: use EL contains to force dragging * feat: use non-passive listener * style: improve code reading
1 parent 4d71230 commit d677d58

File tree

2 files changed

+35
-19
lines changed

2 files changed

+35
-19
lines changed

packages/react-vant/src/components/floating-panel/FloatingPanel.tsx

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import React, { forwardRef, useImperativeHandle, useMemo, useRef } from 'react'
22
import clsx from 'clsx'
3-
import { createNamespace, preventDefault } from '../utils'
3+
import {
4+
createNamespace,
5+
getScrollTop,
6+
getVisibleHeight,
7+
preventDefault,
8+
} from '../utils'
49
import useEventListener from '../hooks/use-event-listener'
510
import { useTouch } from '../hooks'
611
import { FloatingPanelInstance, FloatingPanelProps } from './PropsType'
@@ -9,6 +14,12 @@ import { bound } from '../utils/bound'
914

1015
const [bem] = createNamespace('floating-panel')
1116

17+
/** Check if EL is scrolling reach its bottom */
18+
function scrollReachBottom(el: Element) {
19+
const scrollTop = getScrollTop(el)
20+
return scrollTop >= el.scrollHeight - getVisibleHeight(el)
21+
}
22+
1223
const FloatingPanel = forwardRef<FloatingPanelInstance, FloatingPanelProps>(
1324
(props, ref) => {
1425
const { className, style, onHeightChange, anchors = [100] } = props
@@ -44,24 +55,22 @@ const FloatingPanel = forwardRef<FloatingPanelInstance, FloatingPanelProps>(
4455
}
4556

4657
const onTouchMove: EventListener = event => {
47-
if (!body.current || !header.current) return
58+
const [headerEL, bodyEL] = [header.current, body.current]
4859
touch.move(event)
49-
if (touch.firstMove.current && touch.isVertical()) {
50-
const bodyEL = body.current
60+
if (visibleH.goal >= maxAnchor && bodyEL) {
5161
if (
52-
// attempt scroll at max anchor
53-
(touch.deltaY.current < 0 &&
54-
visibleH.goal >= maxAnchor &&
55-
bodyEL.offsetHeight < bodyEL.scrollHeight &&
56-
!(bodyEL.scrollTop + bodyEL.offsetHeight >= bodyEL.scrollHeight)) ||
57-
// attempt scroll back to top at max anchor
58-
(touch.deltaY.current > 0 && bodyEL.scrollTop > 0)
62+
touch.firstMove.current &&
63+
// try going up to body top
64+
((touch.deltaY.current > 0 && getScrollTop(bodyEL) > 0) ||
65+
// try going down to body bottom
66+
(touch.deltaY.current < 0 && !scrollReachBottom(bodyEL)))
5967
) {
6068
dragging.current = false
61-
return
6269
}
6370
}
64-
if (event.target === header.current) dragging.current = true
71+
if (headerEL && headerEL.contains(event.target as Element)) {
72+
dragging.current = true
73+
}
6574
if (!dragging.current) return
6675
preventDefault(event, true)
6776
api.start({
@@ -87,9 +96,12 @@ const FloatingPanel = forwardRef<FloatingPanelInstance, FloatingPanelProps>(
8796
}
8897
}
8998

90-
useEventListener('touchstart', onTouchStart, { target: root })
91-
useEventListener('touchmove', onTouchMove, { target: root })
92-
useEventListener('touchend', onTouchEnd, { target: root })
99+
useEventListener('touchstart', onTouchStart, {
100+
target: root,
101+
passive: false,
102+
})
103+
useEventListener('touchmove', onTouchMove, { target: root, passive: false })
104+
useEventListener('touchend', onTouchEnd, { target: root, passive: false })
93105

94106
return (
95107
<animated.div

packages/react-vant/src/components/hooks/use-touch.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export default function useTouch() {
2222
const offsetX = useRef(0)
2323
const offsetY = useRef(0)
2424
const direction = useRef<Direction>('')
25-
const firstMove = useRef(false)
25+
const firstMove = useRef<boolean>(null)
2626

2727
const isVertical = () => direction.current === 'vertical'
2828
const isHorizontal = () => direction.current === 'horizontal'
@@ -33,7 +33,7 @@ export default function useTouch() {
3333
offsetX.current = 0
3434
offsetY.current = 0
3535
direction.current = ''
36-
firstMove.current = false
36+
firstMove.current = null
3737
}
3838

3939
const start = ((event: TouchEvent) => {
@@ -49,7 +49,11 @@ export default function useTouch() {
4949
deltaY.current = touch.clientY - startY.current
5050
offsetX.current = Math.abs(deltaX.current)
5151
offsetY.current = Math.abs(deltaY.current)
52-
firstMove.current = !direction.current
52+
if (firstMove.current === null) {
53+
firstMove.current = true
54+
} else {
55+
firstMove.current = false
56+
}
5357

5458
if (!direction.current) {
5559
direction.current = getDirection(offsetX.current, offsetY.current)

0 commit comments

Comments
 (0)