react-native-gesture-image-viewer 2.4.0 → 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 -146
- 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/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 -206
- 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 -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/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 +9 -9
- 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 -206
- 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/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 +557 -271
- package/src/useGestureViewerController.ts +3 -3
- package/src/useGestureViewerManagerBridge.ts +118 -0
- package/src/useGestureViewerPaging.ts +277 -114
- 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,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,22 +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;
|
|
105
|
-
const previousDataLengthRef = useRef(dataLength);
|
|
147
|
+
const { clearPendingWebSingleTap, scheduleWebSingleTap } = useWebSingleTapTimer();
|
|
106
148
|
|
|
107
149
|
const animationConfig = useMemo(
|
|
108
150
|
() => ({
|
|
@@ -130,28 +172,24 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
130
172
|
],
|
|
131
173
|
);
|
|
132
174
|
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
+
});
|
|
140
187
|
|
|
141
188
|
const constrainTranslation = useMemo(
|
|
142
189
|
() => createBoundsConstraint({ height, width }),
|
|
143
190
|
[width, height],
|
|
144
191
|
);
|
|
145
192
|
|
|
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
193
|
const resetTransformState = useCallback(() => {
|
|
156
194
|
translateX.set(withTiming(0));
|
|
157
195
|
translateY.set(withTiming(0));
|
|
@@ -162,38 +200,179 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
162
200
|
rotation.set(0);
|
|
163
201
|
}, [initialTranslateX, initialTranslateY, rotation, scale, startScale, translateX, translateY]);
|
|
164
202
|
|
|
165
|
-
const
|
|
166
|
-
(
|
|
167
|
-
|
|
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) {
|
|
168
236
|
return;
|
|
169
237
|
}
|
|
170
238
|
|
|
171
|
-
|
|
239
|
+
centerVirtualIndexRef.current = nextVirtualIndex;
|
|
240
|
+
setCenterVirtualIndex(nextVirtualIndex);
|
|
241
|
+
commitCurrentIndex(nextLogicalIndex);
|
|
172
242
|
},
|
|
173
|
-
[
|
|
243
|
+
[commitCurrentIndex],
|
|
174
244
|
);
|
|
175
245
|
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
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') {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (
|
|
287
|
+
isPageTransitioningRef.current &&
|
|
288
|
+
!shouldRunNavigationDuringTransition(resolution, options)
|
|
289
|
+
) {
|
|
179
290
|
return;
|
|
180
291
|
}
|
|
181
292
|
|
|
182
|
-
|
|
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
|
+
);
|
|
183
302
|
|
|
184
|
-
|
|
303
|
+
if (targetVirtualIndex === null) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
185
306
|
|
|
186
|
-
if (
|
|
307
|
+
if (resolution.kind !== 'step' || options?.animated === false) {
|
|
308
|
+
resetTransformStateImmediately();
|
|
309
|
+
snapToVirtualPage(targetVirtualIndex);
|
|
187
310
|
return;
|
|
188
311
|
}
|
|
189
312
|
|
|
190
|
-
manager.setCurrentIndex(nextIndex);
|
|
191
|
-
manager.notifyStateChange();
|
|
192
313
|
resetTransformState();
|
|
314
|
+
animateToVirtualPage(targetVirtualIndex);
|
|
193
315
|
},
|
|
194
|
-
[
|
|
316
|
+
[
|
|
317
|
+
animateToVirtualPage,
|
|
318
|
+
isPageTransitioningRef,
|
|
319
|
+
resetTransformStateImmediately,
|
|
320
|
+
resetTransformState,
|
|
321
|
+
snapToVirtualPage,
|
|
322
|
+
],
|
|
195
323
|
);
|
|
196
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
|
+
|
|
197
376
|
const emitZoomChange = useCallback((currentScale: number, prevScale: number | null) => {
|
|
198
377
|
managerRef.current?.emitZoomChange(currentScale, prevScale);
|
|
199
378
|
}, []);
|
|
@@ -202,146 +381,203 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
202
381
|
managerRef.current?.emitRotationChange(currentRotation, prevRotation);
|
|
203
382
|
}, []);
|
|
204
383
|
|
|
205
|
-
const
|
|
384
|
+
const finishTriggerOpening = useCallback(() => {
|
|
385
|
+
setIsTriggerOpening(false);
|
|
206
386
|
onAnimationCompleteRef.current?.();
|
|
207
387
|
}, []);
|
|
208
388
|
|
|
209
389
|
useAnimatedReaction(
|
|
210
390
|
() => scale.get(),
|
|
211
391
|
(currentScale, previousScale) => {
|
|
212
|
-
if (currentScale !== previousScale) {
|
|
392
|
+
if (currentScale !== previousScale && hasZoomChangeListeners.get()) {
|
|
213
393
|
scheduleOnRN(emitZoomChange, currentScale, previousScale);
|
|
214
394
|
}
|
|
215
395
|
|
|
216
|
-
|
|
396
|
+
const currentIsZoomed = currentScale > 1;
|
|
397
|
+
const previousIsZoomed = (previousScale ?? 1) > 1;
|
|
398
|
+
|
|
399
|
+
if (previousScale === null || currentIsZoomed !== previousIsZoomed) {
|
|
400
|
+
scheduleOnRN(setIsZoomed, currentIsZoomed);
|
|
401
|
+
}
|
|
217
402
|
},
|
|
218
403
|
);
|
|
219
404
|
|
|
220
405
|
useAnimatedReaction(
|
|
221
406
|
() => rotation.get(),
|
|
222
407
|
(currentRotation, previousRotation) => {
|
|
223
|
-
if (currentRotation !== previousRotation) {
|
|
408
|
+
if (currentRotation !== previousRotation && hasRotationChangeListeners.get()) {
|
|
224
409
|
scheduleOnRN(emitRotationChange, currentRotation, previousRotation);
|
|
225
410
|
}
|
|
226
411
|
|
|
227
|
-
|
|
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
|
+
}
|
|
228
418
|
},
|
|
229
419
|
);
|
|
230
420
|
|
|
231
421
|
useEffect(() => {
|
|
232
|
-
|
|
422
|
+
return registry.subscribeToActiveTrigger(id, (node) => {
|
|
423
|
+
setActiveTriggerNode(node);
|
|
233
424
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
pendingIndexRef.current = state.currentIndex;
|
|
238
|
-
setCurrentIndex(state.currentIndex);
|
|
239
|
-
});
|
|
425
|
+
if (node) {
|
|
426
|
+
setIsTriggerOpening(true);
|
|
427
|
+
}
|
|
240
428
|
});
|
|
241
|
-
|
|
242
|
-
return () => {
|
|
243
|
-
unsubscribeFromRegistry();
|
|
244
|
-
unsubscribe?.();
|
|
245
|
-
};
|
|
246
429
|
}, [id]);
|
|
247
430
|
|
|
248
431
|
useEffect(() => {
|
|
249
|
-
|
|
250
|
-
}, [
|
|
432
|
+
dataRef.current = data;
|
|
433
|
+
}, [data]);
|
|
251
434
|
|
|
252
435
|
useEffect(() => {
|
|
253
|
-
|
|
436
|
+
isZoomedRef.current = isZoomed;
|
|
437
|
+
}, [isZoomed]);
|
|
254
438
|
|
|
255
|
-
|
|
256
|
-
|
|
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
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
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();
|
|
257
502
|
}
|
|
258
503
|
|
|
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();
|
|
504
|
+
if (shouldResetTransform) {
|
|
505
|
+
resetTransformState();
|
|
506
|
+
}
|
|
269
507
|
}, [
|
|
508
|
+
commitCurrentIndex,
|
|
270
509
|
dataLength,
|
|
271
|
-
enableHorizontalSwipe,
|
|
272
|
-
initialIndex,
|
|
273
|
-
manager,
|
|
274
|
-
width,
|
|
275
|
-
itemSpacing,
|
|
276
|
-
maxZoomScale,
|
|
277
510
|
enableLoop,
|
|
278
|
-
|
|
279
|
-
|
|
511
|
+
initialIndex,
|
|
512
|
+
isPageTransitioningRef,
|
|
280
513
|
resetTransformState,
|
|
281
|
-
|
|
282
|
-
translateY,
|
|
283
|
-
rotation,
|
|
514
|
+
snapToVirtualPage,
|
|
284
515
|
]);
|
|
285
516
|
|
|
286
517
|
useEffect(() => {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}, [manager]);
|
|
518
|
+
return () => {
|
|
519
|
+
triggerRectRef.current = null;
|
|
520
|
+
clearPendingWebSingleTap();
|
|
521
|
+
};
|
|
522
|
+
}, [clearPendingWebSingleTap]);
|
|
293
523
|
|
|
294
524
|
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
525
|
if (
|
|
307
|
-
!
|
|
308
|
-
|
|
309
|
-
|
|
526
|
+
!autoPlay ||
|
|
527
|
+
dataLength <= 1 ||
|
|
528
|
+
isTriggerOpening ||
|
|
529
|
+
isZoomed ||
|
|
530
|
+
isRotated ||
|
|
531
|
+
isPinching ||
|
|
532
|
+
(!enableLoop && currentIndex >= dataLength - 1)
|
|
310
533
|
) {
|
|
311
534
|
return;
|
|
312
535
|
}
|
|
313
536
|
|
|
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
|
-
]);
|
|
537
|
+
const interval = Math.max(250, Math.floor(autoPlayInterval || 0));
|
|
329
538
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
539
|
+
if (!Number.isFinite(interval)) {
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
333
542
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
543
|
+
const timer = setInterval(() => {
|
|
544
|
+
if (
|
|
545
|
+
isPageTransitioningRef.current ||
|
|
546
|
+
isZoomedRef.current ||
|
|
547
|
+
isRotatedRef.current ||
|
|
548
|
+
isPinchingRef.current
|
|
549
|
+
) {
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
337
552
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
553
|
+
const nextIndex =
|
|
554
|
+
currentIndexRef.current >= dataLengthRef.current - 1
|
|
555
|
+
? enableLoopRef.current
|
|
556
|
+
? 0
|
|
557
|
+
: null
|
|
558
|
+
: currentIndexRef.current + 1;
|
|
341
559
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
560
|
+
if (nextIndex === null) {
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
navigateToIndex(nextIndex);
|
|
565
|
+
}, interval);
|
|
566
|
+
|
|
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
|
+
]);
|
|
345
581
|
|
|
346
582
|
useEffect(() => {
|
|
347
583
|
if (shouldStartTriggerAnimation && triggerRectRef.current) {
|
|
@@ -360,7 +596,7 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
360
596
|
triggerScale.set(
|
|
361
597
|
withTiming(1, animationConfig, (finished) => {
|
|
362
598
|
if (finished) {
|
|
363
|
-
scheduleOnRN(
|
|
599
|
+
scheduleOnRN(finishTriggerOpening);
|
|
364
600
|
}
|
|
365
601
|
}),
|
|
366
602
|
);
|
|
@@ -385,11 +621,18 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
385
621
|
triggerScale,
|
|
386
622
|
triggerTranslateX,
|
|
387
623
|
triggerTranslateY,
|
|
388
|
-
|
|
624
|
+
finishTriggerOpening,
|
|
389
625
|
]);
|
|
390
626
|
|
|
391
627
|
useEffect(() => {
|
|
392
|
-
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);
|
|
393
636
|
return;
|
|
394
637
|
}
|
|
395
638
|
|
|
@@ -402,6 +645,8 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
402
645
|
} satisfies TriggerRect;
|
|
403
646
|
|
|
404
647
|
if (!isValidTriggerRect(nextTriggerRect)) {
|
|
648
|
+
setIsTriggerOpening(false);
|
|
649
|
+
triggerOpacity.set(1);
|
|
405
650
|
registry.clearActiveTriggerNode(id);
|
|
406
651
|
return;
|
|
407
652
|
}
|
|
@@ -411,13 +656,7 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
411
656
|
setShouldStartTriggerAnimation(true);
|
|
412
657
|
registry.clearActiveTriggerNode(id);
|
|
413
658
|
});
|
|
414
|
-
}, [activeTriggerNode, id,
|
|
415
|
-
|
|
416
|
-
useEffect(() => {
|
|
417
|
-
return () => {
|
|
418
|
-
triggerRectRef.current = null;
|
|
419
|
-
};
|
|
420
|
-
}, []);
|
|
659
|
+
}, [activeTriggerNode, id, triggerOpacity]);
|
|
421
660
|
|
|
422
661
|
const animateDismissToRect = useCallback(
|
|
423
662
|
(rect: TriggerRect) => {
|
|
@@ -455,6 +694,10 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
455
694
|
}, [onDismiss]);
|
|
456
695
|
|
|
457
696
|
const handleDismiss = useCallback(() => {
|
|
697
|
+
if (isPageTransitioningRef.current) {
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
|
|
458
701
|
onDismissStart?.();
|
|
459
702
|
|
|
460
703
|
const dismissTargetIndex = pendingIndexRef.current;
|
|
@@ -490,10 +733,10 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
490
733
|
}
|
|
491
734
|
|
|
492
735
|
dismissWithoutTrigger();
|
|
493
|
-
}, [animateDismissToRect, dismissWithoutTrigger, id,
|
|
736
|
+
}, [animateDismissToRect, dismissWithoutTrigger, id, isPageTransitioningRef, onDismissStart]);
|
|
494
737
|
|
|
495
738
|
const dismissGesture = useMemo(() => {
|
|
496
|
-
const canDismiss = !isZoomed && dismissOptions.enabled;
|
|
739
|
+
const canDismiss = !isTriggerOpening && !isZoomed && dismissOptions.enabled;
|
|
497
740
|
|
|
498
741
|
return Gesture.Pan()
|
|
499
742
|
.minDistance(10)
|
|
@@ -504,9 +747,18 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
504
747
|
.withRef(dismissGestureRef)
|
|
505
748
|
.enabled(canDismiss)
|
|
506
749
|
.onUpdate((event) => {
|
|
750
|
+
if (pageTransitionLocked.get()) {
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
|
|
507
754
|
translateY.set(event.translationY / dismissOptions.resistance);
|
|
508
755
|
})
|
|
509
756
|
.onEnd((event) => {
|
|
757
|
+
if (pageTransitionLocked.get()) {
|
|
758
|
+
translateY.set(withSpring(0, PAGE_SPRING_CONFIG));
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
|
|
510
762
|
if (
|
|
511
763
|
canDismiss &&
|
|
512
764
|
shouldDismissByDirection(
|
|
@@ -519,28 +771,120 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
519
771
|
return;
|
|
520
772
|
}
|
|
521
773
|
|
|
522
|
-
translateY.set(
|
|
523
|
-
withSpring(0, {
|
|
524
|
-
damping: 50,
|
|
525
|
-
energyThreshold: 6e-9,
|
|
526
|
-
mass: 4,
|
|
527
|
-
overshootClamping: false,
|
|
528
|
-
stiffness: 600,
|
|
529
|
-
}),
|
|
530
|
-
);
|
|
774
|
+
translateY.set(withSpring(0, PAGE_SPRING_CONFIG));
|
|
531
775
|
});
|
|
532
|
-
}, [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
|
+
);
|
|
533
869
|
|
|
534
870
|
const zoomPinchGesture = useMemo(
|
|
535
871
|
() =>
|
|
536
872
|
Gesture.Pinch()
|
|
537
873
|
.enabled(enablePinchZoom)
|
|
538
874
|
.onTouchesDown((event) => {
|
|
875
|
+
if (pageTransitionLocked.get()) {
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
|
|
539
879
|
if (event.numberOfTouches === 2) {
|
|
540
880
|
scheduleOnRN(setIsPinching, true);
|
|
541
881
|
}
|
|
542
882
|
})
|
|
543
883
|
.onStart((event) => {
|
|
884
|
+
if (pageTransitionLocked.get()) {
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
|
|
544
888
|
startScale.set(scale.get());
|
|
545
889
|
initialTranslateX.set(translateX.get());
|
|
546
890
|
initialTranslateY.set(translateY.get());
|
|
@@ -551,6 +895,10 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
551
895
|
hasActiveFocal.set(false);
|
|
552
896
|
})
|
|
553
897
|
.onUpdate((event) => {
|
|
898
|
+
if (pageTransitionLocked.get()) {
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
|
|
554
902
|
const initialScale = startScale.get();
|
|
555
903
|
const newScale = initialScale * event.scale;
|
|
556
904
|
|
|
@@ -612,6 +960,10 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
612
960
|
translateY.set(constrainedTranslateY);
|
|
613
961
|
})
|
|
614
962
|
.onEnd(() => {
|
|
963
|
+
if (pageTransitionLocked.get()) {
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
|
|
615
967
|
const currentScale = scale.get();
|
|
616
968
|
|
|
617
969
|
if (currentScale > maxZoomScale) {
|
|
@@ -684,6 +1036,7 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
684
1036
|
startFocalX,
|
|
685
1037
|
startFocalY,
|
|
686
1038
|
hasActiveFocal,
|
|
1039
|
+
pageTransitionLocked,
|
|
687
1040
|
],
|
|
688
1041
|
);
|
|
689
1042
|
|
|
@@ -694,10 +1047,18 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
694
1047
|
.activeCursor('grabbing')
|
|
695
1048
|
.averageTouches(true)
|
|
696
1049
|
.onBegin(() => {
|
|
1050
|
+
if (pageTransitionLocked.get()) {
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
697
1054
|
initialTranslateX.set(translateX.get());
|
|
698
1055
|
initialTranslateY.set(translateY.get());
|
|
699
1056
|
})
|
|
700
1057
|
.onUpdate((event) => {
|
|
1058
|
+
if (pageTransitionLocked.get()) {
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
701
1062
|
const currentScale = scale.get();
|
|
702
1063
|
|
|
703
1064
|
if (currentScale > 1) {
|
|
@@ -724,73 +1085,30 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
724
1085
|
initialTranslateX,
|
|
725
1086
|
initialTranslateY,
|
|
726
1087
|
constrainTranslation,
|
|
1088
|
+
pageTransitionLocked,
|
|
727
1089
|
],
|
|
728
1090
|
);
|
|
729
1091
|
|
|
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
1092
|
const zoomGesture = useMemo(
|
|
790
1093
|
() => Gesture.Race(zoomPinchGesture, Gesture.Exclusive(zoomPanGesture, tapGesture)),
|
|
791
1094
|
[zoomPinchGesture, zoomPanGesture, tapGesture],
|
|
792
1095
|
);
|
|
793
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
|
+
|
|
794
1112
|
const animatedStyle = useAnimatedStyle(() => ({
|
|
795
1113
|
opacity: triggerOpacity.get(),
|
|
796
1114
|
transform: [
|
|
@@ -818,54 +1136,22 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
818
1136
|
return { opacity: baseOpacity * dismissOpacity };
|
|
819
1137
|
}, [dismissOptions.direction, dismissOptions.fadeBackdrop]);
|
|
820
1138
|
|
|
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
1139
|
return {
|
|
852
|
-
activeListIndex,
|
|
853
1140
|
animatedStyle,
|
|
854
1141
|
backdropStyle,
|
|
1142
|
+
centerVirtualIndex: renderCenterVirtualIndex,
|
|
1143
|
+
currentIndex: renderCurrentIndex,
|
|
855
1144
|
dataLength,
|
|
856
1145
|
dismissGesture,
|
|
857
1146
|
handleDismiss,
|
|
1147
|
+
horizontalPagingGesture,
|
|
858
1148
|
isPinching,
|
|
859
1149
|
isRotated,
|
|
860
|
-
|
|
861
1150
|
isZoomed,
|
|
862
|
-
listRef,
|
|
863
|
-
nativeScrollGesture,
|
|
864
1151
|
onWebClick,
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
onScrollBeginDrag,
|
|
1152
|
+
pageStride,
|
|
1153
|
+
renderWindowSlots,
|
|
1154
|
+
visualPage,
|
|
869
1155
|
zoomGesture,
|
|
870
1156
|
};
|
|
871
1157
|
};
|