react-native-gesture-image-viewer 2.3.3 → 3.0.0-beta.0
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 +88 -140
- 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/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/index.js.map +1 -1
- 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 +386 -200
- 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 +147 -65
- 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/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/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- 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 +45 -52
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useGestureViewer.d.ts +9 -8
- 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 +31 -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 +1 -1
- package/src/GestureViewer.tsx +104 -191
- package/src/GestureViewerManager.ts +77 -122
- package/src/gestureViewerAnimation.ts +18 -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/index.tsx +1 -0
- package/src/platformTapGestures.ts +1 -0
- package/src/platformTapGestures.web.ts +1 -0
- package/src/renderWindow.ts +253 -0
- package/src/types.ts +44 -99
- package/src/useGestureViewer.ts +557 -258
- package/src/useGestureViewerController.ts +3 -3
- package/src/useGestureViewerManagerBridge.ts +118 -0
- package/src/useGestureViewerPaging.ts +277 -98
- 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 -275
- 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 -41
- 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 -41
- package/src/useGestureViewerPaging.web.ts +0 -434
- 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,46 @@ 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
|
-
| 'containerStyle'
|
|
32
|
-
| 'backdropStyle'
|
|
33
|
-
| 'enableSnapMode'
|
|
46
|
+
const isValidTriggerRect = (rect: TriggerRect | null): rect is TriggerRect => {
|
|
47
|
+
return !!rect && rect.width > 0 && rect.height > 0;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
type UseGestureViewerProps<ItemT> = Omit<
|
|
51
|
+
GestureViewerProps<ItemT>,
|
|
52
|
+
'renderItem' | 'renderContainer' | 'containerStyle' | 'backdropStyle'
|
|
34
53
|
>;
|
|
35
54
|
|
|
36
|
-
export const useGestureViewer = <ItemT
|
|
55
|
+
export const useGestureViewer = <ItemT>({
|
|
37
56
|
data,
|
|
38
57
|
initialIndex = 0,
|
|
39
58
|
onDismiss,
|
|
@@ -42,21 +61,35 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
42
61
|
dismiss,
|
|
43
62
|
enableDoubleTapZoom = true,
|
|
44
63
|
enablePinchZoom = true,
|
|
45
|
-
|
|
64
|
+
horizontalSwipe,
|
|
46
65
|
enablePanWhenZoomed = true,
|
|
47
66
|
enableLoop = false,
|
|
48
67
|
maxZoomScale = 2,
|
|
49
|
-
|
|
68
|
+
pageSpacing = 0,
|
|
69
|
+
windowSize = 3,
|
|
50
70
|
height: customHeight,
|
|
51
71
|
id = 'default',
|
|
52
72
|
onDismissStart,
|
|
53
73
|
triggerAnimation,
|
|
54
74
|
autoPlay = false,
|
|
55
75
|
autoPlayInterval = 3000,
|
|
56
|
-
}: UseGestureViewerProps<ItemT
|
|
76
|
+
}: UseGestureViewerProps<ItemT>) => {
|
|
57
77
|
const { width: screenWidth, height: screenHeight } = useWindowDimensions();
|
|
58
78
|
const width = customWidth || screenWidth;
|
|
59
79
|
const height = customHeight || screenHeight;
|
|
80
|
+
const dataLength = data?.length || 0;
|
|
81
|
+
const normalizedPageSpacing = normalizePageSpacing(pageSpacing);
|
|
82
|
+
const pageStride = getPageStride(width, normalizedPageSpacing);
|
|
83
|
+
const initialCurrentIndex = clampIndex(initialIndex, dataLength);
|
|
84
|
+
const horizontalSwipeEnabled = horizontalSwipe?.enabled ?? true;
|
|
85
|
+
const horizontalSwipeDistanceThresholdRatio = normalizeHorizontalSwipeThreshold(
|
|
86
|
+
horizontalSwipe?.distanceThresholdRatio,
|
|
87
|
+
SWIPE_THRESHOLD_RATIO,
|
|
88
|
+
);
|
|
89
|
+
const horizontalSwipeVelocityThreshold = normalizeHorizontalSwipeThreshold(
|
|
90
|
+
horizontalSwipe?.velocityThreshold,
|
|
91
|
+
SWIPE_VELOCITY_THRESHOLD,
|
|
92
|
+
);
|
|
60
93
|
|
|
61
94
|
const dismissGestureRef = useRef<GestureType>(undefined);
|
|
62
95
|
|
|
@@ -64,21 +97,30 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
64
97
|
const [isRotated, setIsRotated] = useState(false);
|
|
65
98
|
const [isPinching, setIsPinching] = useState(false);
|
|
66
99
|
const [shouldStartTriggerAnimation, setShouldStartTriggerAnimation] = useState(false);
|
|
67
|
-
const [
|
|
68
|
-
const [
|
|
69
|
-
const [activeTriggerNode, setActiveTriggerNode] = useState<View | null>(
|
|
100
|
+
const [currentIndex, setCurrentIndex] = useState(initialCurrentIndex);
|
|
101
|
+
const [centerVirtualIndex, setCenterVirtualIndex] = useState(initialCurrentIndex);
|
|
102
|
+
const [activeTriggerNode, setActiveTriggerNode] = useState<View | null>(() =>
|
|
103
|
+
registry.getActiveTriggerNode(id),
|
|
104
|
+
);
|
|
105
|
+
const [isTriggerOpening, setIsTriggerOpening] = useState(
|
|
106
|
+
() => !!registry.getActiveTriggerNode(id),
|
|
107
|
+
);
|
|
70
108
|
|
|
71
|
-
const listRef = useRef<any>(null);
|
|
72
109
|
const triggerRectRef = useRef<TriggerRect | null>(null);
|
|
73
|
-
const pendingIndexRef = useRef(
|
|
110
|
+
const pendingIndexRef = useRef(initialCurrentIndex);
|
|
111
|
+
const currentIndexRef = useRef(initialCurrentIndex);
|
|
112
|
+
const centerVirtualIndexRef = useRef(initialCurrentIndex);
|
|
113
|
+
const previousInitialIndexRef = useRef(initialIndex);
|
|
114
|
+
const hasResolvedInitialIndexRef = useRef(dataLength > 0);
|
|
74
115
|
const onAnimationCompleteRef = useRef(triggerAnimation?.onAnimationComplete);
|
|
75
116
|
const onSingleTapRef = useRef(onSingleTap);
|
|
76
117
|
const dataRef = useRef(data);
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
118
|
+
const dataLengthRef = useRef(dataLength);
|
|
119
|
+
const enableLoopRef = useRef(enableLoop);
|
|
120
|
+
const managerRef = useRef<GestureViewerManager | null>(null);
|
|
121
|
+
const isZoomedRef = useRef(isZoomed);
|
|
122
|
+
const isRotatedRef = useRef(isRotated);
|
|
123
|
+
const isPinchingRef = useRef(isPinching);
|
|
82
124
|
|
|
83
125
|
const initialTranslateY = useSharedValue(0);
|
|
84
126
|
const initialTranslateX = useSharedValue(0);
|
|
@@ -87,21 +129,22 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
87
129
|
const translateY = useSharedValue(0);
|
|
88
130
|
const translateX = useSharedValue(0);
|
|
89
131
|
const scale = useSharedValue(1);
|
|
90
|
-
const backdropOpacity = useSharedValue(1);
|
|
91
132
|
const rotation = useSharedValue(0);
|
|
92
133
|
|
|
93
134
|
const triggerScale = useSharedValue(1);
|
|
94
135
|
const triggerTranslateX = useSharedValue(0);
|
|
95
136
|
const triggerTranslateY = useSharedValue(0);
|
|
96
|
-
const triggerOpacity = useSharedValue(1);
|
|
137
|
+
const triggerOpacity = useSharedValue(activeTriggerNode ? 0 : 1);
|
|
138
|
+
|
|
139
|
+
const hasZoomChangeListeners = useSharedValue(false);
|
|
140
|
+
const hasRotationChangeListeners = useSharedValue(false);
|
|
97
141
|
|
|
98
142
|
const lastFocalX = useSharedValue(0);
|
|
99
143
|
const lastFocalY = useSharedValue(0);
|
|
100
144
|
const startFocalX = useSharedValue(0);
|
|
101
145
|
const startFocalY = useSharedValue(0);
|
|
102
146
|
const hasActiveFocal = useSharedValue(false);
|
|
103
|
-
|
|
104
|
-
const dataLength = data?.length || 0;
|
|
147
|
+
const { clearPendingWebSingleTap, scheduleWebSingleTap } = useWebSingleTapTimer();
|
|
105
148
|
|
|
106
149
|
const animationConfig = useMemo(
|
|
107
150
|
() => ({
|
|
@@ -129,28 +172,24 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
129
172
|
],
|
|
130
173
|
);
|
|
131
174
|
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
175
|
+
const {
|
|
176
|
+
centerVirtualIndex: renderCenterVirtualIndex,
|
|
177
|
+
currentIndex: renderCurrentIndex,
|
|
178
|
+
slots: renderWindowSlots,
|
|
179
|
+
} = resolveGestureViewerRenderWindow({
|
|
180
|
+
centerVirtualIndex,
|
|
181
|
+
currentIndex,
|
|
182
|
+
data,
|
|
183
|
+
enableLoop,
|
|
184
|
+
isTriggerOpening,
|
|
185
|
+
windowSize,
|
|
186
|
+
});
|
|
139
187
|
|
|
140
188
|
const constrainTranslation = useMemo(
|
|
141
189
|
() => createBoundsConstraint({ height, width }),
|
|
142
190
|
[width, height],
|
|
143
191
|
);
|
|
144
192
|
|
|
145
|
-
const scrollTo = useCallback(
|
|
146
|
-
(index: number, animated: boolean) => {
|
|
147
|
-
const scrollAction = createScrollAction(listRef.current, width + itemSpacing);
|
|
148
|
-
|
|
149
|
-
return scrollAction.scrollTo(index, animated);
|
|
150
|
-
},
|
|
151
|
-
[width, itemSpacing],
|
|
152
|
-
);
|
|
153
|
-
|
|
154
193
|
const resetTransformState = useCallback(() => {
|
|
155
194
|
translateX.set(withTiming(0));
|
|
156
195
|
translateY.set(withTiming(0));
|
|
@@ -161,38 +200,179 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
161
200
|
rotation.set(0);
|
|
162
201
|
}, [initialTranslateX, initialTranslateY, rotation, scale, startScale, translateX, translateY]);
|
|
163
202
|
|
|
164
|
-
const
|
|
165
|
-
(
|
|
166
|
-
|
|
203
|
+
const resetTransformStateImmediately = useCallback(() => {
|
|
204
|
+
cancelAnimation(translateX);
|
|
205
|
+
cancelAnimation(translateY);
|
|
206
|
+
cancelAnimation(initialTranslateX);
|
|
207
|
+
cancelAnimation(initialTranslateY);
|
|
208
|
+
cancelAnimation(startScale);
|
|
209
|
+
cancelAnimation(scale);
|
|
210
|
+
cancelAnimation(rotation);
|
|
211
|
+
translateX.set(0);
|
|
212
|
+
translateY.set(0);
|
|
213
|
+
initialTranslateX.set(0);
|
|
214
|
+
initialTranslateY.set(0);
|
|
215
|
+
startScale.set(1);
|
|
216
|
+
scale.set(1);
|
|
217
|
+
rotation.set(0);
|
|
218
|
+
}, [initialTranslateX, initialTranslateY, rotation, scale, startScale, translateX, translateY]);
|
|
219
|
+
|
|
220
|
+
const commitCurrentIndex = useCallback((nextIndex: number) => {
|
|
221
|
+
pendingIndexRef.current = nextIndex;
|
|
222
|
+
currentIndexRef.current = nextIndex;
|
|
223
|
+
setCurrentIndex(nextIndex);
|
|
224
|
+
managerRef.current?.notifyStateChange();
|
|
225
|
+
}, []);
|
|
226
|
+
|
|
227
|
+
const commitVirtualIndexOnly = useCallback(
|
|
228
|
+
(nextVirtualIndex: number) => {
|
|
229
|
+
const nextLogicalIndex = getLogicalIndex(
|
|
230
|
+
nextVirtualIndex,
|
|
231
|
+
dataLengthRef.current,
|
|
232
|
+
enableLoopRef.current,
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
if (nextLogicalIndex === null) {
|
|
167
236
|
return;
|
|
168
237
|
}
|
|
169
238
|
|
|
170
|
-
|
|
239
|
+
centerVirtualIndexRef.current = nextVirtualIndex;
|
|
240
|
+
setCenterVirtualIndex(nextVirtualIndex);
|
|
241
|
+
commitCurrentIndex(nextLogicalIndex);
|
|
171
242
|
},
|
|
172
|
-
[
|
|
243
|
+
[commitCurrentIndex],
|
|
173
244
|
);
|
|
174
245
|
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
246
|
+
const {
|
|
247
|
+
animateToVirtualPage,
|
|
248
|
+
horizontalPagingGesture,
|
|
249
|
+
isPageTransitioningRef,
|
|
250
|
+
pageTransitionLocked,
|
|
251
|
+
snapToVirtualPage,
|
|
252
|
+
visualPage,
|
|
253
|
+
} = useGestureViewerPaging({
|
|
254
|
+
centerVirtualIndex,
|
|
255
|
+
clearPendingWebSingleTap,
|
|
256
|
+
commitVirtualIndexOnly,
|
|
257
|
+
currentIndex,
|
|
258
|
+
dataLength,
|
|
259
|
+
enableLoop,
|
|
260
|
+
horizontalSwipeDistanceThresholdRatio,
|
|
261
|
+
horizontalSwipeEnabled,
|
|
262
|
+
horizontalSwipeVelocityThreshold,
|
|
263
|
+
initialPage: initialCurrentIndex,
|
|
264
|
+
isPinching,
|
|
265
|
+
isRotated,
|
|
266
|
+
isTriggerOpening,
|
|
267
|
+
isZoomed,
|
|
268
|
+
pageStride,
|
|
269
|
+
width,
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
const navigateToIndex = useCallback(
|
|
273
|
+
(targetIndex: number, options?: NavigationOptions, preferredDirection?: -1 | 1) => {
|
|
274
|
+
const resolution = resolveNavigation({
|
|
275
|
+
currentIndex: currentIndexRef.current,
|
|
276
|
+
dataLength: dataLengthRef.current,
|
|
277
|
+
enableLoop: enableLoopRef.current,
|
|
278
|
+
preferredDirection,
|
|
279
|
+
targetIndex,
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
if (resolution.kind === 'noop') {
|
|
178
283
|
return;
|
|
179
284
|
}
|
|
180
285
|
|
|
181
|
-
|
|
286
|
+
if (
|
|
287
|
+
isPageTransitioningRef.current &&
|
|
288
|
+
!shouldRunNavigationDuringTransition(resolution, options)
|
|
289
|
+
) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
182
292
|
|
|
183
|
-
const
|
|
293
|
+
const targetVirtualIndex =
|
|
294
|
+
resolution.kind === 'step'
|
|
295
|
+
? centerVirtualIndexRef.current + resolution.direction
|
|
296
|
+
: getVirtualIndexForLogicalIndex(
|
|
297
|
+
resolution.targetIndex,
|
|
298
|
+
centerVirtualIndexRef.current,
|
|
299
|
+
dataLengthRef.current,
|
|
300
|
+
enableLoopRef.current,
|
|
301
|
+
);
|
|
184
302
|
|
|
185
|
-
if (
|
|
303
|
+
if (targetVirtualIndex === null) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (resolution.kind !== 'step' || options?.animated === false) {
|
|
308
|
+
resetTransformStateImmediately();
|
|
309
|
+
snapToVirtualPage(targetVirtualIndex);
|
|
186
310
|
return;
|
|
187
311
|
}
|
|
188
312
|
|
|
189
|
-
manager.setCurrentIndex(nextIndex);
|
|
190
|
-
manager.notifyStateChange();
|
|
191
313
|
resetTransformState();
|
|
314
|
+
animateToVirtualPage(targetVirtualIndex);
|
|
192
315
|
},
|
|
193
|
-
[
|
|
316
|
+
[
|
|
317
|
+
animateToVirtualPage,
|
|
318
|
+
isPageTransitioningRef,
|
|
319
|
+
resetTransformStateImmediately,
|
|
320
|
+
resetTransformState,
|
|
321
|
+
snapToVirtualPage,
|
|
322
|
+
],
|
|
194
323
|
);
|
|
195
324
|
|
|
325
|
+
const navigateByDirection = useCallback(
|
|
326
|
+
(direction: -1 | 1) => {
|
|
327
|
+
const currentDataLength = dataLengthRef.current;
|
|
328
|
+
|
|
329
|
+
if (currentDataLength <= 0) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const nextIndex = currentIndexRef.current + direction;
|
|
334
|
+
|
|
335
|
+
if (nextIndex >= 0 && nextIndex < currentDataLength) {
|
|
336
|
+
navigateToIndex(nextIndex, undefined, direction);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (!enableLoopRef.current || currentDataLength <= 1) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
navigateToIndex(direction === 1 ? 0 : currentDataLength - 1, undefined, direction);
|
|
345
|
+
},
|
|
346
|
+
[navigateToIndex],
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
const navigateToNext = useCallback(() => {
|
|
350
|
+
navigateByDirection(1);
|
|
351
|
+
}, [navigateByDirection]);
|
|
352
|
+
|
|
353
|
+
const navigateToPrevious = useCallback(() => {
|
|
354
|
+
navigateByDirection(-1);
|
|
355
|
+
}, [navigateByDirection]);
|
|
356
|
+
|
|
357
|
+
useGestureViewerManagerBridge({
|
|
358
|
+
currentIndexRef,
|
|
359
|
+
dataLengthRef,
|
|
360
|
+
goToIndex: navigateToIndex,
|
|
361
|
+
goToNext: navigateToNext,
|
|
362
|
+
goToPrevious: navigateToPrevious,
|
|
363
|
+
hasRotationChangeListeners,
|
|
364
|
+
hasZoomChangeListeners,
|
|
365
|
+
height,
|
|
366
|
+
id,
|
|
367
|
+
managerRef,
|
|
368
|
+
maxZoomScale,
|
|
369
|
+
rotation,
|
|
370
|
+
scale,
|
|
371
|
+
translateX,
|
|
372
|
+
translateY,
|
|
373
|
+
width,
|
|
374
|
+
});
|
|
375
|
+
|
|
196
376
|
const emitZoomChange = useCallback((currentScale: number, prevScale: number | null) => {
|
|
197
377
|
managerRef.current?.emitZoomChange(currentScale, prevScale);
|
|
198
378
|
}, []);
|
|
@@ -201,136 +381,203 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
201
381
|
managerRef.current?.emitRotationChange(currentRotation, prevRotation);
|
|
202
382
|
}, []);
|
|
203
383
|
|
|
204
|
-
const
|
|
384
|
+
const finishTriggerOpening = useCallback(() => {
|
|
385
|
+
setIsTriggerOpening(false);
|
|
205
386
|
onAnimationCompleteRef.current?.();
|
|
206
387
|
}, []);
|
|
207
388
|
|
|
208
389
|
useAnimatedReaction(
|
|
209
390
|
() => scale.get(),
|
|
210
391
|
(currentScale, previousScale) => {
|
|
211
|
-
if (currentScale !== previousScale) {
|
|
392
|
+
if (currentScale !== previousScale && hasZoomChangeListeners.get()) {
|
|
212
393
|
scheduleOnRN(emitZoomChange, currentScale, previousScale);
|
|
213
394
|
}
|
|
214
395
|
|
|
215
|
-
|
|
396
|
+
const currentIsZoomed = currentScale > 1;
|
|
397
|
+
const previousIsZoomed = (previousScale ?? 1) > 1;
|
|
398
|
+
|
|
399
|
+
if (previousScale === null || currentIsZoomed !== previousIsZoomed) {
|
|
400
|
+
scheduleOnRN(setIsZoomed, currentIsZoomed);
|
|
401
|
+
}
|
|
216
402
|
},
|
|
217
403
|
);
|
|
218
404
|
|
|
219
405
|
useAnimatedReaction(
|
|
220
406
|
() => rotation.get(),
|
|
221
407
|
(currentRotation, previousRotation) => {
|
|
222
|
-
if (currentRotation !== previousRotation) {
|
|
408
|
+
if (currentRotation !== previousRotation && hasRotationChangeListeners.get()) {
|
|
223
409
|
scheduleOnRN(emitRotationChange, currentRotation, previousRotation);
|
|
224
410
|
}
|
|
225
411
|
|
|
226
|
-
|
|
412
|
+
const currentIsRotated = currentRotation % 360 !== 0;
|
|
413
|
+
const previousIsRotated = (previousRotation ?? 0) % 360 !== 0;
|
|
414
|
+
|
|
415
|
+
if (previousRotation === null || currentIsRotated !== previousIsRotated) {
|
|
416
|
+
scheduleOnRN(setIsRotated, currentIsRotated);
|
|
417
|
+
}
|
|
227
418
|
},
|
|
228
419
|
);
|
|
229
420
|
|
|
230
421
|
useEffect(() => {
|
|
231
|
-
|
|
422
|
+
return registry.subscribeToActiveTrigger(id, (node) => {
|
|
423
|
+
setActiveTriggerNode(node);
|
|
232
424
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
pendingIndexRef.current = state.currentIndex;
|
|
237
|
-
setCurrentIndex(state.currentIndex);
|
|
238
|
-
});
|
|
425
|
+
if (node) {
|
|
426
|
+
setIsTriggerOpening(true);
|
|
427
|
+
}
|
|
239
428
|
});
|
|
240
|
-
|
|
241
|
-
return () => {
|
|
242
|
-
unsubscribeFromRegistry();
|
|
243
|
-
unsubscribe?.();
|
|
244
|
-
};
|
|
245
429
|
}, [id]);
|
|
246
430
|
|
|
247
431
|
useEffect(() => {
|
|
248
|
-
|
|
249
|
-
}, [
|
|
432
|
+
dataRef.current = data;
|
|
433
|
+
}, [data]);
|
|
250
434
|
|
|
251
435
|
useEffect(() => {
|
|
252
|
-
|
|
436
|
+
isZoomedRef.current = isZoomed;
|
|
437
|
+
}, [isZoomed]);
|
|
253
438
|
|
|
254
|
-
|
|
255
|
-
|
|
439
|
+
useEffect(() => {
|
|
440
|
+
isRotatedRef.current = isRotated;
|
|
441
|
+
}, [isRotated]);
|
|
442
|
+
|
|
443
|
+
useEffect(() => {
|
|
444
|
+
isPinchingRef.current = isPinching;
|
|
445
|
+
}, [isPinching]);
|
|
446
|
+
|
|
447
|
+
useEffect(() => {
|
|
448
|
+
onSingleTapRef.current = onSingleTap;
|
|
449
|
+
}, [onSingleTap]);
|
|
450
|
+
|
|
451
|
+
useEffect(() => {
|
|
452
|
+
onAnimationCompleteRef.current = triggerAnimation?.onAnimationComplete;
|
|
453
|
+
}, [triggerAnimation?.onAnimationComplete]);
|
|
454
|
+
|
|
455
|
+
useLayoutEffect(() => {
|
|
456
|
+
const initialIndexChanged = previousInitialIndexRef.current !== initialIndex;
|
|
457
|
+
const shouldResolveInitialIndex =
|
|
458
|
+
initialIndexChanged || (!hasResolvedInitialIndexRef.current && dataLength > 0);
|
|
459
|
+
|
|
460
|
+
previousInitialIndexRef.current = initialIndex;
|
|
461
|
+
dataLengthRef.current = dataLength;
|
|
462
|
+
enableLoopRef.current = enableLoop;
|
|
463
|
+
|
|
464
|
+
hasResolvedInitialIndexRef.current = dataLength > 0;
|
|
465
|
+
|
|
466
|
+
const nextIndex = shouldResolveInitialIndex
|
|
467
|
+
? clampIndex(initialIndex, dataLength)
|
|
468
|
+
: clampIndex(currentIndexRef.current, dataLength);
|
|
469
|
+
|
|
470
|
+
const nextVirtualIndex =
|
|
471
|
+
getVirtualIndexForLogicalIndex(
|
|
472
|
+
nextIndex,
|
|
473
|
+
centerVirtualIndexRef.current,
|
|
474
|
+
dataLength,
|
|
475
|
+
enableLoop,
|
|
476
|
+
) ?? nextIndex;
|
|
477
|
+
|
|
478
|
+
const previousIndex = currentIndexRef.current;
|
|
479
|
+
const previousVirtualIndex = centerVirtualIndexRef.current;
|
|
480
|
+
const shouldSyncVirtualPage =
|
|
481
|
+
isPageTransitioningRef.current || previousVirtualIndex !== nextVirtualIndex;
|
|
482
|
+
const shouldResetTransform =
|
|
483
|
+
initialIndexChanged || previousIndex !== nextIndex || shouldSyncVirtualPage;
|
|
484
|
+
|
|
485
|
+
if (shouldSyncVirtualPage) {
|
|
486
|
+
snapToVirtualPage(nextVirtualIndex);
|
|
487
|
+
|
|
488
|
+
if (dataLength === 0) {
|
|
489
|
+
centerVirtualIndexRef.current = nextVirtualIndex;
|
|
490
|
+
setCenterVirtualIndex(nextVirtualIndex);
|
|
491
|
+
}
|
|
256
492
|
}
|
|
257
493
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
494
|
+
const shouldCommitIndexOutsidePaging = !shouldSyncVirtualPage || dataLength === 0;
|
|
495
|
+
|
|
496
|
+
if (shouldCommitIndexOutsidePaging && previousIndex !== nextIndex) {
|
|
497
|
+
commitCurrentIndex(nextIndex);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if (shouldCommitIndexOutsidePaging && previousIndex === nextIndex) {
|
|
501
|
+
managerRef.current?.notifyStateChange();
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
if (shouldResetTransform) {
|
|
505
|
+
resetTransformState();
|
|
506
|
+
}
|
|
268
507
|
}, [
|
|
508
|
+
commitCurrentIndex,
|
|
269
509
|
dataLength,
|
|
270
|
-
enableHorizontalSwipe,
|
|
271
|
-
initialIndex,
|
|
272
|
-
manager,
|
|
273
|
-
width,
|
|
274
|
-
itemSpacing,
|
|
275
|
-
maxZoomScale,
|
|
276
510
|
enableLoop,
|
|
277
|
-
|
|
278
|
-
|
|
511
|
+
initialIndex,
|
|
512
|
+
isPageTransitioningRef,
|
|
279
513
|
resetTransformState,
|
|
280
|
-
|
|
281
|
-
translateY,
|
|
282
|
-
rotation,
|
|
514
|
+
snapToVirtualPage,
|
|
283
515
|
]);
|
|
284
516
|
|
|
285
517
|
useEffect(() => {
|
|
286
|
-
|
|
518
|
+
return () => {
|
|
519
|
+
triggerRectRef.current = null;
|
|
520
|
+
clearPendingWebSingleTap();
|
|
521
|
+
};
|
|
522
|
+
}, [clearPendingWebSingleTap]);
|
|
523
|
+
|
|
524
|
+
useEffect(() => {
|
|
525
|
+
if (
|
|
526
|
+
!autoPlay ||
|
|
527
|
+
dataLength <= 1 ||
|
|
528
|
+
isTriggerOpening ||
|
|
529
|
+
isZoomed ||
|
|
530
|
+
isRotated ||
|
|
531
|
+
isPinching ||
|
|
532
|
+
(!enableLoop && currentIndex >= dataLength - 1)
|
|
533
|
+
) {
|
|
287
534
|
return;
|
|
288
535
|
}
|
|
289
536
|
|
|
290
|
-
|
|
291
|
-
}, [manager]);
|
|
537
|
+
const interval = Math.max(250, Math.floor(autoPlayInterval || 0));
|
|
292
538
|
|
|
293
|
-
|
|
294
|
-
translateY.set(0);
|
|
295
|
-
translateX.set(0);
|
|
296
|
-
scale.set(1);
|
|
297
|
-
backdropOpacity.set(1);
|
|
298
|
-
startScale.set(1);
|
|
299
|
-
rotation.set(0);
|
|
300
|
-
|
|
301
|
-
if (adjustedInitialIndex <= 0 || !listRef.current) {
|
|
539
|
+
if (!Number.isFinite(interval)) {
|
|
302
540
|
return;
|
|
303
541
|
}
|
|
304
542
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
startScale,
|
|
315
|
-
rotation,
|
|
316
|
-
scrollTo,
|
|
317
|
-
]);
|
|
543
|
+
const timer = setInterval(() => {
|
|
544
|
+
if (
|
|
545
|
+
isPageTransitioningRef.current ||
|
|
546
|
+
isZoomedRef.current ||
|
|
547
|
+
isRotatedRef.current ||
|
|
548
|
+
isPinchingRef.current
|
|
549
|
+
) {
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
318
552
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
553
|
+
const nextIndex =
|
|
554
|
+
currentIndexRef.current >= dataLengthRef.current - 1
|
|
555
|
+
? enableLoopRef.current
|
|
556
|
+
? 0
|
|
557
|
+
: null
|
|
558
|
+
: currentIndexRef.current + 1;
|
|
322
559
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
560
|
+
if (nextIndex === null) {
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
326
563
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
}, [manager]);
|
|
564
|
+
navigateToIndex(nextIndex);
|
|
565
|
+
}, interval);
|
|
330
566
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
567
|
+
return () => clearInterval(timer);
|
|
568
|
+
}, [
|
|
569
|
+
autoPlay,
|
|
570
|
+
autoPlayInterval,
|
|
571
|
+
currentIndex,
|
|
572
|
+
dataLength,
|
|
573
|
+
enableLoop,
|
|
574
|
+
isPinching,
|
|
575
|
+
isRotated,
|
|
576
|
+
isTriggerOpening,
|
|
577
|
+
isZoomed,
|
|
578
|
+
isPageTransitioningRef,
|
|
579
|
+
navigateToIndex,
|
|
580
|
+
]);
|
|
334
581
|
|
|
335
582
|
useEffect(() => {
|
|
336
583
|
if (shouldStartTriggerAnimation && triggerRectRef.current) {
|
|
@@ -349,7 +596,7 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
349
596
|
triggerScale.set(
|
|
350
597
|
withTiming(1, animationConfig, (finished) => {
|
|
351
598
|
if (finished) {
|
|
352
|
-
scheduleOnRN(
|
|
599
|
+
scheduleOnRN(finishTriggerOpening);
|
|
353
600
|
}
|
|
354
601
|
}),
|
|
355
602
|
);
|
|
@@ -374,11 +621,18 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
374
621
|
triggerScale,
|
|
375
622
|
triggerTranslateX,
|
|
376
623
|
triggerTranslateY,
|
|
377
|
-
|
|
624
|
+
finishTriggerOpening,
|
|
378
625
|
]);
|
|
379
626
|
|
|
380
627
|
useEffect(() => {
|
|
381
|
-
if (!activeTriggerNode
|
|
628
|
+
if (!activeTriggerNode) {
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
if (typeof activeTriggerNode.measure !== 'function') {
|
|
633
|
+
setIsTriggerOpening(false);
|
|
634
|
+
triggerOpacity.set(1);
|
|
635
|
+
registry.clearActiveTriggerNode(id);
|
|
382
636
|
return;
|
|
383
637
|
}
|
|
384
638
|
|
|
@@ -391,6 +645,8 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
391
645
|
} satisfies TriggerRect;
|
|
392
646
|
|
|
393
647
|
if (!isValidTriggerRect(nextTriggerRect)) {
|
|
648
|
+
setIsTriggerOpening(false);
|
|
649
|
+
triggerOpacity.set(1);
|
|
394
650
|
registry.clearActiveTriggerNode(id);
|
|
395
651
|
return;
|
|
396
652
|
}
|
|
@@ -400,13 +656,7 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
400
656
|
setShouldStartTriggerAnimation(true);
|
|
401
657
|
registry.clearActiveTriggerNode(id);
|
|
402
658
|
});
|
|
403
|
-
}, [activeTriggerNode, id,
|
|
404
|
-
|
|
405
|
-
useEffect(() => {
|
|
406
|
-
return () => {
|
|
407
|
-
triggerRectRef.current = null;
|
|
408
|
-
};
|
|
409
|
-
}, []);
|
|
659
|
+
}, [activeTriggerNode, id, triggerOpacity]);
|
|
410
660
|
|
|
411
661
|
const animateDismissToRect = useCallback(
|
|
412
662
|
(rect: TriggerRect) => {
|
|
@@ -444,6 +694,10 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
444
694
|
}, [onDismiss]);
|
|
445
695
|
|
|
446
696
|
const handleDismiss = useCallback(() => {
|
|
697
|
+
if (isPageTransitioningRef.current) {
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
|
|
447
701
|
onDismissStart?.();
|
|
448
702
|
|
|
449
703
|
const dismissTargetIndex = pendingIndexRef.current;
|
|
@@ -479,10 +733,10 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
479
733
|
}
|
|
480
734
|
|
|
481
735
|
dismissWithoutTrigger();
|
|
482
|
-
}, [animateDismissToRect, dismissWithoutTrigger, id,
|
|
736
|
+
}, [animateDismissToRect, dismissWithoutTrigger, id, isPageTransitioningRef, onDismissStart]);
|
|
483
737
|
|
|
484
738
|
const dismissGesture = useMemo(() => {
|
|
485
|
-
const canDismiss = !isZoomed && dismissOptions.enabled;
|
|
739
|
+
const canDismiss = !isTriggerOpening && !isZoomed && dismissOptions.enabled;
|
|
486
740
|
|
|
487
741
|
return Gesture.Pan()
|
|
488
742
|
.minDistance(10)
|
|
@@ -493,9 +747,18 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
493
747
|
.withRef(dismissGestureRef)
|
|
494
748
|
.enabled(canDismiss)
|
|
495
749
|
.onUpdate((event) => {
|
|
750
|
+
if (pageTransitionLocked.get()) {
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
|
|
496
754
|
translateY.set(event.translationY / dismissOptions.resistance);
|
|
497
755
|
})
|
|
498
756
|
.onEnd((event) => {
|
|
757
|
+
if (pageTransitionLocked.get()) {
|
|
758
|
+
translateY.set(withSpring(0, PAGE_SPRING_CONFIG));
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
|
|
499
762
|
if (
|
|
500
763
|
canDismiss &&
|
|
501
764
|
shouldDismissByDirection(
|
|
@@ -508,28 +771,120 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
508
771
|
return;
|
|
509
772
|
}
|
|
510
773
|
|
|
511
|
-
translateY.set(
|
|
512
|
-
withSpring(0, {
|
|
513
|
-
damping: 50,
|
|
514
|
-
energyThreshold: 6e-9,
|
|
515
|
-
mass: 4,
|
|
516
|
-
overshootClamping: false,
|
|
517
|
-
stiffness: 600,
|
|
518
|
-
}),
|
|
519
|
-
);
|
|
774
|
+
translateY.set(withSpring(0, PAGE_SPRING_CONFIG));
|
|
520
775
|
});
|
|
521
|
-
}, [translateY, dismissOptions, handleDismiss, isZoomed]);
|
|
776
|
+
}, [translateY, dismissOptions, handleDismiss, isTriggerOpening, isZoomed, pageTransitionLocked]);
|
|
777
|
+
|
|
778
|
+
const getCurrentTapTarget = useCallback((): { index: number; item: ItemT } | null => {
|
|
779
|
+
const index = pendingIndexRef.current;
|
|
780
|
+
|
|
781
|
+
if (index < 0 || index >= dataRef.current.length || !(index in dataRef.current)) {
|
|
782
|
+
return null;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
return { index, item: dataRef.current[index] as ItemT };
|
|
786
|
+
}, []);
|
|
787
|
+
|
|
788
|
+
const isWebInteractionLocked = useCallback(
|
|
789
|
+
() => isPageTransitioningRef.current || pageTransitionLocked.get(),
|
|
790
|
+
[isPageTransitioningRef, pageTransitionLocked],
|
|
791
|
+
);
|
|
792
|
+
|
|
793
|
+
const emitSingleTap = useCallback<EmitSingleTap<ItemT>>(
|
|
794
|
+
(x, y, tapTarget) => {
|
|
795
|
+
if (isPageTransitioningRef.current) {
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
const target = tapTarget ?? getCurrentTapTarget();
|
|
800
|
+
|
|
801
|
+
if (!target) {
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
const { index: targetIndex, item: targetItem } = target;
|
|
806
|
+
|
|
807
|
+
if (targetIndex < 0) {
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
managerRef.current?.emitTap({ kind: 'single', x, y, index: targetIndex });
|
|
812
|
+
onSingleTapRef.current?.({ x, y, index: targetIndex, item: targetItem });
|
|
813
|
+
},
|
|
814
|
+
[getCurrentTapTarget, isPageTransitioningRef],
|
|
815
|
+
);
|
|
816
|
+
|
|
817
|
+
const singleTapGesture = useMemo(
|
|
818
|
+
() =>
|
|
819
|
+
Gesture.Tap()
|
|
820
|
+
.enabled(enableNativeTapGestures)
|
|
821
|
+
.numberOfTaps(1)
|
|
822
|
+
.onEnd((event, success) => {
|
|
823
|
+
if (!success || pageTransitionLocked.get()) {
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
scheduleOnRN(emitSingleTap, event.x, event.y);
|
|
828
|
+
}),
|
|
829
|
+
[emitSingleTap, pageTransitionLocked],
|
|
830
|
+
);
|
|
831
|
+
|
|
832
|
+
const doubleTapGesture = useMemo(
|
|
833
|
+
() =>
|
|
834
|
+
Gesture.Tap()
|
|
835
|
+
.enabled(enableDoubleTapZoom && enableNativeTapGestures)
|
|
836
|
+
.numberOfTaps(2)
|
|
837
|
+
.onEnd((event) => {
|
|
838
|
+
if (pageTransitionLocked.get()) {
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
applyTapZoomAtPoint({
|
|
843
|
+
x: event.x,
|
|
844
|
+
y: event.y,
|
|
845
|
+
width,
|
|
846
|
+
height,
|
|
847
|
+
maxZoomScale,
|
|
848
|
+
scale,
|
|
849
|
+
translateX,
|
|
850
|
+
translateY,
|
|
851
|
+
});
|
|
852
|
+
}),
|
|
853
|
+
[
|
|
854
|
+
enableDoubleTapZoom,
|
|
855
|
+
height,
|
|
856
|
+
maxZoomScale,
|
|
857
|
+
pageTransitionLocked,
|
|
858
|
+
scale,
|
|
859
|
+
translateX,
|
|
860
|
+
translateY,
|
|
861
|
+
width,
|
|
862
|
+
],
|
|
863
|
+
);
|
|
864
|
+
|
|
865
|
+
const tapGesture = useMemo(
|
|
866
|
+
() => Gesture.Exclusive(doubleTapGesture, singleTapGesture),
|
|
867
|
+
[doubleTapGesture, singleTapGesture],
|
|
868
|
+
);
|
|
522
869
|
|
|
523
870
|
const zoomPinchGesture = useMemo(
|
|
524
871
|
() =>
|
|
525
872
|
Gesture.Pinch()
|
|
526
873
|
.enabled(enablePinchZoom)
|
|
527
874
|
.onTouchesDown((event) => {
|
|
875
|
+
if (pageTransitionLocked.get()) {
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
|
|
528
879
|
if (event.numberOfTouches === 2) {
|
|
529
880
|
scheduleOnRN(setIsPinching, true);
|
|
530
881
|
}
|
|
531
882
|
})
|
|
532
883
|
.onStart((event) => {
|
|
884
|
+
if (pageTransitionLocked.get()) {
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
|
|
533
888
|
startScale.set(scale.get());
|
|
534
889
|
initialTranslateX.set(translateX.get());
|
|
535
890
|
initialTranslateY.set(translateY.get());
|
|
@@ -540,6 +895,10 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
540
895
|
hasActiveFocal.set(false);
|
|
541
896
|
})
|
|
542
897
|
.onUpdate((event) => {
|
|
898
|
+
if (pageTransitionLocked.get()) {
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
|
|
543
902
|
const initialScale = startScale.get();
|
|
544
903
|
const newScale = initialScale * event.scale;
|
|
545
904
|
|
|
@@ -601,6 +960,10 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
601
960
|
translateY.set(constrainedTranslateY);
|
|
602
961
|
})
|
|
603
962
|
.onEnd(() => {
|
|
963
|
+
if (pageTransitionLocked.get()) {
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
|
|
604
967
|
const currentScale = scale.get();
|
|
605
968
|
|
|
606
969
|
if (currentScale > maxZoomScale) {
|
|
@@ -673,6 +1036,7 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
673
1036
|
startFocalX,
|
|
674
1037
|
startFocalY,
|
|
675
1038
|
hasActiveFocal,
|
|
1039
|
+
pageTransitionLocked,
|
|
676
1040
|
],
|
|
677
1041
|
);
|
|
678
1042
|
|
|
@@ -683,10 +1047,18 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
683
1047
|
.activeCursor('grabbing')
|
|
684
1048
|
.averageTouches(true)
|
|
685
1049
|
.onBegin(() => {
|
|
1050
|
+
if (pageTransitionLocked.get()) {
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
686
1054
|
initialTranslateX.set(translateX.get());
|
|
687
1055
|
initialTranslateY.set(translateY.get());
|
|
688
1056
|
})
|
|
689
1057
|
.onUpdate((event) => {
|
|
1058
|
+
if (pageTransitionLocked.get()) {
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
690
1062
|
const currentScale = scale.get();
|
|
691
1063
|
|
|
692
1064
|
if (currentScale > 1) {
|
|
@@ -713,73 +1085,30 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
713
1085
|
initialTranslateX,
|
|
714
1086
|
initialTranslateY,
|
|
715
1087
|
constrainTranslation,
|
|
1088
|
+
pageTransitionLocked,
|
|
716
1089
|
],
|
|
717
1090
|
);
|
|
718
1091
|
|
|
719
|
-
const emitSingleTap = useCallback((x: number, y: number) => {
|
|
720
|
-
const index = pendingIndexRef.current;
|
|
721
|
-
const currentData = dataRef.current;
|
|
722
|
-
|
|
723
|
-
if (index < 0 || index >= currentData.length) {
|
|
724
|
-
return;
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
managerRef.current?.emitTap({ kind: 'single', x, y, index });
|
|
728
|
-
|
|
729
|
-
const item = currentData[index];
|
|
730
|
-
|
|
731
|
-
if (item === undefined) {
|
|
732
|
-
return;
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
onSingleTapRef.current?.({ x, y, index, item });
|
|
736
|
-
}, []);
|
|
737
|
-
|
|
738
|
-
const singleTapGesture = useMemo(
|
|
739
|
-
() =>
|
|
740
|
-
Gesture.Tap()
|
|
741
|
-
.enabled(Platform.OS !== 'web')
|
|
742
|
-
.numberOfTaps(1)
|
|
743
|
-
.onEnd((event, success) => {
|
|
744
|
-
if (!success) {
|
|
745
|
-
return;
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
scheduleOnRN(emitSingleTap, event.x, event.y);
|
|
749
|
-
}),
|
|
750
|
-
[emitSingleTap],
|
|
751
|
-
);
|
|
752
|
-
|
|
753
|
-
const doubleTapGesture = useMemo(
|
|
754
|
-
() =>
|
|
755
|
-
Gesture.Tap()
|
|
756
|
-
.enabled(enableDoubleTapZoom && Platform.OS !== 'web')
|
|
757
|
-
.numberOfTaps(2)
|
|
758
|
-
.onEnd((event) => {
|
|
759
|
-
applyTapZoomAtPoint({
|
|
760
|
-
x: event.x,
|
|
761
|
-
y: event.y,
|
|
762
|
-
width,
|
|
763
|
-
height,
|
|
764
|
-
maxZoomScale,
|
|
765
|
-
scale,
|
|
766
|
-
translateX,
|
|
767
|
-
translateY,
|
|
768
|
-
});
|
|
769
|
-
}),
|
|
770
|
-
[enableDoubleTapZoom, height, maxZoomScale, scale, translateX, translateY, width],
|
|
771
|
-
);
|
|
772
|
-
|
|
773
|
-
const tapGesture = useMemo(
|
|
774
|
-
() => Gesture.Exclusive(doubleTapGesture, singleTapGesture),
|
|
775
|
-
[doubleTapGesture, singleTapGesture],
|
|
776
|
-
);
|
|
777
|
-
|
|
778
1092
|
const zoomGesture = useMemo(
|
|
779
1093
|
() => Gesture.Race(zoomPinchGesture, Gesture.Exclusive(zoomPanGesture, tapGesture)),
|
|
780
1094
|
[zoomPinchGesture, zoomPanGesture, tapGesture],
|
|
781
1095
|
);
|
|
782
1096
|
|
|
1097
|
+
const onWebClick = useWebClickHandler({
|
|
1098
|
+
clearPendingWebSingleTap,
|
|
1099
|
+
emitSingleTap,
|
|
1100
|
+
enableDoubleTapZoom,
|
|
1101
|
+
getCurrentTapTarget,
|
|
1102
|
+
height,
|
|
1103
|
+
maxZoomScale,
|
|
1104
|
+
scale,
|
|
1105
|
+
scheduleWebSingleTap,
|
|
1106
|
+
isInteractionLocked: isWebInteractionLocked,
|
|
1107
|
+
translateX,
|
|
1108
|
+
translateY,
|
|
1109
|
+
width,
|
|
1110
|
+
});
|
|
1111
|
+
|
|
783
1112
|
const animatedStyle = useAnimatedStyle(() => ({
|
|
784
1113
|
opacity: triggerOpacity.get(),
|
|
785
1114
|
transform: [
|
|
@@ -807,52 +1136,22 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
807
1136
|
return { opacity: baseOpacity * dismissOpacity };
|
|
808
1137
|
}, [dismissOptions.direction, dismissOptions.fadeBackdrop]);
|
|
809
1138
|
|
|
810
|
-
const nativeScrollGesture = useMemo(() => {
|
|
811
|
-
return Gesture.Native().requireExternalGestureToFail(dismissGestureRef);
|
|
812
|
-
}, []);
|
|
813
|
-
|
|
814
|
-
const { onMomentumScrollEnd, onScroll, onScrollBeginDrag, onWebClick } = useGestureViewerPaging({
|
|
815
|
-
adjustedInitialIndex,
|
|
816
|
-
autoPlay,
|
|
817
|
-
autoPlayInterval,
|
|
818
|
-
currentIndex,
|
|
819
|
-
dataLength,
|
|
820
|
-
enableDoubleTapZoom,
|
|
821
|
-
enableHorizontalSwipe,
|
|
822
|
-
enableLoop,
|
|
823
|
-
height,
|
|
824
|
-
isRotated,
|
|
825
|
-
isZoomed,
|
|
826
|
-
itemSpacing,
|
|
827
|
-
manager,
|
|
828
|
-
maxZoomScale,
|
|
829
|
-
onSingleTap: emitSingleTap,
|
|
830
|
-
scale,
|
|
831
|
-
scrollTo,
|
|
832
|
-
syncCurrentIndex,
|
|
833
|
-
syncPendingIndex,
|
|
834
|
-
translateX,
|
|
835
|
-
translateY,
|
|
836
|
-
width,
|
|
837
|
-
});
|
|
838
|
-
|
|
839
1139
|
return {
|
|
840
1140
|
animatedStyle,
|
|
841
1141
|
backdropStyle,
|
|
1142
|
+
centerVirtualIndex: renderCenterVirtualIndex,
|
|
1143
|
+
currentIndex: renderCurrentIndex,
|
|
842
1144
|
dataLength,
|
|
843
1145
|
dismissGesture,
|
|
844
1146
|
handleDismiss,
|
|
1147
|
+
horizontalPagingGesture,
|
|
845
1148
|
isPinching,
|
|
846
1149
|
isRotated,
|
|
847
|
-
|
|
848
1150
|
isZoomed,
|
|
849
|
-
listRef,
|
|
850
|
-
nativeScrollGesture,
|
|
851
1151
|
onWebClick,
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
onScrollBeginDrag,
|
|
1152
|
+
pageStride,
|
|
1153
|
+
renderWindowSlots,
|
|
1154
|
+
visualPage,
|
|
856
1155
|
zoomGesture,
|
|
857
1156
|
};
|
|
858
1157
|
};
|