react-native-gesture-image-viewer 2.4.0 → 3.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/lib/module/GestureViewer.js +96 -147
- package/lib/module/GestureViewer.js.map +1 -1
- package/lib/module/GestureViewerManager.js +49 -105
- package/lib/module/GestureViewerManager.js.map +1 -1
- package/lib/module/gestureViewerAnimation.js +18 -0
- package/lib/module/gestureViewerAnimation.js.map +1 -0
- package/lib/module/gestureViewerGestures.js +12 -0
- package/lib/module/gestureViewerGestures.js.map +1 -0
- package/lib/module/gestureViewerPaging.js +66 -0
- package/lib/module/gestureViewerPaging.js.map +1 -0
- package/lib/module/gestureViewerRenderWindow.js +29 -0
- package/lib/module/gestureViewerRenderWindow.js.map +1 -0
- package/lib/module/getWebContentProps.js +6 -0
- package/lib/module/getWebContentProps.js.map +1 -0
- package/lib/module/getWebContentProps.web.js +8 -0
- package/lib/module/getWebContentProps.web.js.map +1 -0
- package/lib/module/platformTapGestures.js +4 -0
- package/lib/module/platformTapGestures.js.map +1 -0
- package/lib/module/platformTapGestures.web.js +4 -0
- package/lib/module/platformTapGestures.web.js.map +1 -0
- package/lib/module/renderWindow.js +160 -0
- package/lib/module/renderWindow.js.map +1 -0
- package/lib/module/useGestureViewer.js +459 -211
- package/lib/module/useGestureViewer.js.map +1 -1
- package/lib/module/useGestureViewerController.js +3 -3
- package/lib/module/useGestureViewerController.js.map +1 -1
- package/lib/module/useGestureViewerManagerBridge.js +85 -0
- package/lib/module/useGestureViewerManagerBridge.js.map +1 -0
- package/lib/module/useGestureViewerPaging.js +168 -79
- package/lib/module/useGestureViewerPaging.js.map +1 -1
- package/lib/module/useWebClickHandler.js +7 -0
- package/lib/module/useWebClickHandler.js.map +1 -0
- package/lib/module/useWebClickHandler.web.js +85 -0
- package/lib/module/useWebClickHandler.web.js.map +1 -0
- package/lib/module/useWebSingleTapTimer.js +12 -0
- package/lib/module/useWebSingleTapTimer.js.map +1 -0
- package/lib/module/useWebSingleTapTimer.web.js +25 -0
- package/lib/module/useWebSingleTapTimer.web.js.map +1 -0
- package/lib/module/utils/index.js +0 -77
- package/lib/module/utils/index.js.map +1 -1
- package/lib/module/utils/tapZoom.js +4 -3
- package/lib/module/utils/tapZoom.js.map +1 -1
- package/lib/typescript/src/GestureViewer.d.ts +1 -1
- package/lib/typescript/src/GestureViewer.d.ts.map +1 -1
- package/lib/typescript/src/GestureViewerManager.d.ts +20 -20
- package/lib/typescript/src/GestureViewerManager.d.ts.map +1 -1
- package/lib/typescript/src/gestureViewerAnimation.d.ts +15 -0
- package/lib/typescript/src/gestureViewerAnimation.d.ts.map +1 -0
- package/lib/typescript/src/gestureViewerGestures.d.ts +10 -0
- package/lib/typescript/src/gestureViewerGestures.d.ts.map +1 -0
- package/lib/typescript/src/gestureViewerPaging.d.ts +13 -0
- package/lib/typescript/src/gestureViewerPaging.d.ts.map +1 -0
- package/lib/typescript/src/gestureViewerRenderWindow.d.ts +15 -0
- package/lib/typescript/src/gestureViewerRenderWindow.d.ts.map +1 -0
- package/lib/typescript/src/getWebContentProps.d.ts +3 -0
- package/lib/typescript/src/getWebContentProps.d.ts.map +1 -0
- package/lib/typescript/src/getWebContentProps.web.d.ts +5 -0
- package/lib/typescript/src/getWebContentProps.web.d.ts.map +1 -0
- package/lib/typescript/src/platformTapGestures.d.ts +2 -0
- package/lib/typescript/src/platformTapGestures.d.ts.map +1 -0
- package/lib/typescript/src/platformTapGestures.web.d.ts +2 -0
- package/lib/typescript/src/platformTapGestures.web.d.ts.map +1 -0
- package/lib/typescript/src/renderWindow.d.ts +44 -0
- package/lib/typescript/src/renderWindow.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +36 -53
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useGestureViewer.d.ts +11 -10
- package/lib/typescript/src/useGestureViewer.d.ts.map +1 -1
- package/lib/typescript/src/useGestureViewerController.d.ts +1 -1
- package/lib/typescript/src/useGestureViewerManagerBridge.d.ts +28 -0
- package/lib/typescript/src/useGestureViewerManagerBridge.d.ts.map +1 -0
- package/lib/typescript/src/useGestureViewerPaging.d.ts +33 -2
- package/lib/typescript/src/useGestureViewerPaging.d.ts.map +1 -1
- package/lib/typescript/src/useWebClickHandler.d.ts +35 -0
- package/lib/typescript/src/useWebClickHandler.d.ts.map +1 -0
- package/lib/typescript/src/useWebClickHandler.web.d.ts +36 -0
- package/lib/typescript/src/useWebClickHandler.web.d.ts.map +1 -0
- package/lib/typescript/src/useWebSingleTapTimer.d.ts +6 -0
- package/lib/typescript/src/useWebSingleTapTimer.d.ts.map +1 -0
- package/lib/typescript/src/useWebSingleTapTimer.web.d.ts +6 -0
- package/lib/typescript/src/useWebSingleTapTimer.web.d.ts.map +1 -0
- package/lib/typescript/src/utils/index.d.ts +0 -20
- package/lib/typescript/src/utils/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/tapZoom.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/GestureViewer.tsx +113 -208
- package/src/GestureViewerManager.ts +77 -122
- package/src/gestureViewerAnimation.ts +18 -0
- package/src/gestureViewerGestures.ts +20 -0
- package/src/gestureViewerPaging.ts +116 -0
- package/src/gestureViewerRenderWindow.ts +56 -0
- package/src/getWebContentProps.ts +5 -0
- package/src/getWebContentProps.web.ts +5 -0
- package/src/platformTapGestures.ts +1 -0
- package/src/platformTapGestures.web.ts +1 -0
- package/src/renderWindow.ts +253 -0
- package/src/types.ts +34 -100
- package/src/useGestureViewer.ts +665 -275
- package/src/useGestureViewerController.ts +3 -3
- package/src/useGestureViewerManagerBridge.ts +118 -0
- package/src/useGestureViewerPaging.ts +306 -112
- package/src/useWebClickHandler.ts +44 -0
- package/src/useWebClickHandler.web.ts +146 -0
- package/src/useWebSingleTapTimer.ts +10 -0
- package/src/useWebSingleTapTimer.web.ts +30 -0
- package/src/utils/index.ts +0 -102
- package/src/utils/tapZoom.ts +4 -3
- package/lib/module/WebPagingFixStyle.js +0 -25
- package/lib/module/WebPagingFixStyle.js.map +0 -1
- package/lib/module/scheduleInitialScroll.js +0 -21
- package/lib/module/scheduleInitialScroll.js.map +0 -1
- package/lib/module/useGestureViewerPaging.types.js +0 -4
- package/lib/module/useGestureViewerPaging.types.js.map +0 -1
- package/lib/module/useGestureViewerPaging.web.js +0 -291
- package/lib/module/useGestureViewerPaging.web.js.map +0 -1
- package/lib/module/utils/FlashList.js +0 -9
- package/lib/module/utils/FlashList.js.map +0 -1
- package/lib/module/utils/webScroll.js +0 -70
- package/lib/module/utils/webScroll.js.map +0 -1
- package/lib/typescript/src/WebPagingFixStyle.d.ts +0 -6
- package/lib/typescript/src/WebPagingFixStyle.d.ts.map +0 -1
- package/lib/typescript/src/scheduleInitialScroll.d.ts +0 -3
- package/lib/typescript/src/scheduleInitialScroll.d.ts.map +0 -1
- package/lib/typescript/src/useGestureViewerPaging.types.d.ts +0 -42
- package/lib/typescript/src/useGestureViewerPaging.types.d.ts.map +0 -1
- package/lib/typescript/src/useGestureViewerPaging.web.d.ts +0 -3
- package/lib/typescript/src/useGestureViewerPaging.web.d.ts.map +0 -1
- package/lib/typescript/src/utils/FlashList.d.ts +0 -2
- package/lib/typescript/src/utils/FlashList.d.ts.map +0 -1
- package/lib/typescript/src/utils/webScroll.d.ts +0 -18
- package/lib/typescript/src/utils/webScroll.d.ts.map +0 -1
- package/src/WebPagingFixStyle.tsx +0 -25
- package/src/scheduleInitialScroll.ts +0 -36
- package/src/useGestureViewerPaging.types.ts +0 -42
- package/src/useGestureViewerPaging.web.ts +0 -453
- package/src/utils/FlashList.ts +0 -7
- package/src/utils/webScroll.ts +0 -96
package/src/useGestureViewer.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
-
import {
|
|
1
|
+
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { type View, useWindowDimensions } from 'react-native';
|
|
3
3
|
import { Gesture, type GestureType } from 'react-native-gesture-handler';
|
|
4
4
|
import {
|
|
5
5
|
Easing,
|
|
6
|
+
cancelAnimation,
|
|
6
7
|
interpolate,
|
|
7
8
|
useAnimatedReaction,
|
|
8
9
|
useAnimatedStyle,
|
|
@@ -12,28 +13,48 @@ import {
|
|
|
12
13
|
} from 'react-native-reanimated';
|
|
13
14
|
import { scheduleOnRN } from 'react-native-worklets';
|
|
14
15
|
|
|
16
|
+
import {
|
|
17
|
+
PAGE_SPRING_CONFIG,
|
|
18
|
+
SWIPE_THRESHOLD_RATIO,
|
|
19
|
+
SWIPE_VELOCITY_THRESHOLD,
|
|
20
|
+
} from './gestureViewerAnimation';
|
|
15
21
|
import type GestureViewerManager from './GestureViewerManager';
|
|
22
|
+
import { normalizeHorizontalSwipeThreshold } from './gestureViewerPaging';
|
|
16
23
|
import { registry } from './GestureViewerRegistry';
|
|
17
|
-
import {
|
|
24
|
+
import { resolveGestureViewerRenderWindow } from './gestureViewerRenderWindow';
|
|
25
|
+
import { enableNativeTapGestures } from './platformTapGestures';
|
|
26
|
+
import {
|
|
27
|
+
type NavigationOptions,
|
|
28
|
+
getLogicalIndex,
|
|
29
|
+
getPageStride,
|
|
30
|
+
getVirtualIndexForLogicalIndex,
|
|
31
|
+
normalizePageSpacing,
|
|
32
|
+
clampIndex,
|
|
33
|
+
resolveNavigation,
|
|
34
|
+
shouldRunNavigationDuringTransition,
|
|
35
|
+
} from './renderWindow';
|
|
18
36
|
import type { GestureViewerProps, TriggerRect } from './types';
|
|
37
|
+
import { useGestureViewerManagerBridge } from './useGestureViewerManagerBridge';
|
|
19
38
|
import { useGestureViewerPaging } from './useGestureViewerPaging';
|
|
20
|
-
import {
|
|
39
|
+
import { type EmitSingleTap, useWebClickHandler } from './useWebClickHandler';
|
|
40
|
+
import { useWebSingleTapTimer } from './useWebSingleTapTimer';
|
|
41
|
+
import { createBoundsConstraint } from './utils';
|
|
21
42
|
import { getDismissDistance, shouldDismissByDirection } from './utils/dismiss';
|
|
22
43
|
import { applyTapZoomAtPoint } from './utils/tapZoom';
|
|
23
44
|
import { calculateFocalPointTranslation, shouldAcceptFocalPoint } from './utils/zoom';
|
|
24
45
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
| '
|
|
46
|
+
const NATIVE_TAP_MAX_DISTANCE = 10;
|
|
47
|
+
|
|
48
|
+
const isValidTriggerRect = (rect: TriggerRect | null): rect is TriggerRect => {
|
|
49
|
+
return !!rect && rect.width > 0 && rect.height > 0;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
type UseGestureViewerProps<ItemT> = Omit<
|
|
53
|
+
GestureViewerProps<ItemT>,
|
|
54
|
+
'renderItem' | 'renderContainer' | 'containerStyle' | 'backdropStyle'
|
|
34
55
|
>;
|
|
35
56
|
|
|
36
|
-
export const useGestureViewer = <ItemT
|
|
57
|
+
export const useGestureViewer = <ItemT>({
|
|
37
58
|
data,
|
|
38
59
|
initialIndex = 0,
|
|
39
60
|
onDismiss,
|
|
@@ -42,21 +63,35 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
42
63
|
dismiss,
|
|
43
64
|
enableDoubleTapZoom = true,
|
|
44
65
|
enablePinchZoom = true,
|
|
45
|
-
|
|
66
|
+
horizontalSwipe,
|
|
46
67
|
enablePanWhenZoomed = true,
|
|
47
68
|
enableLoop = false,
|
|
48
69
|
maxZoomScale = 2,
|
|
49
|
-
|
|
70
|
+
pageSpacing = 0,
|
|
71
|
+
windowSize = 3,
|
|
50
72
|
height: customHeight,
|
|
51
73
|
id = 'default',
|
|
52
74
|
onDismissStart,
|
|
53
75
|
triggerAnimation,
|
|
54
76
|
autoPlay = false,
|
|
55
77
|
autoPlayInterval = 3000,
|
|
56
|
-
}: UseGestureViewerProps<ItemT
|
|
78
|
+
}: UseGestureViewerProps<ItemT>) => {
|
|
57
79
|
const { width: screenWidth, height: screenHeight } = useWindowDimensions();
|
|
58
80
|
const width = customWidth || screenWidth;
|
|
59
81
|
const height = customHeight || screenHeight;
|
|
82
|
+
const dataLength = data?.length || 0;
|
|
83
|
+
const normalizedPageSpacing = normalizePageSpacing(pageSpacing);
|
|
84
|
+
const pageStride = getPageStride(width, normalizedPageSpacing);
|
|
85
|
+
const initialCurrentIndex = clampIndex(initialIndex, dataLength);
|
|
86
|
+
const horizontalSwipeEnabled = horizontalSwipe?.enabled ?? true;
|
|
87
|
+
const horizontalSwipeDistanceThresholdRatio = normalizeHorizontalSwipeThreshold(
|
|
88
|
+
horizontalSwipe?.distanceThresholdRatio,
|
|
89
|
+
SWIPE_THRESHOLD_RATIO,
|
|
90
|
+
);
|
|
91
|
+
const horizontalSwipeVelocityThreshold = normalizeHorizontalSwipeThreshold(
|
|
92
|
+
horizontalSwipe?.velocityThreshold,
|
|
93
|
+
SWIPE_VELOCITY_THRESHOLD,
|
|
94
|
+
);
|
|
60
95
|
|
|
61
96
|
const dismissGestureRef = useRef<GestureType>(undefined);
|
|
62
97
|
|
|
@@ -64,21 +99,30 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
64
99
|
const [isRotated, setIsRotated] = useState(false);
|
|
65
100
|
const [isPinching, setIsPinching] = useState(false);
|
|
66
101
|
const [shouldStartTriggerAnimation, setShouldStartTriggerAnimation] = useState(false);
|
|
67
|
-
const [
|
|
68
|
-
const [
|
|
69
|
-
const [activeTriggerNode, setActiveTriggerNode] = useState<View | null>(
|
|
102
|
+
const [currentIndex, setCurrentIndex] = useState(initialCurrentIndex);
|
|
103
|
+
const [centerVirtualIndex, setCenterVirtualIndex] = useState(initialCurrentIndex);
|
|
104
|
+
const [activeTriggerNode, setActiveTriggerNode] = useState<View | null>(() =>
|
|
105
|
+
registry.getActiveTriggerNode(id),
|
|
106
|
+
);
|
|
107
|
+
const [isTriggerOpening, setIsTriggerOpening] = useState(
|
|
108
|
+
() => !!registry.getActiveTriggerNode(id),
|
|
109
|
+
);
|
|
70
110
|
|
|
71
|
-
const listRef = useRef<any>(null);
|
|
72
111
|
const triggerRectRef = useRef<TriggerRect | null>(null);
|
|
73
|
-
const pendingIndexRef = useRef(
|
|
112
|
+
const pendingIndexRef = useRef(initialCurrentIndex);
|
|
113
|
+
const currentIndexRef = useRef(initialCurrentIndex);
|
|
114
|
+
const centerVirtualIndexRef = useRef(initialCurrentIndex);
|
|
115
|
+
const previousInitialIndexRef = useRef(initialIndex);
|
|
116
|
+
const hasResolvedInitialIndexRef = useRef(dataLength > 0);
|
|
74
117
|
const onAnimationCompleteRef = useRef(triggerAnimation?.onAnimationComplete);
|
|
75
118
|
const onSingleTapRef = useRef(onSingleTap);
|
|
76
119
|
const dataRef = useRef(data);
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
120
|
+
const dataLengthRef = useRef(dataLength);
|
|
121
|
+
const enableLoopRef = useRef(enableLoop);
|
|
122
|
+
const managerRef = useRef<GestureViewerManager | null>(null);
|
|
123
|
+
const isZoomedRef = useRef(isZoomed);
|
|
124
|
+
const isRotatedRef = useRef(isRotated);
|
|
125
|
+
const isPinchingRef = useRef(isPinching);
|
|
82
126
|
|
|
83
127
|
const initialTranslateY = useSharedValue(0);
|
|
84
128
|
const initialTranslateX = useSharedValue(0);
|
|
@@ -87,22 +131,24 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
87
131
|
const translateY = useSharedValue(0);
|
|
88
132
|
const translateX = useSharedValue(0);
|
|
89
133
|
const scale = useSharedValue(1);
|
|
90
|
-
const backdropOpacity = useSharedValue(1);
|
|
91
134
|
const rotation = useSharedValue(0);
|
|
92
135
|
|
|
93
136
|
const triggerScale = useSharedValue(1);
|
|
94
137
|
const triggerTranslateX = useSharedValue(0);
|
|
95
138
|
const triggerTranslateY = useSharedValue(0);
|
|
96
|
-
const triggerOpacity = useSharedValue(1);
|
|
139
|
+
const triggerOpacity = useSharedValue(activeTriggerNode ? 0 : 1);
|
|
140
|
+
|
|
141
|
+
const hasZoomChangeListeners = useSharedValue(false);
|
|
142
|
+
const hasRotationChangeListeners = useSharedValue(false);
|
|
97
143
|
|
|
98
144
|
const lastFocalX = useSharedValue(0);
|
|
99
145
|
const lastFocalY = useSharedValue(0);
|
|
100
146
|
const startFocalX = useSharedValue(0);
|
|
101
147
|
const startFocalY = useSharedValue(0);
|
|
102
148
|
const hasActiveFocal = useSharedValue(false);
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
const
|
|
149
|
+
const nativeInteractionHadMultipleTouches = useSharedValue(false);
|
|
150
|
+
const suppressNativeTap = useSharedValue(false);
|
|
151
|
+
const { clearPendingWebSingleTap, scheduleWebSingleTap } = useWebSingleTapTimer();
|
|
106
152
|
|
|
107
153
|
const animationConfig = useMemo(
|
|
108
154
|
() => ({
|
|
@@ -130,28 +176,24 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
130
176
|
],
|
|
131
177
|
);
|
|
132
178
|
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
179
|
+
const {
|
|
180
|
+
centerVirtualIndex: renderCenterVirtualIndex,
|
|
181
|
+
currentIndex: renderCurrentIndex,
|
|
182
|
+
slots: renderWindowSlots,
|
|
183
|
+
} = resolveGestureViewerRenderWindow({
|
|
184
|
+
centerVirtualIndex,
|
|
185
|
+
currentIndex,
|
|
186
|
+
data,
|
|
187
|
+
enableLoop,
|
|
188
|
+
isTriggerOpening,
|
|
189
|
+
windowSize,
|
|
190
|
+
});
|
|
140
191
|
|
|
141
192
|
const constrainTranslation = useMemo(
|
|
142
193
|
() => createBoundsConstraint({ height, width }),
|
|
143
194
|
[width, height],
|
|
144
195
|
);
|
|
145
196
|
|
|
146
|
-
const scrollTo = useCallback(
|
|
147
|
-
(index: number, animated: boolean) => {
|
|
148
|
-
const scrollAction = createScrollAction(listRef.current, width + itemSpacing);
|
|
149
|
-
|
|
150
|
-
return scrollAction.scrollTo(index, animated);
|
|
151
|
-
},
|
|
152
|
-
[width, itemSpacing],
|
|
153
|
-
);
|
|
154
|
-
|
|
155
197
|
const resetTransformState = useCallback(() => {
|
|
156
198
|
translateX.set(withTiming(0));
|
|
157
199
|
translateY.set(withTiming(0));
|
|
@@ -162,38 +204,180 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
162
204
|
rotation.set(0);
|
|
163
205
|
}, [initialTranslateX, initialTranslateY, rotation, scale, startScale, translateX, translateY]);
|
|
164
206
|
|
|
165
|
-
const
|
|
166
|
-
(
|
|
167
|
-
|
|
207
|
+
const resetTransformStateImmediately = useCallback(() => {
|
|
208
|
+
cancelAnimation(translateX);
|
|
209
|
+
cancelAnimation(translateY);
|
|
210
|
+
cancelAnimation(initialTranslateX);
|
|
211
|
+
cancelAnimation(initialTranslateY);
|
|
212
|
+
cancelAnimation(startScale);
|
|
213
|
+
cancelAnimation(scale);
|
|
214
|
+
cancelAnimation(rotation);
|
|
215
|
+
translateX.set(0);
|
|
216
|
+
translateY.set(0);
|
|
217
|
+
initialTranslateX.set(0);
|
|
218
|
+
initialTranslateY.set(0);
|
|
219
|
+
startScale.set(1);
|
|
220
|
+
scale.set(1);
|
|
221
|
+
rotation.set(0);
|
|
222
|
+
}, [initialTranslateX, initialTranslateY, rotation, scale, startScale, translateX, translateY]);
|
|
223
|
+
|
|
224
|
+
const commitCurrentIndex = useCallback((nextIndex: number) => {
|
|
225
|
+
pendingIndexRef.current = nextIndex;
|
|
226
|
+
currentIndexRef.current = nextIndex;
|
|
227
|
+
setCurrentIndex(nextIndex);
|
|
228
|
+
managerRef.current?.notifyStateChange();
|
|
229
|
+
}, []);
|
|
230
|
+
|
|
231
|
+
const commitVirtualIndexOnly = useCallback(
|
|
232
|
+
(nextVirtualIndex: number) => {
|
|
233
|
+
const nextLogicalIndex = getLogicalIndex(
|
|
234
|
+
nextVirtualIndex,
|
|
235
|
+
dataLengthRef.current,
|
|
236
|
+
enableLoopRef.current,
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
if (nextLogicalIndex === null) {
|
|
168
240
|
return;
|
|
169
241
|
}
|
|
170
242
|
|
|
171
|
-
|
|
243
|
+
centerVirtualIndexRef.current = nextVirtualIndex;
|
|
244
|
+
setCenterVirtualIndex(nextVirtualIndex);
|
|
245
|
+
commitCurrentIndex(nextLogicalIndex);
|
|
172
246
|
},
|
|
173
|
-
[
|
|
247
|
+
[commitCurrentIndex],
|
|
174
248
|
);
|
|
175
249
|
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
250
|
+
const {
|
|
251
|
+
animateToVirtualPage,
|
|
252
|
+
horizontalPagingGesture,
|
|
253
|
+
isPageTransitioningRef,
|
|
254
|
+
pageTransitionLocked,
|
|
255
|
+
snapToVirtualPage,
|
|
256
|
+
visualPage,
|
|
257
|
+
} = useGestureViewerPaging({
|
|
258
|
+
centerVirtualIndex,
|
|
259
|
+
clearPendingWebSingleTap,
|
|
260
|
+
commitVirtualIndexOnly,
|
|
261
|
+
currentIndex,
|
|
262
|
+
dataLength,
|
|
263
|
+
enableLoop,
|
|
264
|
+
horizontalSwipeDistanceThresholdRatio,
|
|
265
|
+
horizontalSwipeEnabled,
|
|
266
|
+
horizontalSwipeVelocityThreshold,
|
|
267
|
+
initialPage: initialCurrentIndex,
|
|
268
|
+
isPinching,
|
|
269
|
+
isRotated,
|
|
270
|
+
isTriggerOpening,
|
|
271
|
+
isZoomed,
|
|
272
|
+
pageStride,
|
|
273
|
+
suppressNativeTap,
|
|
274
|
+
width,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
const navigateToIndex = useCallback(
|
|
278
|
+
(targetIndex: number, options?: NavigationOptions, preferredDirection?: -1 | 1) => {
|
|
279
|
+
const resolution = resolveNavigation({
|
|
280
|
+
currentIndex: currentIndexRef.current,
|
|
281
|
+
dataLength: dataLengthRef.current,
|
|
282
|
+
enableLoop: enableLoopRef.current,
|
|
283
|
+
preferredDirection,
|
|
284
|
+
targetIndex,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
if (resolution.kind === 'noop') {
|
|
179
288
|
return;
|
|
180
289
|
}
|
|
181
290
|
|
|
182
|
-
|
|
291
|
+
if (
|
|
292
|
+
isPageTransitioningRef.current &&
|
|
293
|
+
!shouldRunNavigationDuringTransition(resolution, options)
|
|
294
|
+
) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const targetVirtualIndex =
|
|
299
|
+
resolution.kind === 'step'
|
|
300
|
+
? centerVirtualIndexRef.current + resolution.direction
|
|
301
|
+
: getVirtualIndexForLogicalIndex(
|
|
302
|
+
resolution.targetIndex,
|
|
303
|
+
centerVirtualIndexRef.current,
|
|
304
|
+
dataLengthRef.current,
|
|
305
|
+
enableLoopRef.current,
|
|
306
|
+
);
|
|
183
307
|
|
|
184
|
-
|
|
308
|
+
if (targetVirtualIndex === null) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
185
311
|
|
|
186
|
-
if (
|
|
312
|
+
if (resolution.kind !== 'step' || options?.animated === false) {
|
|
313
|
+
resetTransformStateImmediately();
|
|
314
|
+
snapToVirtualPage(targetVirtualIndex);
|
|
187
315
|
return;
|
|
188
316
|
}
|
|
189
317
|
|
|
190
|
-
manager.setCurrentIndex(nextIndex);
|
|
191
|
-
manager.notifyStateChange();
|
|
192
318
|
resetTransformState();
|
|
319
|
+
animateToVirtualPage(targetVirtualIndex);
|
|
193
320
|
},
|
|
194
|
-
[
|
|
321
|
+
[
|
|
322
|
+
animateToVirtualPage,
|
|
323
|
+
isPageTransitioningRef,
|
|
324
|
+
resetTransformStateImmediately,
|
|
325
|
+
resetTransformState,
|
|
326
|
+
snapToVirtualPage,
|
|
327
|
+
],
|
|
195
328
|
);
|
|
196
329
|
|
|
330
|
+
const navigateByDirection = useCallback(
|
|
331
|
+
(direction: -1 | 1) => {
|
|
332
|
+
const currentDataLength = dataLengthRef.current;
|
|
333
|
+
|
|
334
|
+
if (currentDataLength <= 0) {
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const nextIndex = currentIndexRef.current + direction;
|
|
339
|
+
|
|
340
|
+
if (nextIndex >= 0 && nextIndex < currentDataLength) {
|
|
341
|
+
navigateToIndex(nextIndex, undefined, direction);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (!enableLoopRef.current || currentDataLength <= 1) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
navigateToIndex(direction === 1 ? 0 : currentDataLength - 1, undefined, direction);
|
|
350
|
+
},
|
|
351
|
+
[navigateToIndex],
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
const navigateToNext = useCallback(() => {
|
|
355
|
+
navigateByDirection(1);
|
|
356
|
+
}, [navigateByDirection]);
|
|
357
|
+
|
|
358
|
+
const navigateToPrevious = useCallback(() => {
|
|
359
|
+
navigateByDirection(-1);
|
|
360
|
+
}, [navigateByDirection]);
|
|
361
|
+
|
|
362
|
+
useGestureViewerManagerBridge({
|
|
363
|
+
currentIndexRef,
|
|
364
|
+
dataLengthRef,
|
|
365
|
+
goToIndex: navigateToIndex,
|
|
366
|
+
goToNext: navigateToNext,
|
|
367
|
+
goToPrevious: navigateToPrevious,
|
|
368
|
+
hasRotationChangeListeners,
|
|
369
|
+
hasZoomChangeListeners,
|
|
370
|
+
height,
|
|
371
|
+
id,
|
|
372
|
+
managerRef,
|
|
373
|
+
maxZoomScale,
|
|
374
|
+
rotation,
|
|
375
|
+
scale,
|
|
376
|
+
translateX,
|
|
377
|
+
translateY,
|
|
378
|
+
width,
|
|
379
|
+
});
|
|
380
|
+
|
|
197
381
|
const emitZoomChange = useCallback((currentScale: number, prevScale: number | null) => {
|
|
198
382
|
managerRef.current?.emitZoomChange(currentScale, prevScale);
|
|
199
383
|
}, []);
|
|
@@ -202,146 +386,203 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
202
386
|
managerRef.current?.emitRotationChange(currentRotation, prevRotation);
|
|
203
387
|
}, []);
|
|
204
388
|
|
|
205
|
-
const
|
|
389
|
+
const finishTriggerOpening = useCallback(() => {
|
|
390
|
+
setIsTriggerOpening(false);
|
|
206
391
|
onAnimationCompleteRef.current?.();
|
|
207
392
|
}, []);
|
|
208
393
|
|
|
209
394
|
useAnimatedReaction(
|
|
210
395
|
() => scale.get(),
|
|
211
396
|
(currentScale, previousScale) => {
|
|
212
|
-
if (currentScale !== previousScale) {
|
|
397
|
+
if (currentScale !== previousScale && hasZoomChangeListeners.get()) {
|
|
213
398
|
scheduleOnRN(emitZoomChange, currentScale, previousScale);
|
|
214
399
|
}
|
|
215
400
|
|
|
216
|
-
|
|
401
|
+
const currentIsZoomed = currentScale > 1;
|
|
402
|
+
const previousIsZoomed = (previousScale ?? 1) > 1;
|
|
403
|
+
|
|
404
|
+
if (previousScale === null || currentIsZoomed !== previousIsZoomed) {
|
|
405
|
+
scheduleOnRN(setIsZoomed, currentIsZoomed);
|
|
406
|
+
}
|
|
217
407
|
},
|
|
218
408
|
);
|
|
219
409
|
|
|
220
410
|
useAnimatedReaction(
|
|
221
411
|
() => rotation.get(),
|
|
222
412
|
(currentRotation, previousRotation) => {
|
|
223
|
-
if (currentRotation !== previousRotation) {
|
|
413
|
+
if (currentRotation !== previousRotation && hasRotationChangeListeners.get()) {
|
|
224
414
|
scheduleOnRN(emitRotationChange, currentRotation, previousRotation);
|
|
225
415
|
}
|
|
226
416
|
|
|
227
|
-
|
|
417
|
+
const currentIsRotated = currentRotation % 360 !== 0;
|
|
418
|
+
const previousIsRotated = (previousRotation ?? 0) % 360 !== 0;
|
|
419
|
+
|
|
420
|
+
if (previousRotation === null || currentIsRotated !== previousIsRotated) {
|
|
421
|
+
scheduleOnRN(setIsRotated, currentIsRotated);
|
|
422
|
+
}
|
|
228
423
|
},
|
|
229
424
|
);
|
|
230
425
|
|
|
231
426
|
useEffect(() => {
|
|
232
|
-
|
|
427
|
+
return registry.subscribeToActiveTrigger(id, (node) => {
|
|
428
|
+
setActiveTriggerNode(node);
|
|
233
429
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
pendingIndexRef.current = state.currentIndex;
|
|
238
|
-
setCurrentIndex(state.currentIndex);
|
|
239
|
-
});
|
|
430
|
+
if (node) {
|
|
431
|
+
setIsTriggerOpening(true);
|
|
432
|
+
}
|
|
240
433
|
});
|
|
241
|
-
|
|
242
|
-
return () => {
|
|
243
|
-
unsubscribeFromRegistry();
|
|
244
|
-
unsubscribe?.();
|
|
245
|
-
};
|
|
246
434
|
}, [id]);
|
|
247
435
|
|
|
248
436
|
useEffect(() => {
|
|
249
|
-
|
|
250
|
-
}, [
|
|
437
|
+
dataRef.current = data;
|
|
438
|
+
}, [data]);
|
|
251
439
|
|
|
252
440
|
useEffect(() => {
|
|
253
|
-
|
|
441
|
+
isZoomedRef.current = isZoomed;
|
|
442
|
+
}, [isZoomed]);
|
|
254
443
|
|
|
255
|
-
|
|
256
|
-
|
|
444
|
+
useEffect(() => {
|
|
445
|
+
isRotatedRef.current = isRotated;
|
|
446
|
+
}, [isRotated]);
|
|
447
|
+
|
|
448
|
+
useEffect(() => {
|
|
449
|
+
isPinchingRef.current = isPinching;
|
|
450
|
+
}, [isPinching]);
|
|
451
|
+
|
|
452
|
+
useEffect(() => {
|
|
453
|
+
onSingleTapRef.current = onSingleTap;
|
|
454
|
+
}, [onSingleTap]);
|
|
455
|
+
|
|
456
|
+
useEffect(() => {
|
|
457
|
+
onAnimationCompleteRef.current = triggerAnimation?.onAnimationComplete;
|
|
458
|
+
}, [triggerAnimation?.onAnimationComplete]);
|
|
459
|
+
|
|
460
|
+
useLayoutEffect(() => {
|
|
461
|
+
const initialIndexChanged = previousInitialIndexRef.current !== initialIndex;
|
|
462
|
+
const shouldResolveInitialIndex =
|
|
463
|
+
initialIndexChanged || (!hasResolvedInitialIndexRef.current && dataLength > 0);
|
|
464
|
+
|
|
465
|
+
previousInitialIndexRef.current = initialIndex;
|
|
466
|
+
dataLengthRef.current = dataLength;
|
|
467
|
+
enableLoopRef.current = enableLoop;
|
|
468
|
+
|
|
469
|
+
hasResolvedInitialIndexRef.current = dataLength > 0;
|
|
470
|
+
|
|
471
|
+
const nextIndex = shouldResolveInitialIndex
|
|
472
|
+
? clampIndex(initialIndex, dataLength)
|
|
473
|
+
: clampIndex(currentIndexRef.current, dataLength);
|
|
474
|
+
|
|
475
|
+
const nextVirtualIndex =
|
|
476
|
+
getVirtualIndexForLogicalIndex(
|
|
477
|
+
nextIndex,
|
|
478
|
+
centerVirtualIndexRef.current,
|
|
479
|
+
dataLength,
|
|
480
|
+
enableLoop,
|
|
481
|
+
) ?? nextIndex;
|
|
482
|
+
|
|
483
|
+
const previousIndex = currentIndexRef.current;
|
|
484
|
+
const previousVirtualIndex = centerVirtualIndexRef.current;
|
|
485
|
+
const shouldSyncVirtualPage =
|
|
486
|
+
isPageTransitioningRef.current || previousVirtualIndex !== nextVirtualIndex;
|
|
487
|
+
const shouldResetTransform =
|
|
488
|
+
initialIndexChanged || previousIndex !== nextIndex || shouldSyncVirtualPage;
|
|
489
|
+
|
|
490
|
+
if (shouldSyncVirtualPage) {
|
|
491
|
+
snapToVirtualPage(nextVirtualIndex);
|
|
492
|
+
|
|
493
|
+
if (dataLength === 0) {
|
|
494
|
+
centerVirtualIndexRef.current = nextVirtualIndex;
|
|
495
|
+
setCenterVirtualIndex(nextVirtualIndex);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const shouldCommitIndexOutsidePaging = !shouldSyncVirtualPage || dataLength === 0;
|
|
500
|
+
|
|
501
|
+
if (shouldCommitIndexOutsidePaging && previousIndex !== nextIndex) {
|
|
502
|
+
commitCurrentIndex(nextIndex);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (shouldCommitIndexOutsidePaging && previousIndex === nextIndex) {
|
|
506
|
+
managerRef.current?.notifyStateChange();
|
|
257
507
|
}
|
|
258
508
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
manager.setWidth(width + itemSpacing);
|
|
263
|
-
manager.setHeight(height);
|
|
264
|
-
manager.setZoomSharedValues(scale, translateX, translateY, maxZoomScale);
|
|
265
|
-
manager.setResetTransformCallback(resetTransformState);
|
|
266
|
-
manager.setRotation(rotation);
|
|
267
|
-
manager.setEnableLoop(enableLoop);
|
|
268
|
-
manager.notifyStateChange();
|
|
509
|
+
if (shouldResetTransform) {
|
|
510
|
+
resetTransformState();
|
|
511
|
+
}
|
|
269
512
|
}, [
|
|
513
|
+
commitCurrentIndex,
|
|
270
514
|
dataLength,
|
|
271
|
-
enableHorizontalSwipe,
|
|
272
|
-
initialIndex,
|
|
273
|
-
manager,
|
|
274
|
-
width,
|
|
275
|
-
itemSpacing,
|
|
276
|
-
maxZoomScale,
|
|
277
515
|
enableLoop,
|
|
278
|
-
|
|
279
|
-
|
|
516
|
+
initialIndex,
|
|
517
|
+
isPageTransitioningRef,
|
|
280
518
|
resetTransformState,
|
|
281
|
-
|
|
282
|
-
translateY,
|
|
283
|
-
rotation,
|
|
519
|
+
snapToVirtualPage,
|
|
284
520
|
]);
|
|
285
521
|
|
|
286
522
|
useEffect(() => {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}, [manager]);
|
|
523
|
+
return () => {
|
|
524
|
+
triggerRectRef.current = null;
|
|
525
|
+
clearPendingWebSingleTap();
|
|
526
|
+
};
|
|
527
|
+
}, [clearPendingWebSingleTap]);
|
|
293
528
|
|
|
294
529
|
useEffect(() => {
|
|
295
|
-
const hasDataLengthChanged = previousDataLengthRef.current !== dataLength;
|
|
296
|
-
const hasValidInitialIndex = initialIndex >= 0 && initialIndex < dataLength;
|
|
297
|
-
|
|
298
|
-
previousDataLengthRef.current = dataLength;
|
|
299
|
-
translateY.set(0);
|
|
300
|
-
translateX.set(0);
|
|
301
|
-
scale.set(1);
|
|
302
|
-
backdropOpacity.set(1);
|
|
303
|
-
startScale.set(1);
|
|
304
|
-
rotation.set(0);
|
|
305
|
-
|
|
306
530
|
if (
|
|
307
|
-
!
|
|
308
|
-
|
|
309
|
-
|
|
531
|
+
!autoPlay ||
|
|
532
|
+
dataLength <= 1 ||
|
|
533
|
+
isTriggerOpening ||
|
|
534
|
+
isZoomed ||
|
|
535
|
+
isRotated ||
|
|
536
|
+
isPinching ||
|
|
537
|
+
(!enableLoop && currentIndex >= dataLength - 1)
|
|
310
538
|
) {
|
|
311
539
|
return;
|
|
312
540
|
}
|
|
313
541
|
|
|
314
|
-
|
|
315
|
-
scrollTo(adjustedInitialIndex, false);
|
|
316
|
-
});
|
|
317
|
-
}, [
|
|
318
|
-
adjustedInitialIndex,
|
|
319
|
-
dataLength,
|
|
320
|
-
initialIndex,
|
|
321
|
-
translateY,
|
|
322
|
-
backdropOpacity,
|
|
323
|
-
translateX,
|
|
324
|
-
scale,
|
|
325
|
-
startScale,
|
|
326
|
-
rotation,
|
|
327
|
-
scrollTo,
|
|
328
|
-
]);
|
|
542
|
+
const interval = Math.max(250, Math.floor(autoPlayInterval || 0));
|
|
329
543
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
544
|
+
if (!Number.isFinite(interval)) {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
333
547
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
548
|
+
const timer = setInterval(() => {
|
|
549
|
+
if (
|
|
550
|
+
isPageTransitioningRef.current ||
|
|
551
|
+
isZoomedRef.current ||
|
|
552
|
+
isRotatedRef.current ||
|
|
553
|
+
isPinchingRef.current
|
|
554
|
+
) {
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
337
557
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
558
|
+
const nextIndex =
|
|
559
|
+
currentIndexRef.current >= dataLengthRef.current - 1
|
|
560
|
+
? enableLoopRef.current
|
|
561
|
+
? 0
|
|
562
|
+
: null
|
|
563
|
+
: currentIndexRef.current + 1;
|
|
341
564
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
565
|
+
if (nextIndex === null) {
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
navigateToIndex(nextIndex);
|
|
570
|
+
}, interval);
|
|
571
|
+
|
|
572
|
+
return () => clearInterval(timer);
|
|
573
|
+
}, [
|
|
574
|
+
autoPlay,
|
|
575
|
+
autoPlayInterval,
|
|
576
|
+
currentIndex,
|
|
577
|
+
dataLength,
|
|
578
|
+
enableLoop,
|
|
579
|
+
isPinching,
|
|
580
|
+
isRotated,
|
|
581
|
+
isTriggerOpening,
|
|
582
|
+
isZoomed,
|
|
583
|
+
isPageTransitioningRef,
|
|
584
|
+
navigateToIndex,
|
|
585
|
+
]);
|
|
345
586
|
|
|
346
587
|
useEffect(() => {
|
|
347
588
|
if (shouldStartTriggerAnimation && triggerRectRef.current) {
|
|
@@ -360,7 +601,7 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
360
601
|
triggerScale.set(
|
|
361
602
|
withTiming(1, animationConfig, (finished) => {
|
|
362
603
|
if (finished) {
|
|
363
|
-
scheduleOnRN(
|
|
604
|
+
scheduleOnRN(finishTriggerOpening);
|
|
364
605
|
}
|
|
365
606
|
}),
|
|
366
607
|
);
|
|
@@ -385,11 +626,18 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
385
626
|
triggerScale,
|
|
386
627
|
triggerTranslateX,
|
|
387
628
|
triggerTranslateY,
|
|
388
|
-
|
|
629
|
+
finishTriggerOpening,
|
|
389
630
|
]);
|
|
390
631
|
|
|
391
632
|
useEffect(() => {
|
|
392
|
-
if (!activeTriggerNode
|
|
633
|
+
if (!activeTriggerNode) {
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if (typeof activeTriggerNode.measure !== 'function') {
|
|
638
|
+
setIsTriggerOpening(false);
|
|
639
|
+
triggerOpacity.set(1);
|
|
640
|
+
registry.clearActiveTriggerNode(id);
|
|
393
641
|
return;
|
|
394
642
|
}
|
|
395
643
|
|
|
@@ -402,6 +650,8 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
402
650
|
} satisfies TriggerRect;
|
|
403
651
|
|
|
404
652
|
if (!isValidTriggerRect(nextTriggerRect)) {
|
|
653
|
+
setIsTriggerOpening(false);
|
|
654
|
+
triggerOpacity.set(1);
|
|
405
655
|
registry.clearActiveTriggerNode(id);
|
|
406
656
|
return;
|
|
407
657
|
}
|
|
@@ -411,13 +661,7 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
411
661
|
setShouldStartTriggerAnimation(true);
|
|
412
662
|
registry.clearActiveTriggerNode(id);
|
|
413
663
|
});
|
|
414
|
-
}, [activeTriggerNode, id,
|
|
415
|
-
|
|
416
|
-
useEffect(() => {
|
|
417
|
-
return () => {
|
|
418
|
-
triggerRectRef.current = null;
|
|
419
|
-
};
|
|
420
|
-
}, []);
|
|
664
|
+
}, [activeTriggerNode, id, triggerOpacity]);
|
|
421
665
|
|
|
422
666
|
const animateDismissToRect = useCallback(
|
|
423
667
|
(rect: TriggerRect) => {
|
|
@@ -455,6 +699,10 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
455
699
|
}, [onDismiss]);
|
|
456
700
|
|
|
457
701
|
const handleDismiss = useCallback(() => {
|
|
702
|
+
if (isPageTransitioningRef.current) {
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
|
|
458
706
|
onDismissStart?.();
|
|
459
707
|
|
|
460
708
|
const dismissTargetIndex = pendingIndexRef.current;
|
|
@@ -490,23 +738,59 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
490
738
|
}
|
|
491
739
|
|
|
492
740
|
dismissWithoutTrigger();
|
|
493
|
-
}, [animateDismissToRect, dismissWithoutTrigger, id,
|
|
741
|
+
}, [animateDismissToRect, dismissWithoutTrigger, id, isPageTransitioningRef, onDismissStart]);
|
|
494
742
|
|
|
495
743
|
const dismissGesture = useMemo(() => {
|
|
496
|
-
const canDismiss = !isZoomed && dismissOptions.enabled;
|
|
744
|
+
const canDismiss = !isTriggerOpening && !isPinching && !isZoomed && dismissOptions.enabled;
|
|
745
|
+
const resetDismissTranslation = () => {
|
|
746
|
+
'worklet';
|
|
747
|
+
cancelAnimation(translateY);
|
|
748
|
+
translateY.set(0);
|
|
749
|
+
};
|
|
497
750
|
|
|
498
751
|
return Gesture.Pan()
|
|
499
752
|
.minDistance(10)
|
|
753
|
+
.maxPointers(1)
|
|
500
754
|
.averageTouches(true)
|
|
501
755
|
.activeCursor('grabbing')
|
|
502
756
|
.activeOffsetY([-10, 10])
|
|
503
757
|
.failOffsetX([-10, 10])
|
|
504
758
|
.withRef(dismissGestureRef)
|
|
505
759
|
.enabled(canDismiss)
|
|
760
|
+
.onTouchesDown((event, stateManager) => {
|
|
761
|
+
if (event.numberOfTouches === 1) {
|
|
762
|
+
nativeInteractionHadMultipleTouches.set(false);
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
if (event.numberOfTouches > 1) {
|
|
767
|
+
nativeInteractionHadMultipleTouches.set(true);
|
|
768
|
+
suppressNativeTap.set(true);
|
|
769
|
+
resetDismissTranslation();
|
|
770
|
+
stateManager.fail();
|
|
771
|
+
}
|
|
772
|
+
})
|
|
773
|
+
.onStart(() => {
|
|
774
|
+
suppressNativeTap.set(true);
|
|
775
|
+
})
|
|
506
776
|
.onUpdate((event) => {
|
|
777
|
+
if (nativeInteractionHadMultipleTouches.get() || pageTransitionLocked.get()) {
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
|
|
507
781
|
translateY.set(event.translationY / dismissOptions.resistance);
|
|
508
782
|
})
|
|
509
783
|
.onEnd((event) => {
|
|
784
|
+
if (nativeInteractionHadMultipleTouches.get()) {
|
|
785
|
+
resetDismissTranslation();
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
if (pageTransitionLocked.get()) {
|
|
790
|
+
resetDismissTranslation();
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
|
|
510
794
|
if (
|
|
511
795
|
canDismiss &&
|
|
512
796
|
shouldDismissByDirection(
|
|
@@ -519,17 +803,141 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
519
803
|
return;
|
|
520
804
|
}
|
|
521
805
|
|
|
522
|
-
translateY.set(
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
stiffness: 600,
|
|
529
|
-
}),
|
|
530
|
-
);
|
|
806
|
+
translateY.set(withSpring(0, PAGE_SPRING_CONFIG));
|
|
807
|
+
})
|
|
808
|
+
.onFinalize((_event, success) => {
|
|
809
|
+
if ((!success || nativeInteractionHadMultipleTouches.get()) && scale.get() <= 1) {
|
|
810
|
+
resetDismissTranslation();
|
|
811
|
+
}
|
|
531
812
|
});
|
|
532
|
-
}, [
|
|
813
|
+
}, [
|
|
814
|
+
dismissOptions,
|
|
815
|
+
handleDismiss,
|
|
816
|
+
isPinching,
|
|
817
|
+
isTriggerOpening,
|
|
818
|
+
isZoomed,
|
|
819
|
+
nativeInteractionHadMultipleTouches,
|
|
820
|
+
pageTransitionLocked,
|
|
821
|
+
scale,
|
|
822
|
+
suppressNativeTap,
|
|
823
|
+
translateY,
|
|
824
|
+
]);
|
|
825
|
+
|
|
826
|
+
const getCurrentTapTarget = useCallback((): { index: number; item: ItemT } | null => {
|
|
827
|
+
const index = pendingIndexRef.current;
|
|
828
|
+
|
|
829
|
+
if (index < 0 || index >= dataRef.current.length || !(index in dataRef.current)) {
|
|
830
|
+
return null;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
return { index, item: dataRef.current[index] as ItemT };
|
|
834
|
+
}, []);
|
|
835
|
+
|
|
836
|
+
const isWebInteractionLocked = useCallback(
|
|
837
|
+
() => isPageTransitioningRef.current || pageTransitionLocked.get(),
|
|
838
|
+
[isPageTransitioningRef, pageTransitionLocked],
|
|
839
|
+
);
|
|
840
|
+
|
|
841
|
+
const emitSingleTap = useCallback<EmitSingleTap<ItemT>>(
|
|
842
|
+
(x, y, tapTarget) => {
|
|
843
|
+
if (isPageTransitioningRef.current) {
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
const target = tapTarget ?? getCurrentTapTarget();
|
|
848
|
+
|
|
849
|
+
if (!target) {
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
const { index: targetIndex, item: targetItem } = target;
|
|
854
|
+
|
|
855
|
+
if (targetIndex < 0) {
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
managerRef.current?.emitTap({ kind: 'single', x, y, index: targetIndex });
|
|
860
|
+
onSingleTapRef.current?.({ x, y, index: targetIndex, item: targetItem });
|
|
861
|
+
},
|
|
862
|
+
[getCurrentTapTarget, isPageTransitioningRef],
|
|
863
|
+
);
|
|
864
|
+
|
|
865
|
+
const singleTapGesture = useMemo(
|
|
866
|
+
() =>
|
|
867
|
+
Gesture.Tap()
|
|
868
|
+
.enabled(enableNativeTapGestures)
|
|
869
|
+
.numberOfTaps(1)
|
|
870
|
+
.maxDistance(NATIVE_TAP_MAX_DISTANCE)
|
|
871
|
+
.onTouchesDown((event, stateManager) => {
|
|
872
|
+
if (event.numberOfTouches === 1) {
|
|
873
|
+
nativeInteractionHadMultipleTouches.set(false);
|
|
874
|
+
suppressNativeTap.set(false);
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
nativeInteractionHadMultipleTouches.set(true);
|
|
879
|
+
suppressNativeTap.set(true);
|
|
880
|
+
stateManager.fail();
|
|
881
|
+
})
|
|
882
|
+
.onEnd((event, success) => {
|
|
883
|
+
if (!success || pageTransitionLocked.get() || suppressNativeTap.get()) {
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
scheduleOnRN(emitSingleTap, event.x, event.y);
|
|
888
|
+
}),
|
|
889
|
+
[emitSingleTap, nativeInteractionHadMultipleTouches, pageTransitionLocked, suppressNativeTap],
|
|
890
|
+
);
|
|
891
|
+
|
|
892
|
+
const doubleTapGesture = useMemo(
|
|
893
|
+
() =>
|
|
894
|
+
Gesture.Tap()
|
|
895
|
+
.enabled(enableDoubleTapZoom && enableNativeTapGestures)
|
|
896
|
+
.numberOfTaps(2)
|
|
897
|
+
.maxDistance(NATIVE_TAP_MAX_DISTANCE)
|
|
898
|
+
.onTouchesDown((event, stateManager) => {
|
|
899
|
+
if (event.numberOfTouches === 1) {
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
nativeInteractionHadMultipleTouches.set(true);
|
|
904
|
+
suppressNativeTap.set(true);
|
|
905
|
+
stateManager.fail();
|
|
906
|
+
})
|
|
907
|
+
.onEnd((event) => {
|
|
908
|
+
if (pageTransitionLocked.get() || suppressNativeTap.get()) {
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
applyTapZoomAtPoint({
|
|
913
|
+
x: event.x,
|
|
914
|
+
y: event.y,
|
|
915
|
+
width,
|
|
916
|
+
height,
|
|
917
|
+
maxZoomScale,
|
|
918
|
+
scale,
|
|
919
|
+
translateX,
|
|
920
|
+
translateY,
|
|
921
|
+
});
|
|
922
|
+
}),
|
|
923
|
+
[
|
|
924
|
+
enableDoubleTapZoom,
|
|
925
|
+
height,
|
|
926
|
+
maxZoomScale,
|
|
927
|
+
nativeInteractionHadMultipleTouches,
|
|
928
|
+
pageTransitionLocked,
|
|
929
|
+
scale,
|
|
930
|
+
suppressNativeTap,
|
|
931
|
+
translateX,
|
|
932
|
+
translateY,
|
|
933
|
+
width,
|
|
934
|
+
],
|
|
935
|
+
);
|
|
936
|
+
|
|
937
|
+
const tapGesture = useMemo(
|
|
938
|
+
() => Gesture.Exclusive(doubleTapGesture, singleTapGesture),
|
|
939
|
+
[doubleTapGesture, singleTapGesture],
|
|
940
|
+
);
|
|
533
941
|
|
|
534
942
|
const zoomPinchGesture = useMemo(
|
|
535
943
|
() =>
|
|
@@ -537,11 +945,29 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
537
945
|
.enabled(enablePinchZoom)
|
|
538
946
|
.onTouchesDown((event) => {
|
|
539
947
|
if (event.numberOfTouches === 2) {
|
|
948
|
+
nativeInteractionHadMultipleTouches.set(true);
|
|
949
|
+
suppressNativeTap.set(true);
|
|
540
950
|
scheduleOnRN(setIsPinching, true);
|
|
541
951
|
}
|
|
542
952
|
})
|
|
543
953
|
.onStart((event) => {
|
|
544
|
-
|
|
954
|
+
if (pageTransitionLocked.get()) {
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
const currentScale = scale.get();
|
|
959
|
+
|
|
960
|
+
nativeInteractionHadMultipleTouches.set(true);
|
|
961
|
+
suppressNativeTap.set(true);
|
|
962
|
+
startScale.set(currentScale);
|
|
963
|
+
|
|
964
|
+
if (currentScale <= 1) {
|
|
965
|
+
cancelAnimation(translateX);
|
|
966
|
+
cancelAnimation(translateY);
|
|
967
|
+
translateX.set(0);
|
|
968
|
+
translateY.set(0);
|
|
969
|
+
}
|
|
970
|
+
|
|
545
971
|
initialTranslateX.set(translateX.get());
|
|
546
972
|
initialTranslateY.set(translateY.get());
|
|
547
973
|
startFocalX.set(event.focalX);
|
|
@@ -551,6 +977,10 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
551
977
|
hasActiveFocal.set(false);
|
|
552
978
|
})
|
|
553
979
|
.onUpdate((event) => {
|
|
980
|
+
if (pageTransitionLocked.get()) {
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
|
|
554
984
|
const initialScale = startScale.get();
|
|
555
985
|
const newScale = initialScale * event.scale;
|
|
556
986
|
|
|
@@ -612,6 +1042,10 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
612
1042
|
translateY.set(constrainedTranslateY);
|
|
613
1043
|
})
|
|
614
1044
|
.onEnd(() => {
|
|
1045
|
+
if (pageTransitionLocked.get()) {
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
615
1049
|
const currentScale = scale.get();
|
|
616
1050
|
|
|
617
1051
|
if (currentScale > maxZoomScale) {
|
|
@@ -684,20 +1118,47 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
684
1118
|
startFocalX,
|
|
685
1119
|
startFocalY,
|
|
686
1120
|
hasActiveFocal,
|
|
1121
|
+
nativeInteractionHadMultipleTouches,
|
|
1122
|
+
pageTransitionLocked,
|
|
1123
|
+
suppressNativeTap,
|
|
687
1124
|
],
|
|
688
1125
|
);
|
|
689
1126
|
|
|
690
1127
|
const zoomPanGesture = useMemo(
|
|
691
1128
|
() =>
|
|
692
1129
|
Gesture.Pan()
|
|
693
|
-
.enabled(enablePanWhenZoomed && isZoomed)
|
|
1130
|
+
.enabled(enablePanWhenZoomed && !isPinching && isZoomed)
|
|
1131
|
+
.maxPointers(1)
|
|
694
1132
|
.activeCursor('grabbing')
|
|
695
1133
|
.averageTouches(true)
|
|
1134
|
+
.onTouchesDown((event, stateManager) => {
|
|
1135
|
+
if (event.numberOfTouches === 1) {
|
|
1136
|
+
nativeInteractionHadMultipleTouches.set(false);
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
if (event.numberOfTouches > 1) {
|
|
1141
|
+
nativeInteractionHadMultipleTouches.set(true);
|
|
1142
|
+
suppressNativeTap.set(true);
|
|
1143
|
+
stateManager.fail();
|
|
1144
|
+
}
|
|
1145
|
+
})
|
|
696
1146
|
.onBegin(() => {
|
|
1147
|
+
if (nativeInteractionHadMultipleTouches.get() || pageTransitionLocked.get()) {
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
697
1151
|
initialTranslateX.set(translateX.get());
|
|
698
1152
|
initialTranslateY.set(translateY.get());
|
|
699
1153
|
})
|
|
1154
|
+
.onStart(() => {
|
|
1155
|
+
suppressNativeTap.set(true);
|
|
1156
|
+
})
|
|
700
1157
|
.onUpdate((event) => {
|
|
1158
|
+
if (nativeInteractionHadMultipleTouches.get() || pageTransitionLocked.get()) {
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
701
1162
|
const currentScale = scale.get();
|
|
702
1163
|
|
|
703
1164
|
if (currentScale > 1) {
|
|
@@ -719,78 +1180,38 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
719
1180
|
translateX,
|
|
720
1181
|
translateY,
|
|
721
1182
|
enablePanWhenZoomed,
|
|
1183
|
+
isPinching,
|
|
722
1184
|
isZoomed,
|
|
723
1185
|
scale,
|
|
724
1186
|
initialTranslateX,
|
|
725
1187
|
initialTranslateY,
|
|
726
1188
|
constrainTranslation,
|
|
1189
|
+
nativeInteractionHadMultipleTouches,
|
|
1190
|
+
pageTransitionLocked,
|
|
1191
|
+
suppressNativeTap,
|
|
727
1192
|
],
|
|
728
1193
|
);
|
|
729
1194
|
|
|
730
|
-
const emitSingleTap = useCallback((x: number, y: number) => {
|
|
731
|
-
const index = pendingIndexRef.current;
|
|
732
|
-
const currentData = dataRef.current;
|
|
733
|
-
|
|
734
|
-
if (index < 0 || index >= currentData.length) {
|
|
735
|
-
return;
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
managerRef.current?.emitTap({ kind: 'single', x, y, index });
|
|
739
|
-
|
|
740
|
-
const item = currentData[index];
|
|
741
|
-
|
|
742
|
-
if (item === undefined) {
|
|
743
|
-
return;
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
onSingleTapRef.current?.({ x, y, index, item });
|
|
747
|
-
}, []);
|
|
748
|
-
|
|
749
|
-
const singleTapGesture = useMemo(
|
|
750
|
-
() =>
|
|
751
|
-
Gesture.Tap()
|
|
752
|
-
.enabled(Platform.OS !== 'web')
|
|
753
|
-
.numberOfTaps(1)
|
|
754
|
-
.onEnd((event, success) => {
|
|
755
|
-
if (!success) {
|
|
756
|
-
return;
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
scheduleOnRN(emitSingleTap, event.x, event.y);
|
|
760
|
-
}),
|
|
761
|
-
[emitSingleTap],
|
|
762
|
-
);
|
|
763
|
-
|
|
764
|
-
const doubleTapGesture = useMemo(
|
|
765
|
-
() =>
|
|
766
|
-
Gesture.Tap()
|
|
767
|
-
.enabled(enableDoubleTapZoom && Platform.OS !== 'web')
|
|
768
|
-
.numberOfTaps(2)
|
|
769
|
-
.onEnd((event) => {
|
|
770
|
-
applyTapZoomAtPoint({
|
|
771
|
-
x: event.x,
|
|
772
|
-
y: event.y,
|
|
773
|
-
width,
|
|
774
|
-
height,
|
|
775
|
-
maxZoomScale,
|
|
776
|
-
scale,
|
|
777
|
-
translateX,
|
|
778
|
-
translateY,
|
|
779
|
-
});
|
|
780
|
-
}),
|
|
781
|
-
[enableDoubleTapZoom, height, maxZoomScale, scale, translateX, translateY, width],
|
|
782
|
-
);
|
|
783
|
-
|
|
784
|
-
const tapGesture = useMemo(
|
|
785
|
-
() => Gesture.Exclusive(doubleTapGesture, singleTapGesture),
|
|
786
|
-
[doubleTapGesture, singleTapGesture],
|
|
787
|
-
);
|
|
788
|
-
|
|
789
1195
|
const zoomGesture = useMemo(
|
|
790
|
-
() => Gesture.
|
|
791
|
-
[
|
|
1196
|
+
() => Gesture.Exclusive(zoomPanGesture, tapGesture),
|
|
1197
|
+
[zoomPanGesture, tapGesture],
|
|
792
1198
|
);
|
|
793
1199
|
|
|
1200
|
+
const onWebClick = useWebClickHandler({
|
|
1201
|
+
clearPendingWebSingleTap,
|
|
1202
|
+
emitSingleTap,
|
|
1203
|
+
enableDoubleTapZoom,
|
|
1204
|
+
getCurrentTapTarget,
|
|
1205
|
+
height,
|
|
1206
|
+
maxZoomScale,
|
|
1207
|
+
scale,
|
|
1208
|
+
scheduleWebSingleTap,
|
|
1209
|
+
isInteractionLocked: isWebInteractionLocked,
|
|
1210
|
+
translateX,
|
|
1211
|
+
translateY,
|
|
1212
|
+
width,
|
|
1213
|
+
});
|
|
1214
|
+
|
|
794
1215
|
const animatedStyle = useAnimatedStyle(() => ({
|
|
795
1216
|
opacity: triggerOpacity.get(),
|
|
796
1217
|
transform: [
|
|
@@ -818,54 +1239,23 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
818
1239
|
return { opacity: baseOpacity * dismissOpacity };
|
|
819
1240
|
}, [dismissOptions.direction, dismissOptions.fadeBackdrop]);
|
|
820
1241
|
|
|
821
|
-
const nativeScrollGesture = useMemo(() => {
|
|
822
|
-
return Gesture.Native().requireExternalGestureToFail(dismissGestureRef);
|
|
823
|
-
}, []);
|
|
824
|
-
|
|
825
|
-
const { activeListIndex, onMomentumScrollEnd, onScroll, onScrollBeginDrag, onWebClick } =
|
|
826
|
-
useGestureViewerPaging({
|
|
827
|
-
adjustedInitialIndex,
|
|
828
|
-
autoPlay,
|
|
829
|
-
autoPlayInterval,
|
|
830
|
-
currentIndex,
|
|
831
|
-
dataLength,
|
|
832
|
-
enableDoubleTapZoom,
|
|
833
|
-
enableHorizontalSwipe,
|
|
834
|
-
enableLoop,
|
|
835
|
-
height,
|
|
836
|
-
isRotated,
|
|
837
|
-
isZoomed,
|
|
838
|
-
itemSpacing,
|
|
839
|
-
manager,
|
|
840
|
-
maxZoomScale,
|
|
841
|
-
onSingleTap: emitSingleTap,
|
|
842
|
-
scale,
|
|
843
|
-
scrollTo,
|
|
844
|
-
syncCurrentIndex,
|
|
845
|
-
syncPendingIndex,
|
|
846
|
-
translateX,
|
|
847
|
-
translateY,
|
|
848
|
-
width,
|
|
849
|
-
});
|
|
850
|
-
|
|
851
1242
|
return {
|
|
852
|
-
activeListIndex,
|
|
853
1243
|
animatedStyle,
|
|
854
1244
|
backdropStyle,
|
|
1245
|
+
centerVirtualIndex: renderCenterVirtualIndex,
|
|
1246
|
+
currentIndex: renderCurrentIndex,
|
|
855
1247
|
dataLength,
|
|
856
1248
|
dismissGesture,
|
|
857
1249
|
handleDismiss,
|
|
1250
|
+
horizontalPagingGesture,
|
|
858
1251
|
isPinching,
|
|
859
1252
|
isRotated,
|
|
860
|
-
|
|
861
1253
|
isZoomed,
|
|
862
|
-
listRef,
|
|
863
|
-
nativeScrollGesture,
|
|
864
1254
|
onWebClick,
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
onScrollBeginDrag,
|
|
1255
|
+
pageStride,
|
|
1256
|
+
renderWindowSlots,
|
|
1257
|
+
visualPage,
|
|
869
1258
|
zoomGesture,
|
|
1259
|
+
zoomPinchGesture,
|
|
870
1260
|
};
|
|
871
1261
|
};
|