From 980e92007059c10c2ef5c226e5856aae61ea5091 Mon Sep 17 00:00:00 2001 From: KartikKar19 Date: Sat, 18 Oct 2025 12:19:55 +0530 Subject: [PATCH 1/2] fix(DomeGallery): Resolve mobile tap/touch conflict and sizing animation --- .../Components/DomeGallery/DomeGallery.jsx | 103 ++++++++++-------- 1 file changed, 56 insertions(+), 47 deletions(-) diff --git a/src/content/Components/DomeGallery/DomeGallery.jsx b/src/content/Components/DomeGallery/DomeGallery.jsx index a8edbbf7..b1512b4c 100644 --- a/src/content/Components/DomeGallery/DomeGallery.jsx +++ b/src/content/Components/DomeGallery/DomeGallery.jsx @@ -142,7 +142,9 @@ export default function DomeGallery({ const openingRef = useRef(false); const openStartedAtRef = useRef(0); const lastDragEndAt = useRef(0); - + const cancelTapRef = useRef(false); + const pointerTypeRef = useRef('mouse'); + const tapTargetRef = useRef(null); const scrollLockedRef = useRef(false); const lockScroll = useCallback(() => { if (scrollLockedRef.current) return; @@ -300,49 +302,89 @@ export default function DomeGallery({ onDragStart: ({ event }) => { if (focusedElRef.current) return; stopInertia(); - const evt = event; + + pointerTypeRef.current = event.pointerType || 'mouse'; + if (pointerTypeRef.current === 'touch') event.preventDefault(); + if (pointerTypeRef.current === 'touch') lockScroll(); // Lock scroll on touch start + draggingRef.current = true; + cancelTapRef.current = false; movedRef.current = false; startRotRef.current = { ...rotationRef.current }; - startPosRef.current = { x: evt.clientX, y: evt.clientY }; + startPosRef.current = { x: event.clientX, y: event.clientY }; + + const potential = event.target.closest?.('.item__image'); + tapTargetRef.current = potential || null; }, - onDrag: ({ event, last, velocity = [0, 0], direction = [0, 0], movement }) => { + onDrag: ({ event, last, velocity: velArr = [0, 0], direction: dirArr = [0, 0], movement }) => { if (focusedElRef.current || !draggingRef.current || !startPosRef.current) return; - const evt = event; - const dxTotal = evt.clientX - startPosRef.current.x; - const dyTotal = evt.clientY - startPosRef.current.y; + + if (pointerTypeRef.current === 'touch') event.preventDefault(); + + const dxTotal = event.clientX - startPosRef.current.x; + const dyTotal = event.clientY - startPosRef.current.y; + if (!movedRef.current) { const dist2 = dxTotal * dxTotal + dyTotal * dyTotal; if (dist2 > 16) movedRef.current = true; } + const nextX = clamp( startRotRef.current.x - dyTotal / dragSensitivity, -maxVerticalRotationDeg, maxVerticalRotationDeg ); const nextY = wrapAngleSigned(startRotRef.current.y + dxTotal / dragSensitivity); + if (rotationRef.current.x !== nextX || rotationRef.current.y !== nextY) { rotationRef.current = { x: nextX, y: nextY }; applyTransform(nextX, nextY); } + if (last) { draggingRef.current = false; - let [vMagX, vMagY] = velocity; - const [dirX, dirY] = direction; + let isTap = false; + + if (startPosRef.current) { + const dx = event.clientX - startPosRef.current.x; + const dy = event.clientY - startPosRef.current.y; + const dist2 = dx * dx + dy * dy; + const TAP_THRESH_PX = pointerTypeRef.current === 'touch' ? 10 : 6; + if (dist2 <= TAP_THRESH_PX * TAP_THRESH_PX) { + isTap = true; + } + } + + let [vMagX, vMagY] = velArr; + const [dirX, dirY] = dirArr; let vx = vMagX * dirX; let vy = vMagY * dirY; - if (Math.abs(vx) < 0.001 && Math.abs(vy) < 0.001 && Array.isArray(movement)) { + + if (!isTap && Math.abs(vx) < 0.001 && Math.abs(vy) < 0.001 && Array.isArray(movement)) { const [mx, my] = movement; vx = clamp((mx / dragSensitivity) * 0.02, -1.2, 1.2); vy = clamp((my / dragSensitivity) * 0.02, -1.2, 1.2); } - if (Math.abs(vx) > 0.005 || Math.abs(vy) > 0.005) startInertia(vx, vy); + + if (!isTap && (Math.abs(vx) > 0.005 || Math.abs(vy) > 0.005)) { + startInertia(vx, vy); + } + startPosRef.current = null; + cancelTapRef.current = !isTap; + + if (isTap && tapTargetRef.current && !focusedElRef.current) { + openItemFromElement(tapTargetRef.current); + } + tapTargetRef.current = null; + + if (cancelTapRef.current) setTimeout(() => (cancelTapRef.current = false), 120); if (movedRef.current) lastDragEndAt.current = performance.now(); movedRef.current = false; + if (pointerTypeRef.current === 'touch') unlockScroll(); // Unlock scroll on drag end } } }, - { target: mainRef, eventOptions: { passive: true } } + { target: mainRef, eventOptions: { passive: false } } ); useEffect(() => { @@ -541,37 +583,6 @@ export default function DomeGallery({ [enlargeTransitionMs, lockScroll, openedImageHeight, openedImageWidth, segments] ); - const onTileClick = useCallback( - e => { - if (draggingRef.current) return; - if (performance.now() - lastDragEndAt.current < 80) return; - if (openingRef.current) return; - openItemFromElement(e.currentTarget); - }, - [openItemFromElement] - ); - - const onTilePointerUp = useCallback( - e => { - if (e.pointerType !== 'touch') return; - if (draggingRef.current) return; - if (performance.now() - lastDragEndAt.current < 80) return; - if (openingRef.current) return; - openItemFromElement(e.currentTarget); - }, - [openItemFromElement] - ); - - const onTileTouchEnd = useCallback( - e => { - if (draggingRef.current) return; - if (performance.now() - lastDragEndAt.current < 80) return; - if (openingRef.current) return; - openItemFromElement(e.currentTarget); - }, - [openItemFromElement] - ); - useEffect(() => { return () => { document.body.classList.remove('dg-scroll-lock'); @@ -611,13 +622,11 @@ export default function DomeGallery({ }} >
{it.alt}
From 990fa00e6556a622c03181ac8e5d54da69616fc9 Mon Sep 17 00:00:00 2001 From: KartikKar19 Date: Sat, 18 Oct 2025 13:42:22 +0530 Subject: [PATCH 2/2] fix(DomeGallery): Resolve mobile tap/touch conflict and enlargement animation across all variants --- .../Components/DomeGallery/DomeGallery.jsx | 8 - .../Components/DomeGallery/DomeGallery.tsx | 157 +++++++++--------- .../Components/DomeGallery/DomeGallery.tsx | 8 - 3 files changed, 81 insertions(+), 92 deletions(-) diff --git a/src/tailwind/Components/DomeGallery/DomeGallery.jsx b/src/tailwind/Components/DomeGallery/DomeGallery.jsx index 22207168..7150d51f 100644 --- a/src/tailwind/Components/DomeGallery/DomeGallery.jsx +++ b/src/tailwind/Components/DomeGallery/DomeGallery.jsx @@ -796,14 +796,6 @@ export default function DomeGallery({ role="button" tabIndex={0} aria-label={it.alt || 'Open image'} - onClick={e => { - if (performance.now() - lastDragEndAt.current < 80) return; - openItemFromElement(e.currentTarget); - }} - onTouchEnd={e => { - if (performance.now() - lastDragEndAt.current < 80) return; - openItemFromElement(e.currentTarget); - }} style={{ inset: '10px', borderRadius: `var(--tile-radius, ${imageBorderRadius})`, diff --git a/src/ts-default/Components/DomeGallery/DomeGallery.tsx b/src/ts-default/Components/DomeGallery/DomeGallery.tsx index 37cb5346..9e0ada66 100644 --- a/src/ts-default/Components/DomeGallery/DomeGallery.tsx +++ b/src/ts-default/Components/DomeGallery/DomeGallery.tsx @@ -182,7 +182,9 @@ export default function DomeGallery({ const openingRef = useRef(false); const openStartedAtRef = useRef(0); const lastDragEndAt = useRef(0); - + const cancelTapRef = useRef(false); + const pointerTypeRef = useRef<'mouse' | 'pen' | 'touch'>('mouse'); + const tapTargetRef = useRef(null); const scrollLockedRef = useRef(false); const lockScroll = useCallback(() => { if (scrollLockedRef.current) return; @@ -338,66 +340,95 @@ export default function DomeGallery({ ); useGesture( - { - onDragStart: ({ event }) => { - if (focusedElRef.current) return; - stopInertia(); - const evt = event as PointerEvent; - draggingRef.current = true; - movedRef.current = false; - startRotRef.current = { ...rotationRef.current }; - startPosRef.current = { x: evt.clientX, y: evt.clientY }; - }, - onDrag: ({ event, last, velocity = [0, 0], direction = [0, 0], movement }) => { - if (focusedElRef.current || !draggingRef.current || !startPosRef.current) return; - - const evt = event as PointerEvent; - const dxTotal = evt.clientX - startPosRef.current.x; - const dyTotal = evt.clientY - startPosRef.current.y; - - if (!movedRef.current) { - const dist2 = dxTotal * dxTotal + dyTotal * dyTotal; - if (dist2 > 16) movedRef.current = true; - } + { + onDragStart: ({ event }) => { + if (focusedElRef.current) return; + stopInertia(); - const nextX = clamp( - startRotRef.current.x - dyTotal / dragSensitivity, - -maxVerticalRotationDeg, - maxVerticalRotationDeg - ); - const nextY = wrapAngleSigned(startRotRef.current.y + dxTotal / dragSensitivity); + const evt = event as PointerEvent; + pointerTypeRef.current = (evt.pointerType as any) || 'mouse'; + if (pointerTypeRef.current === 'touch') evt.preventDefault(); + if (pointerTypeRef.current === 'touch') lockScroll(); + + draggingRef.current = true; + cancelTapRef.current = false; + movedRef.current = false; + startRotRef.current = { ...rotationRef.current }; + startPosRef.current = { x: evt.clientX, y: evt.clientY }; + const potential = (evt.target as Element).closest?.('.item__image') as HTMLElement | null; + tapTargetRef.current = potential || null; + }, + onDrag: ({ event, last, velocity: velArr = [0, 0], direction: dirArr = [0, 0], movement }) => { + if (focusedElRef.current || !draggingRef.current || !startPosRef.current) return; - if (rotationRef.current.x !== nextX || rotationRef.current.y !== nextY) { - rotationRef.current = { x: nextX, y: nextY }; - applyTransform(nextX, nextY); - } + const evt = event as PointerEvent; + if (pointerTypeRef.current === 'touch') evt.preventDefault(); - if (last) { - draggingRef.current = false; + const dxTotal = evt.clientX - startPosRef.current.x; + const dyTotal = evt.clientY - startPosRef.current.y; - let [vMagX, vMagY] = velocity; - const [dirX, dirY] = direction; - let vx = vMagX * dirX; - let vy = vMagY * dirY; + if (!movedRef.current) { + const dist2 = dxTotal * dxTotal + dyTotal * dyTotal; + if (dist2 > 16) movedRef.current = true; + } - if (Math.abs(vx) < 0.001 && Math.abs(vy) < 0.001 && Array.isArray(movement)) { - const [mx, my] = movement; - vx = clamp((mx / dragSensitivity) * 0.02, -1.2, 1.2); - vy = clamp((my / dragSensitivity) * 0.02, -1.2, 1.2); - } + const nextX = clamp( + startRotRef.current.x - dyTotal / dragSensitivity, + -maxVerticalRotationDeg, + maxVerticalRotationDeg + ); + const nextY = wrapAngleSigned(startRotRef.current.y + dxTotal / dragSensitivity); - if (Math.abs(vx) > 0.005 || Math.abs(vy) > 0.005) { - startInertia(vx, vy); + if (rotationRef.current.x !== nextX || rotationRef.current.y !== nextY) { + rotationRef.current = { x: nextX, y: nextY }; + applyTransform(nextX, nextY); + } + + if (last) { + draggingRef.current = false; + let isTap = false; + + if (startPosRef.current) { + const dx = evt.clientX - startPosRef.current.x; + const dy = evt.clientY - startPosRef.current.y; + const dist2 = dx * dx + dy * dy; + const TAP_THRESH_PX = pointerTypeRef.current === 'touch' ? 10 : 6; + if (dist2 <= TAP_THRESH_PX * TAP_THRESH_PX) { + isTap = true; } + } + + let [vMagX, vMagY] = velArr; + const [dirX, dirY] = dirArr; + let vx = vMagX * dirX; + let vy = vMagY * dirY; + + if (!isTap && Math.abs(vx) < 0.001 && Math.abs(vy) < 0.001 && Array.isArray(movement)) { + const [mx, my] = movement; + vx = clamp((mx / dragSensitivity) * 0.02, -1.2, 1.2); + vy = clamp((my / dragSensitivity) * 0.02, -1.2, 1.2); + } - if (movedRef.current) lastDragEndAt.current = performance.now(); + if (!isTap && (Math.abs(vx) > 0.005 || Math.abs(vy) > 0.005)) { + startInertia(vx, vy); + } + startPosRef.current = null; + cancelTapRef.current = !isTap; - movedRef.current = false; + if (isTap && tapTargetRef.current && !focusedElRef.current) { + openItemFromElement(tapTargetRef.current); } + tapTargetRef.current = null; + + if (cancelTapRef.current) setTimeout(() => (cancelTapRef.current = false), 120); + if (movedRef.current) lastDragEndAt.current = performance.now(); + movedRef.current = false; + if (pointerTypeRef.current === 'touch') unlockScroll(); } - }, - { target: mainRef, eventOptions: { passive: true } } - ); + } + }, + { target: mainRef, eventOptions: { passive: false } } +); const openItemFromElement = (el: HTMLElement) => { if (openingRef.current) return; @@ -507,29 +538,6 @@ export default function DomeGallery({ overlay.addEventListener('transitionend', onFirstEnd); } }; - - const onTileClick = useCallback((e: React.MouseEvent) => { - if (draggingRef.current) return; - if (performance.now() - lastDragEndAt.current < 80) return; - if (openingRef.current) return; - openItemFromElement(e.currentTarget); - }, []); - - const onTilePointerUp = useCallback((e: React.PointerEvent) => { - if (e.pointerType !== 'touch') return; - if (draggingRef.current) return; - if (performance.now() - lastDragEndAt.current < 80) return; - if (openingRef.current) return; - openItemFromElement(e.currentTarget); - }, []); - - const onTileTouchEnd = useCallback((e: React.TouchEvent) => { - if (draggingRef.current) return; - if (performance.now() - lastDragEndAt.current < 80) return; - if (openingRef.current) return; - openItemFromElement(e.currentTarget); - }, []); - useEffect(() => { const scrim = scrimRef.current; if (!scrim) return; @@ -716,9 +724,6 @@ export default function DomeGallery({ role="button" tabIndex={0} aria-label={it.alt || 'Open image'} - onClick={onTileClick} - onPointerUp={onTilePointerUp} - onTouchEnd={onTileTouchEnd} > {it.alt} diff --git a/src/ts-tailwind/Components/DomeGallery/DomeGallery.tsx b/src/ts-tailwind/Components/DomeGallery/DomeGallery.tsx index 6827f35d..820ce7ea 100644 --- a/src/ts-tailwind/Components/DomeGallery/DomeGallery.tsx +++ b/src/ts-tailwind/Components/DomeGallery/DomeGallery.tsx @@ -810,14 +810,6 @@ export default function DomeGallery({ role="button" tabIndex={0} aria-label={it.alt || 'Open image'} - onClick={e => { - if (performance.now() - lastDragEndAt.current < 80) return; - openItemFromElement(e.currentTarget as HTMLElement); - }} - onTouchEnd={e => { - if (performance.now() - lastDragEndAt.current < 80) return; - openItemFromElement(e.currentTarget); - }} style={{ inset: '10px', borderRadius: `var(--tile-radius, ${imageBorderRadius})`,