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
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
|
-
import {
|
|
3
|
+
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
4
|
+
import { useWindowDimensions } from 'react-native';
|
|
5
5
|
import { Gesture } from 'react-native-gesture-handler';
|
|
6
|
-
import { Easing, interpolate, useAnimatedReaction, useAnimatedStyle, useSharedValue, withSpring, withTiming } from 'react-native-reanimated';
|
|
6
|
+
import { Easing, cancelAnimation, interpolate, useAnimatedReaction, useAnimatedStyle, useSharedValue, withSpring, withTiming } from 'react-native-reanimated';
|
|
7
7
|
import { scheduleOnRN } from 'react-native-worklets';
|
|
8
|
+
import { PAGE_SPRING_CONFIG, SWIPE_THRESHOLD_RATIO, SWIPE_VELOCITY_THRESHOLD } from "./gestureViewerAnimation.js";
|
|
9
|
+
import { normalizeHorizontalSwipeThreshold } from "./gestureViewerPaging.js";
|
|
8
10
|
import { registry } from "./GestureViewerRegistry.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
11
|
+
import { resolveGestureViewerRenderWindow } from "./gestureViewerRenderWindow.js";
|
|
12
|
+
import { enableNativeTapGestures } from './platformTapGestures';
|
|
13
|
+
import { getLogicalIndex, getPageStride, getVirtualIndexForLogicalIndex, normalizePageSpacing, clampIndex, resolveNavigation, shouldRunNavigationDuringTransition } from "./renderWindow.js";
|
|
14
|
+
import { useGestureViewerManagerBridge } from "./useGestureViewerManagerBridge.js";
|
|
15
|
+
import { useGestureViewerPaging } from "./useGestureViewerPaging.js";
|
|
16
|
+
import { useWebClickHandler } from './useWebClickHandler';
|
|
17
|
+
import { useWebSingleTapTimer } from './useWebSingleTapTimer';
|
|
18
|
+
import { createBoundsConstraint } from "./utils/index.js";
|
|
12
19
|
import { getDismissDistance, shouldDismissByDirection } from "./utils/dismiss.js";
|
|
13
20
|
import { applyTapZoomAtPoint } from "./utils/tapZoom.js";
|
|
14
21
|
import { calculateFocalPointTranslation, shouldAcceptFocalPoint } from "./utils/zoom.js";
|
|
22
|
+
const isValidTriggerRect = rect => {
|
|
23
|
+
return !!rect && rect.width > 0 && rect.height > 0;
|
|
24
|
+
};
|
|
15
25
|
export const useGestureViewer = ({
|
|
16
26
|
data,
|
|
17
27
|
initialIndex = 0,
|
|
@@ -21,11 +31,12 @@ export const useGestureViewer = ({
|
|
|
21
31
|
dismiss,
|
|
22
32
|
enableDoubleTapZoom = true,
|
|
23
33
|
enablePinchZoom = true,
|
|
24
|
-
|
|
34
|
+
horizontalSwipe,
|
|
25
35
|
enablePanWhenZoomed = true,
|
|
26
36
|
enableLoop = false,
|
|
27
37
|
maxZoomScale = 2,
|
|
28
|
-
|
|
38
|
+
pageSpacing = 0,
|
|
39
|
+
windowSize = 3,
|
|
29
40
|
height: customHeight,
|
|
30
41
|
id = 'default',
|
|
31
42
|
onDismissStart,
|
|
@@ -39,43 +50,59 @@ export const useGestureViewer = ({
|
|
|
39
50
|
} = useWindowDimensions();
|
|
40
51
|
const width = customWidth || screenWidth;
|
|
41
52
|
const height = customHeight || screenHeight;
|
|
53
|
+
const dataLength = data?.length || 0;
|
|
54
|
+
const normalizedPageSpacing = normalizePageSpacing(pageSpacing);
|
|
55
|
+
const pageStride = getPageStride(width, normalizedPageSpacing);
|
|
56
|
+
const initialCurrentIndex = clampIndex(initialIndex, dataLength);
|
|
57
|
+
const horizontalSwipeEnabled = horizontalSwipe?.enabled ?? true;
|
|
58
|
+
const horizontalSwipeDistanceThresholdRatio = normalizeHorizontalSwipeThreshold(horizontalSwipe?.distanceThresholdRatio, SWIPE_THRESHOLD_RATIO);
|
|
59
|
+
const horizontalSwipeVelocityThreshold = normalizeHorizontalSwipeThreshold(horizontalSwipe?.velocityThreshold, SWIPE_VELOCITY_THRESHOLD);
|
|
42
60
|
const dismissGestureRef = useRef(undefined);
|
|
43
61
|
const [isZoomed, setIsZoomed] = useState(false);
|
|
44
62
|
const [isRotated, setIsRotated] = useState(false);
|
|
45
63
|
const [isPinching, setIsPinching] = useState(false);
|
|
46
64
|
const [shouldStartTriggerAnimation, setShouldStartTriggerAnimation] = useState(false);
|
|
47
|
-
const [
|
|
48
|
-
const [
|
|
49
|
-
const [activeTriggerNode, setActiveTriggerNode] = useState(
|
|
50
|
-
const
|
|
65
|
+
const [currentIndex, setCurrentIndex] = useState(initialCurrentIndex);
|
|
66
|
+
const [centerVirtualIndex, setCenterVirtualIndex] = useState(initialCurrentIndex);
|
|
67
|
+
const [activeTriggerNode, setActiveTriggerNode] = useState(() => registry.getActiveTriggerNode(id));
|
|
68
|
+
const [isTriggerOpening, setIsTriggerOpening] = useState(() => !!registry.getActiveTriggerNode(id));
|
|
51
69
|
const triggerRectRef = useRef(null);
|
|
52
|
-
const pendingIndexRef = useRef(
|
|
70
|
+
const pendingIndexRef = useRef(initialCurrentIndex);
|
|
71
|
+
const currentIndexRef = useRef(initialCurrentIndex);
|
|
72
|
+
const centerVirtualIndexRef = useRef(initialCurrentIndex);
|
|
73
|
+
const previousInitialIndexRef = useRef(initialIndex);
|
|
74
|
+
const hasResolvedInitialIndexRef = useRef(dataLength > 0);
|
|
53
75
|
const onAnimationCompleteRef = useRef(triggerAnimation?.onAnimationComplete);
|
|
54
76
|
const onSingleTapRef = useRef(onSingleTap);
|
|
55
77
|
const dataRef = useRef(data);
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
78
|
+
const dataLengthRef = useRef(dataLength);
|
|
79
|
+
const enableLoopRef = useRef(enableLoop);
|
|
80
|
+
const managerRef = useRef(null);
|
|
81
|
+
const isZoomedRef = useRef(isZoomed);
|
|
82
|
+
const isRotatedRef = useRef(isRotated);
|
|
83
|
+
const isPinchingRef = useRef(isPinching);
|
|
60
84
|
const initialTranslateY = useSharedValue(0);
|
|
61
85
|
const initialTranslateX = useSharedValue(0);
|
|
62
86
|
const startScale = useSharedValue(1);
|
|
63
87
|
const translateY = useSharedValue(0);
|
|
64
88
|
const translateX = useSharedValue(0);
|
|
65
89
|
const scale = useSharedValue(1);
|
|
66
|
-
const backdropOpacity = useSharedValue(1);
|
|
67
90
|
const rotation = useSharedValue(0);
|
|
68
91
|
const triggerScale = useSharedValue(1);
|
|
69
92
|
const triggerTranslateX = useSharedValue(0);
|
|
70
93
|
const triggerTranslateY = useSharedValue(0);
|
|
71
|
-
const triggerOpacity = useSharedValue(1);
|
|
94
|
+
const triggerOpacity = useSharedValue(activeTriggerNode ? 0 : 1);
|
|
95
|
+
const hasZoomChangeListeners = useSharedValue(false);
|
|
96
|
+
const hasRotationChangeListeners = useSharedValue(false);
|
|
72
97
|
const lastFocalX = useSharedValue(0);
|
|
73
98
|
const lastFocalY = useSharedValue(0);
|
|
74
99
|
const startFocalX = useSharedValue(0);
|
|
75
100
|
const startFocalY = useSharedValue(0);
|
|
76
101
|
const hasActiveFocal = useSharedValue(false);
|
|
77
|
-
const
|
|
78
|
-
|
|
102
|
+
const {
|
|
103
|
+
clearPendingWebSingleTap,
|
|
104
|
+
scheduleWebSingleTap
|
|
105
|
+
} = useWebSingleTapTimer();
|
|
79
106
|
const animationConfig = useMemo(() => ({
|
|
80
107
|
duration: triggerAnimation?.duration ?? 300,
|
|
81
108
|
easing: triggerAnimation?.easing ?? Easing.bezier(0.25, 0.1, 0.25, 1),
|
|
@@ -88,20 +115,22 @@ export const useGestureViewer = ({
|
|
|
88
115
|
resistance: dismiss?.resistance ?? 2,
|
|
89
116
|
threshold: dismiss?.threshold ?? 80
|
|
90
117
|
}), [dismiss?.direction, dismiss?.enabled, dismiss?.threshold, dismiss?.resistance, dismiss?.fadeBackdrop]);
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
118
|
+
const {
|
|
119
|
+
centerVirtualIndex: renderCenterVirtualIndex,
|
|
120
|
+
currentIndex: renderCurrentIndex,
|
|
121
|
+
slots: renderWindowSlots
|
|
122
|
+
} = resolveGestureViewerRenderWindow({
|
|
123
|
+
centerVirtualIndex,
|
|
124
|
+
currentIndex,
|
|
125
|
+
data,
|
|
126
|
+
enableLoop,
|
|
127
|
+
isTriggerOpening,
|
|
128
|
+
windowSize
|
|
129
|
+
});
|
|
97
130
|
const constrainTranslation = useMemo(() => createBoundsConstraint({
|
|
98
131
|
height,
|
|
99
132
|
width
|
|
100
133
|
}), [width, height]);
|
|
101
|
-
const scrollTo = useCallback((index, animated) => {
|
|
102
|
-
const scrollAction = createScrollAction(listRef.current, width + itemSpacing);
|
|
103
|
-
return scrollAction.scrollTo(index, animated);
|
|
104
|
-
}, [width, itemSpacing]);
|
|
105
134
|
const resetTransformState = useCallback(() => {
|
|
106
135
|
translateX.set(withTiming(0));
|
|
107
136
|
translateY.set(withTiming(0));
|
|
@@ -111,114 +140,240 @@ export const useGestureViewer = ({
|
|
|
111
140
|
scale.set(withTiming(1));
|
|
112
141
|
rotation.set(0);
|
|
113
142
|
}, [initialTranslateX, initialTranslateY, rotation, scale, startScale, translateX, translateY]);
|
|
114
|
-
const
|
|
115
|
-
|
|
143
|
+
const resetTransformStateImmediately = useCallback(() => {
|
|
144
|
+
cancelAnimation(translateX);
|
|
145
|
+
cancelAnimation(translateY);
|
|
146
|
+
cancelAnimation(initialTranslateX);
|
|
147
|
+
cancelAnimation(initialTranslateY);
|
|
148
|
+
cancelAnimation(startScale);
|
|
149
|
+
cancelAnimation(scale);
|
|
150
|
+
cancelAnimation(rotation);
|
|
151
|
+
translateX.set(0);
|
|
152
|
+
translateY.set(0);
|
|
153
|
+
initialTranslateX.set(0);
|
|
154
|
+
initialTranslateY.set(0);
|
|
155
|
+
startScale.set(1);
|
|
156
|
+
scale.set(1);
|
|
157
|
+
rotation.set(0);
|
|
158
|
+
}, [initialTranslateX, initialTranslateY, rotation, scale, startScale, translateX, translateY]);
|
|
159
|
+
const commitCurrentIndex = useCallback(nextIndex => {
|
|
160
|
+
pendingIndexRef.current = nextIndex;
|
|
161
|
+
currentIndexRef.current = nextIndex;
|
|
162
|
+
setCurrentIndex(nextIndex);
|
|
163
|
+
managerRef.current?.notifyStateChange();
|
|
164
|
+
}, []);
|
|
165
|
+
const commitVirtualIndexOnly = useCallback(nextVirtualIndex => {
|
|
166
|
+
const nextLogicalIndex = getLogicalIndex(nextVirtualIndex, dataLengthRef.current, enableLoopRef.current);
|
|
167
|
+
if (nextLogicalIndex === null) {
|
|
116
168
|
return;
|
|
117
169
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
170
|
+
centerVirtualIndexRef.current = nextVirtualIndex;
|
|
171
|
+
setCenterVirtualIndex(nextVirtualIndex);
|
|
172
|
+
commitCurrentIndex(nextLogicalIndex);
|
|
173
|
+
}, [commitCurrentIndex]);
|
|
174
|
+
const {
|
|
175
|
+
animateToVirtualPage,
|
|
176
|
+
horizontalPagingGesture,
|
|
177
|
+
isPageTransitioningRef,
|
|
178
|
+
pageTransitionLocked,
|
|
179
|
+
snapToVirtualPage,
|
|
180
|
+
visualPage
|
|
181
|
+
} = useGestureViewerPaging({
|
|
182
|
+
centerVirtualIndex,
|
|
183
|
+
clearPendingWebSingleTap,
|
|
184
|
+
commitVirtualIndexOnly,
|
|
185
|
+
currentIndex,
|
|
186
|
+
dataLength,
|
|
187
|
+
enableLoop,
|
|
188
|
+
horizontalSwipeDistanceThresholdRatio,
|
|
189
|
+
horizontalSwipeEnabled,
|
|
190
|
+
horizontalSwipeVelocityThreshold,
|
|
191
|
+
initialPage: initialCurrentIndex,
|
|
192
|
+
isPinching,
|
|
193
|
+
isRotated,
|
|
194
|
+
isTriggerOpening,
|
|
195
|
+
isZoomed,
|
|
196
|
+
pageStride,
|
|
197
|
+
width
|
|
198
|
+
});
|
|
199
|
+
const navigateToIndex = useCallback((targetIndex, options, preferredDirection) => {
|
|
200
|
+
const resolution = resolveNavigation({
|
|
201
|
+
currentIndex: currentIndexRef.current,
|
|
202
|
+
dataLength: dataLengthRef.current,
|
|
203
|
+
enableLoop: enableLoopRef.current,
|
|
204
|
+
preferredDirection,
|
|
205
|
+
targetIndex
|
|
206
|
+
});
|
|
207
|
+
if (resolution.kind === 'noop') {
|
|
122
208
|
return;
|
|
123
209
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
210
|
+
if (isPageTransitioningRef.current && !shouldRunNavigationDuringTransition(resolution, options)) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const targetVirtualIndex = resolution.kind === 'step' ? centerVirtualIndexRef.current + resolution.direction : getVirtualIndexForLogicalIndex(resolution.targetIndex, centerVirtualIndexRef.current, dataLengthRef.current, enableLoopRef.current);
|
|
214
|
+
if (targetVirtualIndex === null) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (resolution.kind !== 'step' || options?.animated === false) {
|
|
218
|
+
resetTransformStateImmediately();
|
|
219
|
+
snapToVirtualPage(targetVirtualIndex);
|
|
127
220
|
return;
|
|
128
221
|
}
|
|
129
|
-
manager.setCurrentIndex(nextIndex);
|
|
130
|
-
manager.notifyStateChange();
|
|
131
222
|
resetTransformState();
|
|
132
|
-
|
|
223
|
+
animateToVirtualPage(targetVirtualIndex);
|
|
224
|
+
}, [animateToVirtualPage, isPageTransitioningRef, resetTransformStateImmediately, resetTransformState, snapToVirtualPage]);
|
|
225
|
+
const navigateByDirection = useCallback(direction => {
|
|
226
|
+
const currentDataLength = dataLengthRef.current;
|
|
227
|
+
if (currentDataLength <= 0) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const nextIndex = currentIndexRef.current + direction;
|
|
231
|
+
if (nextIndex >= 0 && nextIndex < currentDataLength) {
|
|
232
|
+
navigateToIndex(nextIndex, undefined, direction);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (!enableLoopRef.current || currentDataLength <= 1) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
navigateToIndex(direction === 1 ? 0 : currentDataLength - 1, undefined, direction);
|
|
239
|
+
}, [navigateToIndex]);
|
|
240
|
+
const navigateToNext = useCallback(() => {
|
|
241
|
+
navigateByDirection(1);
|
|
242
|
+
}, [navigateByDirection]);
|
|
243
|
+
const navigateToPrevious = useCallback(() => {
|
|
244
|
+
navigateByDirection(-1);
|
|
245
|
+
}, [navigateByDirection]);
|
|
246
|
+
useGestureViewerManagerBridge({
|
|
247
|
+
currentIndexRef,
|
|
248
|
+
dataLengthRef,
|
|
249
|
+
goToIndex: navigateToIndex,
|
|
250
|
+
goToNext: navigateToNext,
|
|
251
|
+
goToPrevious: navigateToPrevious,
|
|
252
|
+
hasRotationChangeListeners,
|
|
253
|
+
hasZoomChangeListeners,
|
|
254
|
+
height,
|
|
255
|
+
id,
|
|
256
|
+
managerRef,
|
|
257
|
+
maxZoomScale,
|
|
258
|
+
rotation,
|
|
259
|
+
scale,
|
|
260
|
+
translateX,
|
|
261
|
+
translateY,
|
|
262
|
+
width
|
|
263
|
+
});
|
|
133
264
|
const emitZoomChange = useCallback((currentScale, prevScale) => {
|
|
134
265
|
managerRef.current?.emitZoomChange(currentScale, prevScale);
|
|
135
266
|
}, []);
|
|
136
267
|
const emitRotationChange = useCallback((currentRotation, prevRotation) => {
|
|
137
268
|
managerRef.current?.emitRotationChange(currentRotation, prevRotation);
|
|
138
269
|
}, []);
|
|
139
|
-
const
|
|
270
|
+
const finishTriggerOpening = useCallback(() => {
|
|
271
|
+
setIsTriggerOpening(false);
|
|
140
272
|
onAnimationCompleteRef.current?.();
|
|
141
273
|
}, []);
|
|
142
274
|
useAnimatedReaction(() => scale.get(), (currentScale, previousScale) => {
|
|
143
|
-
if (currentScale !== previousScale) {
|
|
275
|
+
if (currentScale !== previousScale && hasZoomChangeListeners.get()) {
|
|
144
276
|
scheduleOnRN(emitZoomChange, currentScale, previousScale);
|
|
145
277
|
}
|
|
146
|
-
|
|
278
|
+
const currentIsZoomed = currentScale > 1;
|
|
279
|
+
const previousIsZoomed = (previousScale ?? 1) > 1;
|
|
280
|
+
if (previousScale === null || currentIsZoomed !== previousIsZoomed) {
|
|
281
|
+
scheduleOnRN(setIsZoomed, currentIsZoomed);
|
|
282
|
+
}
|
|
147
283
|
});
|
|
148
284
|
useAnimatedReaction(() => rotation.get(), (currentRotation, previousRotation) => {
|
|
149
|
-
if (currentRotation !== previousRotation) {
|
|
285
|
+
if (currentRotation !== previousRotation && hasRotationChangeListeners.get()) {
|
|
150
286
|
scheduleOnRN(emitRotationChange, currentRotation, previousRotation);
|
|
151
287
|
}
|
|
152
|
-
|
|
288
|
+
const currentIsRotated = currentRotation % 360 !== 0;
|
|
289
|
+
const previousIsRotated = (previousRotation ?? 0) % 360 !== 0;
|
|
290
|
+
if (previousRotation === null || currentIsRotated !== previousIsRotated) {
|
|
291
|
+
scheduleOnRN(setIsRotated, currentIsRotated);
|
|
292
|
+
}
|
|
153
293
|
});
|
|
154
294
|
useEffect(() => {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
setCurrentIndex(state.currentIndex);
|
|
161
|
-
});
|
|
295
|
+
return registry.subscribeToActiveTrigger(id, node => {
|
|
296
|
+
setActiveTriggerNode(node);
|
|
297
|
+
if (node) {
|
|
298
|
+
setIsTriggerOpening(true);
|
|
299
|
+
}
|
|
162
300
|
});
|
|
163
|
-
return () => {
|
|
164
|
-
unsubscribeFromRegistry();
|
|
165
|
-
unsubscribe?.();
|
|
166
|
-
};
|
|
167
301
|
}, [id]);
|
|
168
302
|
useEffect(() => {
|
|
169
|
-
|
|
170
|
-
}, [
|
|
303
|
+
dataRef.current = data;
|
|
304
|
+
}, [data]);
|
|
171
305
|
useEffect(() => {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
manager.setDataLength(dataLength);
|
|
177
|
-
manager.setEnableHorizontalSwipe(enableHorizontalSwipe);
|
|
178
|
-
manager.setCurrentIndex(initialIndex);
|
|
179
|
-
manager.setWidth(width + itemSpacing);
|
|
180
|
-
manager.setHeight(height);
|
|
181
|
-
manager.setZoomSharedValues(scale, translateX, translateY, maxZoomScale);
|
|
182
|
-
manager.setResetTransformCallback(resetTransformState);
|
|
183
|
-
manager.setRotation(rotation);
|
|
184
|
-
manager.setEnableLoop(enableLoop);
|
|
185
|
-
manager.notifyStateChange();
|
|
186
|
-
}, [dataLength, enableHorizontalSwipe, initialIndex, manager, width, itemSpacing, maxZoomScale, enableLoop, scale, height, resetTransformState, translateX, translateY, rotation]);
|
|
306
|
+
isZoomedRef.current = isZoomed;
|
|
307
|
+
}, [isZoomed]);
|
|
187
308
|
useEffect(() => {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
manager.setListRef(listRef.current);
|
|
192
|
-
}, [manager]);
|
|
309
|
+
isRotatedRef.current = isRotated;
|
|
310
|
+
}, [isRotated]);
|
|
193
311
|
useEffect(() => {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
scale.set(1);
|
|
200
|
-
backdropOpacity.set(1);
|
|
201
|
-
startScale.set(1);
|
|
202
|
-
rotation.set(0);
|
|
203
|
-
if (!hasValidInitialIndex || !hasDataLengthChanged && adjustedInitialIndex <= 0 || !listRef.current) {
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
return scheduleInitialScroll(() => {
|
|
207
|
-
scrollTo(adjustedInitialIndex, false);
|
|
208
|
-
});
|
|
209
|
-
}, [adjustedInitialIndex, dataLength, initialIndex, translateY, backdropOpacity, translateX, scale, startScale, rotation, scrollTo]);
|
|
312
|
+
isPinchingRef.current = isPinching;
|
|
313
|
+
}, [isPinching]);
|
|
314
|
+
useEffect(() => {
|
|
315
|
+
onSingleTapRef.current = onSingleTap;
|
|
316
|
+
}, [onSingleTap]);
|
|
210
317
|
useEffect(() => {
|
|
211
318
|
onAnimationCompleteRef.current = triggerAnimation?.onAnimationComplete;
|
|
212
319
|
}, [triggerAnimation?.onAnimationComplete]);
|
|
320
|
+
useLayoutEffect(() => {
|
|
321
|
+
const initialIndexChanged = previousInitialIndexRef.current !== initialIndex;
|
|
322
|
+
const shouldResolveInitialIndex = initialIndexChanged || !hasResolvedInitialIndexRef.current && dataLength > 0;
|
|
323
|
+
previousInitialIndexRef.current = initialIndex;
|
|
324
|
+
dataLengthRef.current = dataLength;
|
|
325
|
+
enableLoopRef.current = enableLoop;
|
|
326
|
+
hasResolvedInitialIndexRef.current = dataLength > 0;
|
|
327
|
+
const nextIndex = shouldResolveInitialIndex ? clampIndex(initialIndex, dataLength) : clampIndex(currentIndexRef.current, dataLength);
|
|
328
|
+
const nextVirtualIndex = getVirtualIndexForLogicalIndex(nextIndex, centerVirtualIndexRef.current, dataLength, enableLoop) ?? nextIndex;
|
|
329
|
+
const previousIndex = currentIndexRef.current;
|
|
330
|
+
const previousVirtualIndex = centerVirtualIndexRef.current;
|
|
331
|
+
const shouldSyncVirtualPage = isPageTransitioningRef.current || previousVirtualIndex !== nextVirtualIndex;
|
|
332
|
+
const shouldResetTransform = initialIndexChanged || previousIndex !== nextIndex || shouldSyncVirtualPage;
|
|
333
|
+
if (shouldSyncVirtualPage) {
|
|
334
|
+
snapToVirtualPage(nextVirtualIndex);
|
|
335
|
+
if (dataLength === 0) {
|
|
336
|
+
centerVirtualIndexRef.current = nextVirtualIndex;
|
|
337
|
+
setCenterVirtualIndex(nextVirtualIndex);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
const shouldCommitIndexOutsidePaging = !shouldSyncVirtualPage || dataLength === 0;
|
|
341
|
+
if (shouldCommitIndexOutsidePaging && previousIndex !== nextIndex) {
|
|
342
|
+
commitCurrentIndex(nextIndex);
|
|
343
|
+
}
|
|
344
|
+
if (shouldCommitIndexOutsidePaging && previousIndex === nextIndex) {
|
|
345
|
+
managerRef.current?.notifyStateChange();
|
|
346
|
+
}
|
|
347
|
+
if (shouldResetTransform) {
|
|
348
|
+
resetTransformState();
|
|
349
|
+
}
|
|
350
|
+
}, [commitCurrentIndex, dataLength, enableLoop, initialIndex, isPageTransitioningRef, resetTransformState, snapToVirtualPage]);
|
|
213
351
|
useEffect(() => {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}, [
|
|
352
|
+
return () => {
|
|
353
|
+
triggerRectRef.current = null;
|
|
354
|
+
clearPendingWebSingleTap();
|
|
355
|
+
};
|
|
356
|
+
}, [clearPendingWebSingleTap]);
|
|
219
357
|
useEffect(() => {
|
|
220
|
-
|
|
221
|
-
|
|
358
|
+
if (!autoPlay || dataLength <= 1 || isTriggerOpening || isZoomed || isRotated || isPinching || !enableLoop && currentIndex >= dataLength - 1) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
const interval = Math.max(250, Math.floor(autoPlayInterval || 0));
|
|
362
|
+
if (!Number.isFinite(interval)) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const timer = setInterval(() => {
|
|
366
|
+
if (isPageTransitioningRef.current || isZoomedRef.current || isRotatedRef.current || isPinchingRef.current) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
const nextIndex = currentIndexRef.current >= dataLengthRef.current - 1 ? enableLoopRef.current ? 0 : null : currentIndexRef.current + 1;
|
|
370
|
+
if (nextIndex === null) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
navigateToIndex(nextIndex);
|
|
374
|
+
}, interval);
|
|
375
|
+
return () => clearInterval(timer);
|
|
376
|
+
}, [autoPlay, autoPlayInterval, currentIndex, dataLength, enableLoop, isPinching, isRotated, isTriggerOpening, isZoomed, isPageTransitioningRef, navigateToIndex]);
|
|
222
377
|
useEffect(() => {
|
|
223
378
|
if (shouldStartTriggerAnimation && triggerRectRef.current) {
|
|
224
379
|
const startX = triggerRectRef.current.x + triggerRectRef.current.width / 2 - width / 2;
|
|
@@ -230,7 +385,7 @@ export const useGestureViewer = ({
|
|
|
230
385
|
triggerOpacity.set(0);
|
|
231
386
|
triggerScale.set(withTiming(1, animationConfig, finished => {
|
|
232
387
|
if (finished) {
|
|
233
|
-
scheduleOnRN(
|
|
388
|
+
scheduleOnRN(finishTriggerOpening);
|
|
234
389
|
}
|
|
235
390
|
}));
|
|
236
391
|
triggerTranslateX.set(withTiming(0, animationConfig));
|
|
@@ -242,9 +397,15 @@ export const useGestureViewer = ({
|
|
|
242
397
|
}));
|
|
243
398
|
setShouldStartTriggerAnimation(false);
|
|
244
399
|
}
|
|
245
|
-
}, [shouldStartTriggerAnimation, animationConfig, width, height, triggerOpacity, triggerScale, triggerTranslateX, triggerTranslateY,
|
|
400
|
+
}, [shouldStartTriggerAnimation, animationConfig, width, height, triggerOpacity, triggerScale, triggerTranslateX, triggerTranslateY, finishTriggerOpening]);
|
|
246
401
|
useEffect(() => {
|
|
247
|
-
if (!activeTriggerNode
|
|
402
|
+
if (!activeTriggerNode) {
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
if (typeof activeTriggerNode.measure !== 'function') {
|
|
406
|
+
setIsTriggerOpening(false);
|
|
407
|
+
triggerOpacity.set(1);
|
|
408
|
+
registry.clearActiveTriggerNode(id);
|
|
248
409
|
return;
|
|
249
410
|
}
|
|
250
411
|
activeTriggerNode.measure((_x, _y, measuredWidth, measuredHeight, pageX, pageY) => {
|
|
@@ -255,6 +416,8 @@ export const useGestureViewer = ({
|
|
|
255
416
|
y: pageY
|
|
256
417
|
};
|
|
257
418
|
if (!isValidTriggerRect(nextTriggerRect)) {
|
|
419
|
+
setIsTriggerOpening(false);
|
|
420
|
+
triggerOpacity.set(1);
|
|
258
421
|
registry.clearActiveTriggerNode(id);
|
|
259
422
|
return;
|
|
260
423
|
}
|
|
@@ -263,12 +426,7 @@ export const useGestureViewer = ({
|
|
|
263
426
|
setShouldStartTriggerAnimation(true);
|
|
264
427
|
registry.clearActiveTriggerNode(id);
|
|
265
428
|
});
|
|
266
|
-
}, [activeTriggerNode, id,
|
|
267
|
-
useEffect(() => {
|
|
268
|
-
return () => {
|
|
269
|
-
triggerRectRef.current = null;
|
|
270
|
-
};
|
|
271
|
-
}, []);
|
|
429
|
+
}, [activeTriggerNode, id, triggerOpacity]);
|
|
272
430
|
const animateDismissToRect = useCallback(rect => {
|
|
273
431
|
const endX = rect.x + rect.width / 2 - width / 2;
|
|
274
432
|
const endY = rect.y + rect.height / 2 - height / 2;
|
|
@@ -288,6 +446,9 @@ export const useGestureViewer = ({
|
|
|
288
446
|
}
|
|
289
447
|
}, [onDismiss]);
|
|
290
448
|
const handleDismiss = useCallback(() => {
|
|
449
|
+
if (isPageTransitioningRef.current) {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
291
452
|
onDismissStart?.();
|
|
292
453
|
const dismissTargetIndex = pendingIndexRef.current;
|
|
293
454
|
const indexedTriggerNode = registry.getIndexedTriggerNode(id, dismissTargetIndex);
|
|
@@ -316,30 +477,98 @@ export const useGestureViewer = ({
|
|
|
316
477
|
return;
|
|
317
478
|
}
|
|
318
479
|
dismissWithoutTrigger();
|
|
319
|
-
}, [animateDismissToRect, dismissWithoutTrigger, id,
|
|
480
|
+
}, [animateDismissToRect, dismissWithoutTrigger, id, isPageTransitioningRef, onDismissStart]);
|
|
320
481
|
const dismissGesture = useMemo(() => {
|
|
321
|
-
const canDismiss = !isZoomed && dismissOptions.enabled;
|
|
482
|
+
const canDismiss = !isTriggerOpening && !isZoomed && dismissOptions.enabled;
|
|
322
483
|
return Gesture.Pan().minDistance(10).averageTouches(true).activeCursor('grabbing').activeOffsetY([-10, 10]).failOffsetX([-10, 10]).withRef(dismissGestureRef).enabled(canDismiss).onUpdate(event => {
|
|
484
|
+
if (pageTransitionLocked.get()) {
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
323
487
|
translateY.set(event.translationY / dismissOptions.resistance);
|
|
324
488
|
}).onEnd(event => {
|
|
489
|
+
if (pageTransitionLocked.get()) {
|
|
490
|
+
translateY.set(withSpring(0, PAGE_SPRING_CONFIG));
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
325
493
|
if (canDismiss && shouldDismissByDirection(event.translationY, dismissOptions.threshold, dismissOptions.direction)) {
|
|
326
494
|
scheduleOnRN(handleDismiss);
|
|
327
495
|
return;
|
|
328
496
|
}
|
|
329
|
-
translateY.set(withSpring(0,
|
|
330
|
-
damping: 50,
|
|
331
|
-
energyThreshold: 6e-9,
|
|
332
|
-
mass: 4,
|
|
333
|
-
overshootClamping: false,
|
|
334
|
-
stiffness: 600
|
|
335
|
-
}));
|
|
497
|
+
translateY.set(withSpring(0, PAGE_SPRING_CONFIG));
|
|
336
498
|
});
|
|
337
|
-
}, [translateY, dismissOptions, handleDismiss, isZoomed]);
|
|
499
|
+
}, [translateY, dismissOptions, handleDismiss, isTriggerOpening, isZoomed, pageTransitionLocked]);
|
|
500
|
+
const getCurrentTapTarget = useCallback(() => {
|
|
501
|
+
const index = pendingIndexRef.current;
|
|
502
|
+
if (index < 0 || index >= dataRef.current.length || !(index in dataRef.current)) {
|
|
503
|
+
return null;
|
|
504
|
+
}
|
|
505
|
+
return {
|
|
506
|
+
index,
|
|
507
|
+
item: dataRef.current[index]
|
|
508
|
+
};
|
|
509
|
+
}, []);
|
|
510
|
+
const isWebInteractionLocked = useCallback(() => isPageTransitioningRef.current || pageTransitionLocked.get(), [isPageTransitioningRef, pageTransitionLocked]);
|
|
511
|
+
const emitSingleTap = useCallback((x, y, tapTarget) => {
|
|
512
|
+
if (isPageTransitioningRef.current) {
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
const target = tapTarget ?? getCurrentTapTarget();
|
|
516
|
+
if (!target) {
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
const {
|
|
520
|
+
index: targetIndex,
|
|
521
|
+
item: targetItem
|
|
522
|
+
} = target;
|
|
523
|
+
if (targetIndex < 0) {
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
managerRef.current?.emitTap({
|
|
527
|
+
kind: 'single',
|
|
528
|
+
x,
|
|
529
|
+
y,
|
|
530
|
+
index: targetIndex
|
|
531
|
+
});
|
|
532
|
+
onSingleTapRef.current?.({
|
|
533
|
+
x,
|
|
534
|
+
y,
|
|
535
|
+
index: targetIndex,
|
|
536
|
+
item: targetItem
|
|
537
|
+
});
|
|
538
|
+
}, [getCurrentTapTarget, isPageTransitioningRef]);
|
|
539
|
+
const singleTapGesture = useMemo(() => Gesture.Tap().enabled(enableNativeTapGestures).numberOfTaps(1).onEnd((event, success) => {
|
|
540
|
+
if (!success || pageTransitionLocked.get()) {
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
scheduleOnRN(emitSingleTap, event.x, event.y);
|
|
544
|
+
}), [emitSingleTap, pageTransitionLocked]);
|
|
545
|
+
const doubleTapGesture = useMemo(() => Gesture.Tap().enabled(enableDoubleTapZoom && enableNativeTapGestures).numberOfTaps(2).onEnd(event => {
|
|
546
|
+
if (pageTransitionLocked.get()) {
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
applyTapZoomAtPoint({
|
|
550
|
+
x: event.x,
|
|
551
|
+
y: event.y,
|
|
552
|
+
width,
|
|
553
|
+
height,
|
|
554
|
+
maxZoomScale,
|
|
555
|
+
scale,
|
|
556
|
+
translateX,
|
|
557
|
+
translateY
|
|
558
|
+
});
|
|
559
|
+
}), [enableDoubleTapZoom, height, maxZoomScale, pageTransitionLocked, scale, translateX, translateY, width]);
|
|
560
|
+
const tapGesture = useMemo(() => Gesture.Exclusive(doubleTapGesture, singleTapGesture), [doubleTapGesture, singleTapGesture]);
|
|
338
561
|
const zoomPinchGesture = useMemo(() => Gesture.Pinch().enabled(enablePinchZoom).onTouchesDown(event => {
|
|
562
|
+
if (pageTransitionLocked.get()) {
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
339
565
|
if (event.numberOfTouches === 2) {
|
|
340
566
|
scheduleOnRN(setIsPinching, true);
|
|
341
567
|
}
|
|
342
568
|
}).onStart(event => {
|
|
569
|
+
if (pageTransitionLocked.get()) {
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
343
572
|
startScale.set(scale.get());
|
|
344
573
|
initialTranslateX.set(translateX.get());
|
|
345
574
|
initialTranslateY.set(translateY.get());
|
|
@@ -349,6 +578,9 @@ export const useGestureViewer = ({
|
|
|
349
578
|
lastFocalY.set(event.focalY);
|
|
350
579
|
hasActiveFocal.set(false);
|
|
351
580
|
}).onUpdate(event => {
|
|
581
|
+
if (pageTransitionLocked.get()) {
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
352
584
|
const initialScale = startScale.get();
|
|
353
585
|
const newScale = initialScale * event.scale;
|
|
354
586
|
scale.set(newScale);
|
|
@@ -402,6 +634,9 @@ export const useGestureViewer = ({
|
|
|
402
634
|
translateX.set(constrainedTranslateX);
|
|
403
635
|
translateY.set(constrainedTranslateY);
|
|
404
636
|
}).onEnd(() => {
|
|
637
|
+
if (pageTransitionLocked.get()) {
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
405
640
|
const currentScale = scale.get();
|
|
406
641
|
if (currentScale > maxZoomScale) {
|
|
407
642
|
scale.set(withTiming(maxZoomScale, {
|
|
@@ -447,11 +682,17 @@ export const useGestureViewer = ({
|
|
|
447
682
|
}).onFinalize(() => {
|
|
448
683
|
hasActiveFocal.set(false);
|
|
449
684
|
scheduleOnRN(setIsPinching, false);
|
|
450
|
-
}), [scale, enablePinchZoom, maxZoomScale, translateX, translateY, startScale, initialTranslateX, initialTranslateY, width, height, constrainTranslation, lastFocalX, lastFocalY, startFocalX, startFocalY, hasActiveFocal]);
|
|
685
|
+
}), [scale, enablePinchZoom, maxZoomScale, translateX, translateY, startScale, initialTranslateX, initialTranslateY, width, height, constrainTranslation, lastFocalX, lastFocalY, startFocalX, startFocalY, hasActiveFocal, pageTransitionLocked]);
|
|
451
686
|
const zoomPanGesture = useMemo(() => Gesture.Pan().enabled(enablePanWhenZoomed && isZoomed).activeCursor('grabbing').averageTouches(true).onBegin(() => {
|
|
687
|
+
if (pageTransitionLocked.get()) {
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
452
690
|
initialTranslateX.set(translateX.get());
|
|
453
691
|
initialTranslateY.set(translateY.get());
|
|
454
692
|
}).onUpdate(event => {
|
|
693
|
+
if (pageTransitionLocked.get()) {
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
455
696
|
const currentScale = scale.get();
|
|
456
697
|
if (currentScale > 1) {
|
|
457
698
|
const newTranslateX = initialTranslateX.get() + event.translationX;
|
|
@@ -467,50 +708,22 @@ export const useGestureViewer = ({
|
|
|
467
708
|
translateX.set(constrainedTranslateX);
|
|
468
709
|
translateY.set(constrainedTranslateY);
|
|
469
710
|
}
|
|
470
|
-
}), [translateX, translateY, enablePanWhenZoomed, isZoomed, scale, initialTranslateX, initialTranslateY, constrainTranslation]);
|
|
471
|
-
const emitSingleTap = useCallback((x, y) => {
|
|
472
|
-
const index = pendingIndexRef.current;
|
|
473
|
-
const currentData = dataRef.current;
|
|
474
|
-
if (index < 0 || index >= currentData.length) {
|
|
475
|
-
return;
|
|
476
|
-
}
|
|
477
|
-
managerRef.current?.emitTap({
|
|
478
|
-
kind: 'single',
|
|
479
|
-
x,
|
|
480
|
-
y,
|
|
481
|
-
index
|
|
482
|
-
});
|
|
483
|
-
const item = currentData[index];
|
|
484
|
-
if (item === undefined) {
|
|
485
|
-
return;
|
|
486
|
-
}
|
|
487
|
-
onSingleTapRef.current?.({
|
|
488
|
-
x,
|
|
489
|
-
y,
|
|
490
|
-
index,
|
|
491
|
-
item
|
|
492
|
-
});
|
|
493
|
-
}, []);
|
|
494
|
-
const singleTapGesture = useMemo(() => Gesture.Tap().enabled(Platform.OS !== 'web').numberOfTaps(1).onEnd((event, success) => {
|
|
495
|
-
if (!success) {
|
|
496
|
-
return;
|
|
497
|
-
}
|
|
498
|
-
scheduleOnRN(emitSingleTap, event.x, event.y);
|
|
499
|
-
}), [emitSingleTap]);
|
|
500
|
-
const doubleTapGesture = useMemo(() => Gesture.Tap().enabled(enableDoubleTapZoom && Platform.OS !== 'web').numberOfTaps(2).onEnd(event => {
|
|
501
|
-
applyTapZoomAtPoint({
|
|
502
|
-
x: event.x,
|
|
503
|
-
y: event.y,
|
|
504
|
-
width,
|
|
505
|
-
height,
|
|
506
|
-
maxZoomScale,
|
|
507
|
-
scale,
|
|
508
|
-
translateX,
|
|
509
|
-
translateY
|
|
510
|
-
});
|
|
511
|
-
}), [enableDoubleTapZoom, height, maxZoomScale, scale, translateX, translateY, width]);
|
|
512
|
-
const tapGesture = useMemo(() => Gesture.Exclusive(doubleTapGesture, singleTapGesture), [doubleTapGesture, singleTapGesture]);
|
|
711
|
+
}), [translateX, translateY, enablePanWhenZoomed, isZoomed, scale, initialTranslateX, initialTranslateY, constrainTranslation, pageTransitionLocked]);
|
|
513
712
|
const zoomGesture = useMemo(() => Gesture.Race(zoomPinchGesture, Gesture.Exclusive(zoomPanGesture, tapGesture)), [zoomPinchGesture, zoomPanGesture, tapGesture]);
|
|
713
|
+
const onWebClick = useWebClickHandler({
|
|
714
|
+
clearPendingWebSingleTap,
|
|
715
|
+
emitSingleTap,
|
|
716
|
+
enableDoubleTapZoom,
|
|
717
|
+
getCurrentTapTarget,
|
|
718
|
+
height,
|
|
719
|
+
maxZoomScale,
|
|
720
|
+
scale,
|
|
721
|
+
scheduleWebSingleTap,
|
|
722
|
+
isInteractionLocked: isWebInteractionLocked,
|
|
723
|
+
translateX,
|
|
724
|
+
translateY,
|
|
725
|
+
width
|
|
726
|
+
});
|
|
514
727
|
const animatedStyle = useAnimatedStyle(() => ({
|
|
515
728
|
opacity: triggerOpacity.get(),
|
|
516
729
|
transform: [{
|
|
@@ -542,55 +755,22 @@ export const useGestureViewer = ({
|
|
|
542
755
|
opacity: baseOpacity * dismissOpacity
|
|
543
756
|
};
|
|
544
757
|
}, [dismissOptions.direction, dismissOptions.fadeBackdrop]);
|
|
545
|
-
const nativeScrollGesture = useMemo(() => {
|
|
546
|
-
return Gesture.Native().requireExternalGestureToFail(dismissGestureRef);
|
|
547
|
-
}, []);
|
|
548
|
-
const {
|
|
549
|
-
activeListIndex,
|
|
550
|
-
onMomentumScrollEnd,
|
|
551
|
-
onScroll,
|
|
552
|
-
onScrollBeginDrag,
|
|
553
|
-
onWebClick
|
|
554
|
-
} = useGestureViewerPaging({
|
|
555
|
-
adjustedInitialIndex,
|
|
556
|
-
autoPlay,
|
|
557
|
-
autoPlayInterval,
|
|
558
|
-
currentIndex,
|
|
559
|
-
dataLength,
|
|
560
|
-
enableDoubleTapZoom,
|
|
561
|
-
enableHorizontalSwipe,
|
|
562
|
-
enableLoop,
|
|
563
|
-
height,
|
|
564
|
-
isRotated,
|
|
565
|
-
isZoomed,
|
|
566
|
-
itemSpacing,
|
|
567
|
-
manager,
|
|
568
|
-
maxZoomScale,
|
|
569
|
-
onSingleTap: emitSingleTap,
|
|
570
|
-
scale,
|
|
571
|
-
scrollTo,
|
|
572
|
-
syncCurrentIndex,
|
|
573
|
-
syncPendingIndex,
|
|
574
|
-
translateX,
|
|
575
|
-
translateY,
|
|
576
|
-
width
|
|
577
|
-
});
|
|
578
758
|
return {
|
|
579
|
-
activeListIndex,
|
|
580
759
|
animatedStyle,
|
|
581
760
|
backdropStyle,
|
|
761
|
+
centerVirtualIndex: renderCenterVirtualIndex,
|
|
762
|
+
currentIndex: renderCurrentIndex,
|
|
582
763
|
dataLength,
|
|
583
764
|
dismissGesture,
|
|
584
765
|
handleDismiss,
|
|
766
|
+
horizontalPagingGesture,
|
|
585
767
|
isPinching,
|
|
586
768
|
isRotated,
|
|
587
769
|
isZoomed,
|
|
588
|
-
listRef,
|
|
589
|
-
nativeScrollGesture,
|
|
590
770
|
onWebClick,
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
771
|
+
pageStride,
|
|
772
|
+
renderWindowSlots,
|
|
773
|
+
visualPage,
|
|
594
774
|
zoomGesture
|
|
595
775
|
};
|
|
596
776
|
};
|