From 61ac00689b4dd46520c73fe874492c4834dba54a Mon Sep 17 00:00:00 2001 From: Zephyr Date: Wed, 28 May 2025 19:16:46 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=20useLonePress=20?= =?UTF-8?q?=E5=9C=A8=E7=89=B9=E6=AE=8A=E8=AE=BE=E5=A4=87=E3=80=81=E7=89=B9?= =?UTF-8?q?=E6=AE=8A=E6=83=85=E5=86=B5=E4=B8=8B=E9=95=BF=E6=8C=89=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=E4=B8=8D=E8=A7=A6=E5=8F=91=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/src/useLongPress/index.ts | 119 ++++++++++++++++------- 1 file changed, 85 insertions(+), 34 deletions(-) diff --git a/packages/hooks/src/useLongPress/index.ts b/packages/hooks/src/useLongPress/index.ts index caa3aee13a..87cd6e1d0e 100644 --- a/packages/hooks/src/useLongPress/index.ts +++ b/packages/hooks/src/useLongPress/index.ts @@ -13,11 +13,6 @@ export interface Options { onLongPressEnd?: (event: EventType) => void; } -const touchSupported = - isBrowser && - // @ts-ignore - ('ontouchstart' in window || (window.DocumentTouch && document instanceof DocumentTouch)); - function useLongPress( onLongPress: (event: EventType) => void, target: BasicTarget, @@ -30,6 +25,8 @@ function useLongPress( const timerRef = useRef>(); const isTriggeredRef = useRef(false); const pervPositionRef = useRef({ x: 0, y: 0 }); + const mousePressed = useRef(false); + const touchPressed = useRef(false); const hasMoveThreshold = !!( (moveThreshold?.x && moveThreshold.x > 0) || (moveThreshold?.y && moveThreshold.y > 0) @@ -60,7 +57,6 @@ function useLongPress( clientY: event.touches[0].clientY, }; } - if (event instanceof MouseEvent) { return { clientX: event.clientX, @@ -73,64 +69,119 @@ function useLongPress( return { clientX: 0, clientY: 0 }; } - const onStart = (event: EventType) => { + const createTimer = (event: EventType) => { + timerRef.current = setTimeout(() => { + onLongPressRef.current(event); + isTriggeredRef.current = true; + }, delay); + }; + + const onTouchStart = (event: TouchEvent) => { + if (touchPressed.current) return; + touchPressed.current = true; + if (hasMoveThreshold) { const { clientX, clientY } = getClientPosition(event); pervPositionRef.current.x = clientX; pervPositionRef.current.y = clientY; } - timerRef.current = setTimeout(() => { - onLongPressRef.current(event); - isTriggeredRef.current = true; - }, delay); + createTimer(event); }; - const onMove = (event: TouchEvent) => { + const onMouseDown = (event: MouseEvent) => { + if ((event as any)?.sourceCapabilities?.firesTouchEvents) return; + + mousePressed.current = true; + + if (hasMoveThreshold) { + pervPositionRef.current.x = event.clientX; + pervPositionRef.current.y = event.clientY; + } + createTimer(event); + }; + + const onMove = (event: EventType) => { if (timerRef.current && overThreshold(event)) { clearTimeout(timerRef.current); timerRef.current = undefined; } }; - const onEnd = (event: EventType, shouldTriggerClick: boolean = false) => { + const onTouchEnd = (event: TouchEvent) => { + if (!touchPressed.current) return; + touchPressed.current = false; + if (timerRef.current) { clearTimeout(timerRef.current); + timerRef.current = undefined; } + if (isTriggeredRef.current) { onLongPressEndRef.current?.(event); + } else if (onClickRef.current) { + onClickRef.current(event); + } + isTriggeredRef.current = false; + }; + + const onMouseUp = (event: MouseEvent) => { + if ((event as any)?.sourceCapabilities?.firesTouchEvents) return; + if (!mousePressed.current) return; + mousePressed.current = false; + + if (timerRef.current) { + clearTimeout(timerRef.current); + timerRef.current = undefined; } - if (shouldTriggerClick && !isTriggeredRef.current && onClickRef.current) { + + if (isTriggeredRef.current) { + onLongPressEndRef.current?.(event); + } else if (onClickRef.current) { onClickRef.current(event); } isTriggeredRef.current = false; }; - const onEndWithClick = (event: EventType) => onEnd(event, true); - - if (!touchSupported) { - targetElement.addEventListener('mousedown', onStart); - targetElement.addEventListener('mouseup', onEndWithClick); - targetElement.addEventListener('mouseleave', onEnd); - if (hasMoveThreshold) targetElement.addEventListener('mousemove', onMove); - } else { - targetElement.addEventListener('touchstart', onStart); - targetElement.addEventListener('touchend', onEndWithClick); - if (hasMoveThreshold) targetElement.addEventListener('touchmove', onMove); + const onMouseLeave = (event: MouseEvent) => { + if (!mousePressed.current) return; + mousePressed.current = false; + + if (timerRef.current) { + clearTimeout(timerRef.current); + timerRef.current = undefined; + } + if (isTriggeredRef.current) { + onLongPressEndRef.current?.(event); + isTriggeredRef.current = false; + } + }; + + targetElement.addEventListener('mousedown', onMouseDown); + targetElement.addEventListener('mouseup', onMouseUp); + targetElement.addEventListener('mouseleave', onMouseLeave); + targetElement.addEventListener('touchstart', onTouchStart); + targetElement.addEventListener('touchend', onTouchEnd); + + if (hasMoveThreshold) { + targetElement.addEventListener('mousemove', onMove); + targetElement.addEventListener('touchmove', onMove); } + return () => { if (timerRef.current) { clearTimeout(timerRef.current); isTriggeredRef.current = false; } - if (!touchSupported) { - targetElement.removeEventListener('mousedown', onStart); - targetElement.removeEventListener('mouseup', onEndWithClick); - targetElement.removeEventListener('mouseleave', onEnd); - if (hasMoveThreshold) targetElement.removeEventListener('mousemove', onMove); - } else { - targetElement.removeEventListener('touchstart', onStart); - targetElement.removeEventListener('touchend', onEndWithClick); - if (hasMoveThreshold) targetElement.removeEventListener('touchmove', onMove); + + targetElement.removeEventListener('mousedown', onMouseDown); + targetElement.removeEventListener('mouseup', onMouseUp); + targetElement.removeEventListener('mouseleave', onMouseLeave); + targetElement.removeEventListener('touchstart', onTouchStart); + targetElement.removeEventListener('touchend', onTouchEnd); + + if (hasMoveThreshold) { + targetElement.removeEventListener('mousemove', onMove); + targetElement.removeEventListener('touchmove', onMove); } }; },