@@ -2,10 +2,9 @@ import { useRef } from 'react';
22import useLatest from '../useLatest' ;
33import type { BasicTarget } from '../utils/domTarget' ;
44import { getTargetElement } from '../utils/domTarget' ;
5- import isBrowser from '../utils/isBrowser' ;
65import useEffectWithTarget from '../utils/useEffectWithTarget' ;
76
8- type EventType = MouseEvent | TouchEvent ;
7+ type EventType = PointerEvent ;
98export interface Options {
109 delay ?: number ;
1110 moveThreshold ?: { x ?: number ; y ?: number } ;
@@ -25,8 +24,8 @@ function useLongPress(
2524 const timerRef = useRef < ReturnType < typeof setTimeout > > ( ) ;
2625 const isTriggeredRef = useRef ( false ) ;
2726 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 ) ;
3029 const hasMoveThreshold = ! ! (
3130 ( moveThreshold ?. x && moveThreshold . x > 0 ) ||
3231 ( moveThreshold ?. y && moveThreshold . y > 0 )
@@ -40,94 +39,56 @@ function useLongPress(
4039 }
4140
4241 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 ) ;
4644
4745 return ! ! (
4846 ( moveThreshold ?. x && offsetX > moveThreshold . x ) ||
4947 ( moveThreshold ?. y && offsetY > moveThreshold . y )
5048 ) ;
5149 } ;
5250
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-
7251 const createTimer = ( event : EventType ) => {
7352 timerRef . current = setTimeout ( ( ) => {
7453 onLongPressRef . current ( event ) ;
7554 isTriggeredRef . current = true ;
7655 } , delay ) ;
7756 } ;
7857
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 ;
8261
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 ;
9064
91- const onMouseDown = ( event : MouseEvent ) => {
92- if ( ( event as any ) ?. sourceCapabilities ?. firesTouchEvents ) return ;
93-
94- mousePressed . current = true ;
65+ // 捕获指针以确保即使指针移出元素也能接收到事件
66+ targetElement . setPointerCapture ( event . pointerId ) ;
9567
9668 if ( hasMoveThreshold ) {
9769 pervPositionRef . current . x = event . clientX ;
9870 pervPositionRef . current . y = event . clientY ;
9971 }
72+
10073 createTimer ( event ) ;
10174 } ;
10275
103- const onMove = ( event : EventType ) => {
76+ const onPointerMove = ( event : PointerEvent ) => {
77+ // 只处理已按下的指针
78+ if ( ! isPressed . current || event . pointerId !== pointerId . current ) return ;
79+
10480 if ( timerRef . current && overThreshold ( event ) ) {
10581 clearTimeout ( timerRef . current ) ;
10682 timerRef . current = undefined ;
10783 }
10884 } ;
10985
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 ;
11389
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 ;
13192
13293 if ( timerRef . current ) {
13394 clearTimeout ( timerRef . current ) ;
@@ -139,32 +100,34 @@ function useLongPress(
139100 } else if ( onClickRef . current ) {
140101 onClickRef . current ( event ) ;
141102 }
103+
142104 isTriggeredRef . current = false ;
143105 } ;
144106
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 ;
148113
149114 if ( timerRef . current ) {
150115 clearTimeout ( timerRef . current ) ;
151116 timerRef . current = undefined ;
152117 }
118+
153119 if ( isTriggeredRef . current ) {
154120 onLongPressEndRef . current ?.( event ) ;
155121 isTriggeredRef . current = false ;
156122 }
157123 } ;
158124
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 ) ;
164128
165129 if ( hasMoveThreshold ) {
166- targetElement . addEventListener ( 'mousemove' , onMove ) ;
167- targetElement . addEventListener ( 'touchmove' , onMove ) ;
130+ targetElement . addEventListener ( 'pointermove' , onPointerMove ) ;
168131 }
169132
170133 return ( ) => {
@@ -173,15 +136,12 @@ function useLongPress(
173136 isTriggeredRef . current = false ;
174137 }
175138
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 ) ;
181142
182143 if ( hasMoveThreshold ) {
183- targetElement . removeEventListener ( 'mousemove' , onMove ) ;
184- targetElement . removeEventListener ( 'touchmove' , onMove ) ;
144+ targetElement . removeEventListener ( 'pointermove' , onPointerMove ) ;
185145 }
186146 } ;
187147 } ,
0 commit comments