@@ -2,10 +2,9 @@ import { useRef } from 'react';
2
2
import useLatest from '../useLatest' ;
3
3
import type { BasicTarget } from '../utils/domTarget' ;
4
4
import { getTargetElement } from '../utils/domTarget' ;
5
- import isBrowser from '../utils/isBrowser' ;
6
5
import useEffectWithTarget from '../utils/useEffectWithTarget' ;
7
6
8
- type EventType = MouseEvent | TouchEvent ;
7
+ type EventType = PointerEvent ;
9
8
export interface Options {
10
9
delay ?: number ;
11
10
moveThreshold ?: { x ?: number ; y ?: number } ;
@@ -25,8 +24,8 @@ function useLongPress(
25
24
const timerRef = useRef < ReturnType < typeof setTimeout > > ( ) ;
26
25
const isTriggeredRef = useRef ( false ) ;
27
26
const pervPositionRef = useRef ( { x : 0 , y : 0 } ) ;
28
- const mousePressed = useRef ( false ) ;
29
- const touchPressed = useRef ( false ) ;
27
+ const isPressed = useRef ( false ) ;
28
+ const pointerId = useRef < number | null > ( null ) ;
30
29
const hasMoveThreshold = ! ! (
31
30
( moveThreshold ?. x && moveThreshold . x > 0 ) ||
32
31
( moveThreshold ?. y && moveThreshold . y > 0 )
@@ -40,94 +39,56 @@ function useLongPress(
40
39
}
41
40
42
41
const overThreshold = ( event : EventType ) => {
43
- const { clientX, clientY } = getClientPosition ( event ) ;
44
- const offsetX = Math . abs ( clientX - pervPositionRef . current . x ) ;
45
- const offsetY = Math . abs ( clientY - pervPositionRef . current . y ) ;
42
+ const offsetX = Math . abs ( event . clientX - pervPositionRef . current . x ) ;
43
+ const offsetY = Math . abs ( event . clientY - pervPositionRef . current . y ) ;
46
44
47
45
return ! ! (
48
46
( moveThreshold ?. x && offsetX > moveThreshold . x ) ||
49
47
( moveThreshold ?. y && offsetY > moveThreshold . y )
50
48
) ;
51
49
} ;
52
50
53
- function getClientPosition ( event : EventType ) {
54
- if ( 'TouchEvent' in window && event instanceof TouchEvent ) {
55
- return {
56
- clientX : event . touches [ 0 ] . clientX ,
57
- clientY : event . touches [ 0 ] . clientY ,
58
- } ;
59
- }
60
- if ( event instanceof MouseEvent ) {
61
- return {
62
- clientX : event . clientX ,
63
- clientY : event . clientY ,
64
- } ;
65
- }
66
-
67
- console . warn ( 'Unsupported event type' ) ;
68
-
69
- return { clientX : 0 , clientY : 0 } ;
70
- }
71
-
72
51
const createTimer = ( event : EventType ) => {
73
52
timerRef . current = setTimeout ( ( ) => {
74
53
onLongPressRef . current ( event ) ;
75
54
isTriggeredRef . current = true ;
76
55
} , delay ) ;
77
56
} ;
78
57
79
- const onTouchStart = ( event : TouchEvent ) => {
80
- if ( touchPressed . current ) return ;
81
- touchPressed . current = true ;
58
+ const onPointerDown = ( event : PointerEvent ) => {
59
+ // 只处理第一个按下的指针
60
+ if ( isPressed . current ) return ;
82
61
83
- if ( hasMoveThreshold ) {
84
- const { clientX, clientY } = getClientPosition ( event ) ;
85
- pervPositionRef . current . x = clientX ;
86
- pervPositionRef . current . y = clientY ;
87
- }
88
- createTimer ( event ) ;
89
- } ;
62
+ isPressed . current = true ;
63
+ pointerId . current = event . pointerId ;
90
64
91
- const onMouseDown = ( event : MouseEvent ) => {
92
- if ( ( event as any ) ?. sourceCapabilities ?. firesTouchEvents ) return ;
93
-
94
- mousePressed . current = true ;
65
+ // 捕获指针以确保即使指针移出元素也能接收到事件
66
+ targetElement . setPointerCapture ( event . pointerId ) ;
95
67
96
68
if ( hasMoveThreshold ) {
97
69
pervPositionRef . current . x = event . clientX ;
98
70
pervPositionRef . current . y = event . clientY ;
99
71
}
72
+
100
73
createTimer ( event ) ;
101
74
} ;
102
75
103
- const onMove = ( event : EventType ) => {
76
+ const onPointerMove = ( event : PointerEvent ) => {
77
+ // 只处理已按下的指针
78
+ if ( ! isPressed . current || event . pointerId !== pointerId . current ) return ;
79
+
104
80
if ( timerRef . current && overThreshold ( event ) ) {
105
81
clearTimeout ( timerRef . current ) ;
106
82
timerRef . current = undefined ;
107
83
}
108
84
} ;
109
85
110
- const onTouchEnd = ( event : TouchEvent ) => {
111
- if ( ! touchPressed . current ) return ;
112
- touchPressed . current = false ;
86
+ const onPointerUp = ( event : PointerEvent ) => {
87
+ // 只处理已按下的指针
88
+ if ( ! isPressed . current || event . pointerId !== pointerId . current ) return ;
113
89
114
- if ( timerRef . current ) {
115
- clearTimeout ( timerRef . current ) ;
116
- timerRef . current = undefined ;
117
- }
118
-
119
- if ( isTriggeredRef . current ) {
120
- onLongPressEndRef . current ?.( event ) ;
121
- } else if ( onClickRef . current ) {
122
- onClickRef . current ( event ) ;
123
- }
124
- isTriggeredRef . current = false ;
125
- } ;
126
-
127
- const onMouseUp = ( event : MouseEvent ) => {
128
- if ( ( event as any ) ?. sourceCapabilities ?. firesTouchEvents ) return ;
129
- if ( ! mousePressed . current ) return ;
130
- mousePressed . current = false ;
90
+ isPressed . current = false ;
91
+ pointerId . current = null ;
131
92
132
93
if ( timerRef . current ) {
133
94
clearTimeout ( timerRef . current ) ;
@@ -139,32 +100,34 @@ function useLongPress(
139
100
} else if ( onClickRef . current ) {
140
101
onClickRef . current ( event ) ;
141
102
}
103
+
142
104
isTriggeredRef . current = false ;
143
105
} ;
144
106
145
- const onMouseLeave = ( event : MouseEvent ) => {
146
- if ( ! mousePressed . current ) return ;
147
- mousePressed . current = false ;
107
+ const onPointerCancel = ( event : PointerEvent ) => {
108
+ // 只处理已按下的指针
109
+ if ( ! isPressed . current || event . pointerId !== pointerId . current ) return ;
110
+
111
+ isPressed . current = false ;
112
+ pointerId . current = null ;
148
113
149
114
if ( timerRef . current ) {
150
115
clearTimeout ( timerRef . current ) ;
151
116
timerRef . current = undefined ;
152
117
}
118
+
153
119
if ( isTriggeredRef . current ) {
154
120
onLongPressEndRef . current ?.( event ) ;
155
121
isTriggeredRef . current = false ;
156
122
}
157
123
} ;
158
124
159
- targetElement . addEventListener ( 'mousedown' , onMouseDown ) ;
160
- targetElement . addEventListener ( 'mouseup' , onMouseUp ) ;
161
- targetElement . addEventListener ( 'mouseleave' , onMouseLeave ) ;
162
- targetElement . addEventListener ( 'touchstart' , onTouchStart ) ;
163
- targetElement . addEventListener ( 'touchend' , onTouchEnd ) ;
125
+ targetElement . addEventListener ( 'pointerdown' , onPointerDown ) ;
126
+ targetElement . addEventListener ( 'pointerup' , onPointerUp ) ;
127
+ targetElement . addEventListener ( 'pointercancel' , onPointerCancel ) ;
164
128
165
129
if ( hasMoveThreshold ) {
166
- targetElement . addEventListener ( 'mousemove' , onMove ) ;
167
- targetElement . addEventListener ( 'touchmove' , onMove ) ;
130
+ targetElement . addEventListener ( 'pointermove' , onPointerMove ) ;
168
131
}
169
132
170
133
return ( ) => {
@@ -173,15 +136,12 @@ function useLongPress(
173
136
isTriggeredRef . current = false ;
174
137
}
175
138
176
- targetElement . removeEventListener ( 'mousedown' , onMouseDown ) ;
177
- targetElement . removeEventListener ( 'mouseup' , onMouseUp ) ;
178
- targetElement . removeEventListener ( 'mouseleave' , onMouseLeave ) ;
179
- targetElement . removeEventListener ( 'touchstart' , onTouchStart ) ;
180
- targetElement . removeEventListener ( 'touchend' , onTouchEnd ) ;
139
+ targetElement . removeEventListener ( 'pointerdown' , onPointerDown ) ;
140
+ targetElement . removeEventListener ( 'pointerup' , onPointerUp ) ;
141
+ targetElement . removeEventListener ( 'pointercancel' , onPointerCancel ) ;
181
142
182
143
if ( hasMoveThreshold ) {
183
- targetElement . removeEventListener ( 'mousemove' , onMove ) ;
184
- targetElement . removeEventListener ( 'touchmove' , onMove ) ;
144
+ targetElement . removeEventListener ( 'pointermove' , onPointerMove ) ;
185
145
}
186
146
} ;
187
147
} ,
0 commit comments