react-native-gesture-image-viewer 2.4.0 → 3.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/lib/module/GestureViewer.js +96 -147
- package/lib/module/GestureViewer.js.map +1 -1
- package/lib/module/GestureViewerManager.js +49 -105
- package/lib/module/GestureViewerManager.js.map +1 -1
- package/lib/module/gestureViewerAnimation.js +18 -0
- package/lib/module/gestureViewerAnimation.js.map +1 -0
- package/lib/module/gestureViewerGestures.js +12 -0
- package/lib/module/gestureViewerGestures.js.map +1 -0
- package/lib/module/gestureViewerPaging.js +66 -0
- package/lib/module/gestureViewerPaging.js.map +1 -0
- package/lib/module/gestureViewerRenderWindow.js +29 -0
- package/lib/module/gestureViewerRenderWindow.js.map +1 -0
- package/lib/module/getWebContentProps.js +6 -0
- package/lib/module/getWebContentProps.js.map +1 -0
- package/lib/module/getWebContentProps.web.js +8 -0
- package/lib/module/getWebContentProps.web.js.map +1 -0
- package/lib/module/platformTapGestures.js +4 -0
- package/lib/module/platformTapGestures.js.map +1 -0
- package/lib/module/platformTapGestures.web.js +4 -0
- package/lib/module/platformTapGestures.web.js.map +1 -0
- package/lib/module/renderWindow.js +160 -0
- package/lib/module/renderWindow.js.map +1 -0
- package/lib/module/useGestureViewer.js +459 -211
- package/lib/module/useGestureViewer.js.map +1 -1
- package/lib/module/useGestureViewerController.js +3 -3
- package/lib/module/useGestureViewerController.js.map +1 -1
- package/lib/module/useGestureViewerManagerBridge.js +85 -0
- package/lib/module/useGestureViewerManagerBridge.js.map +1 -0
- package/lib/module/useGestureViewerPaging.js +168 -79
- package/lib/module/useGestureViewerPaging.js.map +1 -1
- package/lib/module/useWebClickHandler.js +7 -0
- package/lib/module/useWebClickHandler.js.map +1 -0
- package/lib/module/useWebClickHandler.web.js +85 -0
- package/lib/module/useWebClickHandler.web.js.map +1 -0
- package/lib/module/useWebSingleTapTimer.js +12 -0
- package/lib/module/useWebSingleTapTimer.js.map +1 -0
- package/lib/module/useWebSingleTapTimer.web.js +25 -0
- package/lib/module/useWebSingleTapTimer.web.js.map +1 -0
- package/lib/module/utils/index.js +0 -77
- package/lib/module/utils/index.js.map +1 -1
- package/lib/module/utils/tapZoom.js +4 -3
- package/lib/module/utils/tapZoom.js.map +1 -1
- package/lib/typescript/src/GestureViewer.d.ts +1 -1
- package/lib/typescript/src/GestureViewer.d.ts.map +1 -1
- package/lib/typescript/src/GestureViewerManager.d.ts +20 -20
- package/lib/typescript/src/GestureViewerManager.d.ts.map +1 -1
- package/lib/typescript/src/gestureViewerAnimation.d.ts +15 -0
- package/lib/typescript/src/gestureViewerAnimation.d.ts.map +1 -0
- package/lib/typescript/src/gestureViewerGestures.d.ts +10 -0
- package/lib/typescript/src/gestureViewerGestures.d.ts.map +1 -0
- package/lib/typescript/src/gestureViewerPaging.d.ts +13 -0
- package/lib/typescript/src/gestureViewerPaging.d.ts.map +1 -0
- package/lib/typescript/src/gestureViewerRenderWindow.d.ts +15 -0
- package/lib/typescript/src/gestureViewerRenderWindow.d.ts.map +1 -0
- package/lib/typescript/src/getWebContentProps.d.ts +3 -0
- package/lib/typescript/src/getWebContentProps.d.ts.map +1 -0
- package/lib/typescript/src/getWebContentProps.web.d.ts +5 -0
- package/lib/typescript/src/getWebContentProps.web.d.ts.map +1 -0
- package/lib/typescript/src/platformTapGestures.d.ts +2 -0
- package/lib/typescript/src/platformTapGestures.d.ts.map +1 -0
- package/lib/typescript/src/platformTapGestures.web.d.ts +2 -0
- package/lib/typescript/src/platformTapGestures.web.d.ts.map +1 -0
- package/lib/typescript/src/renderWindow.d.ts +44 -0
- package/lib/typescript/src/renderWindow.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +36 -53
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useGestureViewer.d.ts +11 -10
- package/lib/typescript/src/useGestureViewer.d.ts.map +1 -1
- package/lib/typescript/src/useGestureViewerController.d.ts +1 -1
- package/lib/typescript/src/useGestureViewerManagerBridge.d.ts +28 -0
- package/lib/typescript/src/useGestureViewerManagerBridge.d.ts.map +1 -0
- package/lib/typescript/src/useGestureViewerPaging.d.ts +33 -2
- package/lib/typescript/src/useGestureViewerPaging.d.ts.map +1 -1
- package/lib/typescript/src/useWebClickHandler.d.ts +35 -0
- package/lib/typescript/src/useWebClickHandler.d.ts.map +1 -0
- package/lib/typescript/src/useWebClickHandler.web.d.ts +36 -0
- package/lib/typescript/src/useWebClickHandler.web.d.ts.map +1 -0
- package/lib/typescript/src/useWebSingleTapTimer.d.ts +6 -0
- package/lib/typescript/src/useWebSingleTapTimer.d.ts.map +1 -0
- package/lib/typescript/src/useWebSingleTapTimer.web.d.ts +6 -0
- package/lib/typescript/src/useWebSingleTapTimer.web.d.ts.map +1 -0
- package/lib/typescript/src/utils/index.d.ts +0 -20
- package/lib/typescript/src/utils/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/tapZoom.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/GestureViewer.tsx +113 -208
- package/src/GestureViewerManager.ts +77 -122
- package/src/gestureViewerAnimation.ts +18 -0
- package/src/gestureViewerGestures.ts +20 -0
- package/src/gestureViewerPaging.ts +116 -0
- package/src/gestureViewerRenderWindow.ts +56 -0
- package/src/getWebContentProps.ts +5 -0
- package/src/getWebContentProps.web.ts +5 -0
- package/src/platformTapGestures.ts +1 -0
- package/src/platformTapGestures.web.ts +1 -0
- package/src/renderWindow.ts +253 -0
- package/src/types.ts +34 -100
- package/src/useGestureViewer.ts +665 -275
- package/src/useGestureViewerController.ts +3 -3
- package/src/useGestureViewerManagerBridge.ts +118 -0
- package/src/useGestureViewerPaging.ts +306 -112
- package/src/useWebClickHandler.ts +44 -0
- package/src/useWebClickHandler.web.ts +146 -0
- package/src/useWebSingleTapTimer.ts +10 -0
- package/src/useWebSingleTapTimer.web.ts +30 -0
- package/src/utils/index.ts +0 -102
- package/src/utils/tapZoom.ts +4 -3
- package/lib/module/WebPagingFixStyle.js +0 -25
- package/lib/module/WebPagingFixStyle.js.map +0 -1
- package/lib/module/scheduleInitialScroll.js +0 -21
- package/lib/module/scheduleInitialScroll.js.map +0 -1
- package/lib/module/useGestureViewerPaging.types.js +0 -4
- package/lib/module/useGestureViewerPaging.types.js.map +0 -1
- package/lib/module/useGestureViewerPaging.web.js +0 -291
- package/lib/module/useGestureViewerPaging.web.js.map +0 -1
- package/lib/module/utils/FlashList.js +0 -9
- package/lib/module/utils/FlashList.js.map +0 -1
- package/lib/module/utils/webScroll.js +0 -70
- package/lib/module/utils/webScroll.js.map +0 -1
- package/lib/typescript/src/WebPagingFixStyle.d.ts +0 -6
- package/lib/typescript/src/WebPagingFixStyle.d.ts.map +0 -1
- package/lib/typescript/src/scheduleInitialScroll.d.ts +0 -3
- package/lib/typescript/src/scheduleInitialScroll.d.ts.map +0 -1
- package/lib/typescript/src/useGestureViewerPaging.types.d.ts +0 -42
- package/lib/typescript/src/useGestureViewerPaging.types.d.ts.map +0 -1
- package/lib/typescript/src/useGestureViewerPaging.web.d.ts +0 -3
- package/lib/typescript/src/useGestureViewerPaging.web.d.ts.map +0 -1
- package/lib/typescript/src/utils/FlashList.d.ts +0 -2
- package/lib/typescript/src/utils/FlashList.d.ts.map +0 -1
- package/lib/typescript/src/utils/webScroll.d.ts +0 -18
- package/lib/typescript/src/utils/webScroll.d.ts.map +0 -1
- package/src/WebPagingFixStyle.tsx +0 -25
- package/src/scheduleInitialScroll.ts +0 -36
- package/src/useGestureViewerPaging.types.ts +0 -42
- package/src/useGestureViewerPaging.web.ts +0 -453
- package/src/utils/FlashList.ts +0 -7
- package/src/utils/webScroll.ts +0 -96
package/src/GestureViewer.tsx
CHANGED
|
@@ -1,145 +1,117 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} from 'react-native';
|
|
9
|
-
|
|
10
|
-
import
|
|
11
|
-
|
|
1
|
+
import { useEffect, useMemo } from 'react';
|
|
2
|
+
import { StyleSheet, useWindowDimensions, View, type ViewStyle } from 'react-native';
|
|
3
|
+
import { GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
4
|
+
import Animated, {
|
|
5
|
+
type AnimatedStyle,
|
|
6
|
+
type SharedValue,
|
|
7
|
+
useAnimatedStyle,
|
|
8
|
+
} from 'react-native-reanimated';
|
|
9
|
+
|
|
10
|
+
import { composeGestureViewerGestures } from './gestureViewerGestures';
|
|
12
11
|
import { registry } from './GestureViewerRegistry';
|
|
12
|
+
import { getWebContentProps } from './getWebContentProps';
|
|
13
|
+
import type { RenderWindowSlot } from './renderWindow';
|
|
13
14
|
import type { GestureViewerProps } from './types';
|
|
14
15
|
import { useGestureViewer } from './useGestureViewer';
|
|
15
|
-
import {
|
|
16
|
-
createLoopData,
|
|
17
|
-
isFlashListLike,
|
|
18
|
-
isFlatListLike,
|
|
19
|
-
isScrollViewLike,
|
|
20
|
-
shouldUseNativeScrollGesture,
|
|
21
|
-
} from './utils';
|
|
22
|
-
import WebPagingFixStyle from './WebPagingFixStyle';
|
|
23
16
|
|
|
24
|
-
|
|
17
|
+
type RenderWindowSlotViewProps<ItemT> = {
|
|
18
|
+
animatedStyle: AnimatedStyle<ViewStyle>;
|
|
19
|
+
height: number;
|
|
20
|
+
isActive: boolean;
|
|
21
|
+
pageStride: number;
|
|
22
|
+
renderItem: GestureViewerProps<ItemT>['renderItem'];
|
|
23
|
+
slot: RenderWindowSlot<ItemT>;
|
|
24
|
+
visualPage: SharedValue<number>;
|
|
25
|
+
width: number;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function RenderWindowSlotView<ItemT>({
|
|
29
|
+
animatedStyle,
|
|
30
|
+
height,
|
|
31
|
+
isActive,
|
|
32
|
+
pageStride,
|
|
33
|
+
renderItem,
|
|
34
|
+
slot,
|
|
35
|
+
visualPage,
|
|
36
|
+
width,
|
|
37
|
+
}: RenderWindowSlotViewProps<ItemT>) {
|
|
38
|
+
const slotAnimatedStyle = useAnimatedStyle(() => ({
|
|
39
|
+
transform: [{ translateX: (slot.virtualIndex - visualPage.get()) * pageStride }],
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<Animated.View
|
|
44
|
+
style={[
|
|
45
|
+
styles.slot,
|
|
46
|
+
{
|
|
47
|
+
height,
|
|
48
|
+
width,
|
|
49
|
+
},
|
|
50
|
+
slotAnimatedStyle,
|
|
51
|
+
]}
|
|
52
|
+
>
|
|
53
|
+
<Animated.View style={[styles.page, isActive && animatedStyle]}>
|
|
54
|
+
<View style={[styles.item, { height, width }]}>
|
|
55
|
+
{renderItem(slot.item, slot.logicalIndex, { isActive })}
|
|
56
|
+
</View>
|
|
57
|
+
</Animated.View>
|
|
58
|
+
</Animated.View>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function GestureViewer<ItemT>({
|
|
25
63
|
id = 'default',
|
|
26
64
|
data,
|
|
27
|
-
renderItem
|
|
65
|
+
renderItem,
|
|
28
66
|
renderContainer,
|
|
29
|
-
ListComponent,
|
|
30
67
|
width: customWidth,
|
|
31
68
|
height: customHeight,
|
|
32
|
-
listProps,
|
|
33
69
|
backdropStyle: backdropStyleProps,
|
|
34
70
|
containerStyle,
|
|
35
71
|
initialIndex = 0,
|
|
36
|
-
|
|
37
|
-
|
|
72
|
+
pageSpacing = 0,
|
|
73
|
+
windowSize = 3,
|
|
38
74
|
enableLoop = false,
|
|
39
75
|
...props
|
|
40
|
-
}: GestureViewerProps<ItemT
|
|
41
|
-
const Component = ListComponent as React.ComponentType<any>;
|
|
42
|
-
|
|
43
|
-
const dataRef = useRef(data);
|
|
44
|
-
|
|
76
|
+
}: GestureViewerProps<ItemT>) {
|
|
45
77
|
const { width: screenWidth, height: screenHeight } = useWindowDimensions();
|
|
46
78
|
|
|
47
79
|
const width = customWidth || screenWidth;
|
|
48
80
|
const height = customHeight || screenHeight;
|
|
49
81
|
|
|
50
|
-
const loopData = useMemo(() => createLoopData(dataRef, enableLoop), [enableLoop]);
|
|
51
|
-
|
|
52
|
-
const isScrollView = isScrollViewLike(Component);
|
|
53
|
-
const isFlashList = isFlashListLike(Component);
|
|
54
|
-
|
|
55
82
|
const {
|
|
56
|
-
activeListIndex,
|
|
57
|
-
listRef,
|
|
58
|
-
isZoomed,
|
|
59
|
-
isRotated,
|
|
60
|
-
isPinching,
|
|
61
|
-
dismissGesture,
|
|
62
|
-
zoomGesture,
|
|
63
|
-
nativeScrollGesture,
|
|
64
|
-
onWebClick,
|
|
65
|
-
onMomentumScrollEnd,
|
|
66
|
-
onScroll,
|
|
67
|
-
onScrollBeginDrag,
|
|
68
83
|
animatedStyle,
|
|
69
84
|
backdropStyle,
|
|
85
|
+
centerVirtualIndex,
|
|
86
|
+
dismissGesture,
|
|
70
87
|
handleDismiss,
|
|
88
|
+
horizontalPagingGesture,
|
|
89
|
+
onWebClick,
|
|
90
|
+
pageStride,
|
|
91
|
+
renderWindowSlots,
|
|
92
|
+
visualPage,
|
|
93
|
+
zoomGesture,
|
|
94
|
+
zoomPinchGesture,
|
|
71
95
|
} = useGestureViewer({
|
|
72
96
|
id,
|
|
73
97
|
data,
|
|
74
98
|
width,
|
|
75
99
|
height,
|
|
76
100
|
initialIndex,
|
|
77
|
-
|
|
101
|
+
pageSpacing,
|
|
102
|
+
windowSize,
|
|
78
103
|
enableLoop,
|
|
79
104
|
...props,
|
|
80
105
|
});
|
|
81
106
|
|
|
82
|
-
const keyExtractor = useCallback(
|
|
83
|
-
(item: ItemT, index: number) => {
|
|
84
|
-
if (enableLoop) {
|
|
85
|
-
return typeof item === 'string' ? `${item}-${index}` : `item-${index}`;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return typeof item === 'string' ? item : `image-${index}`;
|
|
89
|
-
},
|
|
90
|
-
[enableLoop],
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
const renderItem = useCallback(
|
|
94
|
-
({ item, index, target }: { item: ItemT; index: number; target?: string }) => {
|
|
95
|
-
const isFlashListCell = !isFlashList || target === undefined || target === 'Cell';
|
|
96
|
-
|
|
97
|
-
return (
|
|
98
|
-
<View
|
|
99
|
-
key={isScrollView ? keyExtractor(item, index) : undefined}
|
|
100
|
-
style={[
|
|
101
|
-
{
|
|
102
|
-
width,
|
|
103
|
-
height,
|
|
104
|
-
marginHorizontal: itemSpacing / 2,
|
|
105
|
-
},
|
|
106
|
-
styles.item,
|
|
107
|
-
]}
|
|
108
|
-
>
|
|
109
|
-
{renderItemProp(item, index, {
|
|
110
|
-
isActive: index === activeListIndex && isFlashListCell,
|
|
111
|
-
})}
|
|
112
|
-
</View>
|
|
113
|
-
);
|
|
114
|
-
},
|
|
115
|
-
[
|
|
116
|
-
activeListIndex,
|
|
117
|
-
width,
|
|
118
|
-
itemSpacing,
|
|
119
|
-
renderItemProp,
|
|
120
|
-
keyExtractor,
|
|
121
|
-
isScrollView,
|
|
122
|
-
isFlashList,
|
|
123
|
-
height,
|
|
124
|
-
],
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
const getItemLayout = useCallback(
|
|
128
|
-
(_: ArrayLike<ItemT> | null | undefined, index: number) => ({
|
|
129
|
-
length: width + itemSpacing,
|
|
130
|
-
offset: (width + itemSpacing) * index,
|
|
131
|
-
index,
|
|
132
|
-
}),
|
|
133
|
-
[width, itemSpacing],
|
|
134
|
-
);
|
|
135
|
-
|
|
136
107
|
const gesture = useMemo(() => {
|
|
137
|
-
return
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
108
|
+
return composeGestureViewerGestures({
|
|
109
|
+
dismissGesture,
|
|
110
|
+
horizontalPagingGesture,
|
|
111
|
+
zoomGesture,
|
|
112
|
+
zoomPinchGesture,
|
|
113
|
+
});
|
|
114
|
+
}, [dismissGesture, horizontalPagingGesture, zoomGesture, zoomPinchGesture]);
|
|
143
115
|
|
|
144
116
|
useEffect(() => {
|
|
145
117
|
registry.createManager(id);
|
|
@@ -147,126 +119,59 @@ export function GestureViewer<ItemT, LC>({
|
|
|
147
119
|
return () => registry.deleteManager(id);
|
|
148
120
|
}, [id]);
|
|
149
121
|
|
|
150
|
-
const commonProps = useMemo(
|
|
151
|
-
() =>
|
|
152
|
-
({
|
|
153
|
-
horizontal: true,
|
|
154
|
-
scrollEnabled: !isZoomed && !isRotated && !isPinching,
|
|
155
|
-
showsHorizontalScrollIndicator: false,
|
|
156
|
-
onMomentumScrollEnd,
|
|
157
|
-
onScroll,
|
|
158
|
-
onScrollBeginDrag,
|
|
159
|
-
...(enableSnapMode
|
|
160
|
-
? {
|
|
161
|
-
snapToInterval: width + itemSpacing,
|
|
162
|
-
snapToAlignment: 'center',
|
|
163
|
-
decelerationRate: 'fast',
|
|
164
|
-
}
|
|
165
|
-
: {
|
|
166
|
-
pagingEnabled: true,
|
|
167
|
-
}),
|
|
168
|
-
scrollEventThrottle: 16,
|
|
169
|
-
removeClippedSubviews: true,
|
|
170
|
-
}) satisfies ScrollViewProps,
|
|
171
|
-
[
|
|
172
|
-
width,
|
|
173
|
-
itemSpacing,
|
|
174
|
-
isZoomed,
|
|
175
|
-
isRotated,
|
|
176
|
-
isPinching,
|
|
177
|
-
onMomentumScrollEnd,
|
|
178
|
-
onScroll,
|
|
179
|
-
onScrollBeginDrag,
|
|
180
|
-
enableSnapMode,
|
|
181
|
-
],
|
|
182
|
-
);
|
|
183
|
-
|
|
184
122
|
const control = useMemo(() => ({ dismiss: handleDismiss }), [handleDismiss]);
|
|
123
|
+
const webContentProps = getWebContentProps(onWebClick);
|
|
185
124
|
|
|
186
|
-
const
|
|
187
|
-
Platform.OS,
|
|
188
|
-
Component,
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
const maybeWrapWithNativeScrollGesture = useCallback(
|
|
192
|
-
(children: React.ReactNode) => {
|
|
193
|
-
if (!shouldWrapScrollableWithNativeGesture) {
|
|
194
|
-
return children;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return <GestureDetector gesture={nativeScrollGesture}>{children}</GestureDetector>;
|
|
198
|
-
},
|
|
199
|
-
[nativeScrollGesture, shouldWrapScrollableWithNativeGesture],
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
const listComponent = (
|
|
125
|
+
const viewer = (
|
|
203
126
|
<GestureHandlerRootView>
|
|
204
127
|
<GestureDetector gesture={gesture}>
|
|
205
|
-
<View style={[{
|
|
128
|
+
<View style={[styles.container, { height, width }, containerStyle]}>
|
|
206
129
|
<Animated.View style={[styles.background, backdropStyleProps, backdropStyle]} />
|
|
207
|
-
<Animated.View
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
: isFlatListLike(Component) &&
|
|
222
|
-
maybeWrapWithNativeScrollGesture(
|
|
223
|
-
<Component
|
|
224
|
-
ref={listRef}
|
|
225
|
-
{...commonProps}
|
|
226
|
-
data={loopData}
|
|
227
|
-
renderItem={renderItem}
|
|
228
|
-
initialScrollIndex={
|
|
229
|
-
enableLoop && data.length > 1 ? initialIndex + 1 : initialIndex
|
|
230
|
-
}
|
|
231
|
-
keyExtractor={keyExtractor}
|
|
232
|
-
{...(isFlashList
|
|
233
|
-
? // NOTE - Deprecated estimatedItemSize for FlashList V2 (https://shopify.github.io/flash-list/docs/v2-changes#deprecated)
|
|
234
|
-
{ estimatedItemSize: width + itemSpacing }
|
|
235
|
-
: { windowSize: 3, maxToRenderPerBatch: 3, getItemLayout })}
|
|
236
|
-
// NOTE - https://github.com/necolas/react-native-web/issues/1299
|
|
237
|
-
{...(Platform.OS === 'web' &&
|
|
238
|
-
isFlatListLike(Component) && {
|
|
239
|
-
dataSet: { 'flat-list-paging-enabled-fix': true },
|
|
240
|
-
})}
|
|
241
|
-
{...listProps}
|
|
242
|
-
/>,
|
|
243
|
-
)}
|
|
130
|
+
<Animated.View style={styles.content} {...webContentProps}>
|
|
131
|
+
{renderWindowSlots.map((slot) => (
|
|
132
|
+
<RenderWindowSlotView
|
|
133
|
+
animatedStyle={animatedStyle}
|
|
134
|
+
height={height}
|
|
135
|
+
isActive={slot.virtualIndex === centerVirtualIndex}
|
|
136
|
+
key={slot.slotKey}
|
|
137
|
+
pageStride={pageStride}
|
|
138
|
+
renderItem={renderItem}
|
|
139
|
+
slot={slot}
|
|
140
|
+
visualPage={visualPage}
|
|
141
|
+
width={width}
|
|
142
|
+
/>
|
|
143
|
+
))}
|
|
244
144
|
</Animated.View>
|
|
245
|
-
<WebPagingFixStyle Component={Component} />
|
|
246
145
|
</View>
|
|
247
146
|
</GestureDetector>
|
|
248
147
|
</GestureHandlerRootView>
|
|
249
148
|
);
|
|
250
149
|
|
|
251
|
-
return renderContainer ? renderContainer(
|
|
150
|
+
return renderContainer ? renderContainer(viewer, control) : viewer;
|
|
252
151
|
}
|
|
253
152
|
|
|
254
153
|
const styles = StyleSheet.create({
|
|
154
|
+
background: {
|
|
155
|
+
...StyleSheet.absoluteFill,
|
|
156
|
+
backgroundColor: '#000',
|
|
157
|
+
},
|
|
158
|
+
container: {
|
|
159
|
+
overflow: 'hidden',
|
|
160
|
+
},
|
|
255
161
|
content: {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
height: '100%',
|
|
162
|
+
...StyleSheet.absoluteFill,
|
|
163
|
+
overflow: 'hidden',
|
|
259
164
|
},
|
|
260
165
|
item: {
|
|
261
|
-
justifyContent: 'center',
|
|
262
166
|
alignItems: 'center',
|
|
167
|
+
justifyContent: 'center',
|
|
263
168
|
},
|
|
264
|
-
|
|
169
|
+
page: {
|
|
170
|
+
flex: 1,
|
|
171
|
+
},
|
|
172
|
+
slot: {
|
|
173
|
+
left: 0,
|
|
265
174
|
position: 'absolute',
|
|
266
175
|
top: 0,
|
|
267
|
-
left: 0,
|
|
268
|
-
right: 0,
|
|
269
|
-
bottom: 0,
|
|
270
|
-
backgroundColor: 'black',
|
|
271
176
|
},
|
|
272
177
|
});
|
|
@@ -6,29 +6,45 @@ import type {
|
|
|
6
6
|
GestureViewerEventType,
|
|
7
7
|
GestureViewerState,
|
|
8
8
|
} from './types';
|
|
9
|
-
import { createBoundsConstraint
|
|
9
|
+
import { createBoundsConstraint } from './utils';
|
|
10
|
+
|
|
11
|
+
export type GestureViewerNavigationOptions = {
|
|
12
|
+
animated?: boolean;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type GestureViewerNavigationAdapter = {
|
|
16
|
+
goToIndex: (index: number, options?: GestureViewerNavigationOptions) => void;
|
|
17
|
+
goToNext: () => void;
|
|
18
|
+
goToPrevious: () => void;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type GestureViewerStateReader = () => GestureViewerState;
|
|
22
|
+
|
|
23
|
+
type GestureViewerEventListenerPresenceCallback = (
|
|
24
|
+
eventType: GestureViewerEventType,
|
|
25
|
+
hasListeners: boolean,
|
|
26
|
+
) => void;
|
|
27
|
+
|
|
28
|
+
const DEFAULT_STATE_READER: GestureViewerStateReader = () => ({
|
|
29
|
+
currentIndex: 0,
|
|
30
|
+
totalCount: 0,
|
|
31
|
+
});
|
|
10
32
|
|
|
11
33
|
class GestureViewerManager {
|
|
12
|
-
private currentIndex = 0;
|
|
13
|
-
private dataLength = 0;
|
|
14
34
|
private width = 0;
|
|
15
35
|
private height = 0;
|
|
16
36
|
private maxZoomScale = 2;
|
|
17
|
-
private
|
|
18
|
-
private
|
|
19
|
-
private listRef: any | null = null;
|
|
37
|
+
private navigationAdapter: GestureViewerNavigationAdapter | null = null;
|
|
38
|
+
private stateReader = DEFAULT_STATE_READER;
|
|
20
39
|
|
|
21
40
|
private scale: SharedValue<number> | null = null;
|
|
22
41
|
private rotation: SharedValue<number> | null = null;
|
|
23
42
|
private translateX: SharedValue<number> | null = null;
|
|
24
43
|
private translateY: SharedValue<number> | null = null;
|
|
25
|
-
private resetTransformCallback: (() => void) | null = null;
|
|
26
|
-
|
|
27
|
-
private loopCallback: (() => void) | null = null;
|
|
28
|
-
private programmaticScrollVersion = 0;
|
|
29
44
|
|
|
30
45
|
private listeners = new Set<(state: GestureViewerState) => void>();
|
|
31
46
|
private eventListeners = new Map<GestureViewerEventType, Set<(data: any) => void>>();
|
|
47
|
+
private eventListenerPresenceSubscribers = new Set<GestureViewerEventListenerPresenceCallback>();
|
|
32
48
|
|
|
33
49
|
private notifyListeners() {
|
|
34
50
|
const state = this.getState();
|
|
@@ -53,6 +69,7 @@ class GestureViewerManager {
|
|
|
53
69
|
}
|
|
54
70
|
|
|
55
71
|
this.eventListeners.get(eventType)?.add(callback);
|
|
72
|
+
this.notifyEventListenerPresence(eventType);
|
|
56
73
|
|
|
57
74
|
return () => {
|
|
58
75
|
const listeners = this.eventListeners.get(eventType);
|
|
@@ -64,9 +81,40 @@ class GestureViewerManager {
|
|
|
64
81
|
this.eventListeners.delete(eventType);
|
|
65
82
|
}
|
|
66
83
|
}
|
|
84
|
+
|
|
85
|
+
this.notifyEventListenerPresence(eventType);
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
subscribeToEventListenerPresence(callback: GestureViewerEventListenerPresenceCallback) {
|
|
90
|
+
this.eventListenerPresenceSubscribers.add(callback);
|
|
91
|
+
|
|
92
|
+
this.notifyEventListenerPresenceSubscriber(callback, 'zoomChange');
|
|
93
|
+
this.notifyEventListenerPresenceSubscriber(callback, 'rotationChange');
|
|
94
|
+
this.notifyEventListenerPresenceSubscriber(callback, 'tap');
|
|
95
|
+
|
|
96
|
+
return () => {
|
|
97
|
+
this.eventListenerPresenceSubscribers.delete(callback);
|
|
67
98
|
};
|
|
68
99
|
}
|
|
69
100
|
|
|
101
|
+
hasEventListeners(eventType: GestureViewerEventType) {
|
|
102
|
+
return (this.eventListeners.get(eventType)?.size ?? 0) > 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private notifyEventListenerPresence(eventType: GestureViewerEventType) {
|
|
106
|
+
this.eventListenerPresenceSubscribers.forEach((callback) => {
|
|
107
|
+
this.notifyEventListenerPresenceSubscriber(callback, eventType);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private notifyEventListenerPresenceSubscriber(
|
|
112
|
+
callback: GestureViewerEventListenerPresenceCallback,
|
|
113
|
+
eventType: GestureViewerEventType,
|
|
114
|
+
) {
|
|
115
|
+
callback(eventType, this.hasEventListeners(eventType));
|
|
116
|
+
}
|
|
117
|
+
|
|
70
118
|
private emitEvent<T extends GestureViewerEventType>(
|
|
71
119
|
eventType: T,
|
|
72
120
|
data: GestureViewerEventData[T],
|
|
@@ -91,18 +139,7 @@ class GestureViewerManager {
|
|
|
91
139
|
};
|
|
92
140
|
|
|
93
141
|
getState(): GestureViewerState {
|
|
94
|
-
return
|
|
95
|
-
currentIndex: this.currentIndex,
|
|
96
|
-
totalCount: this.dataLength,
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
getProgrammaticScrollVersion() {
|
|
101
|
-
return this.programmaticScrollVersion;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
setEnableLoop(enabled: boolean) {
|
|
105
|
-
this.enableLoop = enabled;
|
|
142
|
+
return this.stateReader();
|
|
106
143
|
}
|
|
107
144
|
|
|
108
145
|
setWidth(width: number) {
|
|
@@ -113,22 +150,12 @@ class GestureViewerManager {
|
|
|
113
150
|
this.height = height;
|
|
114
151
|
}
|
|
115
152
|
|
|
116
|
-
|
|
117
|
-
this.
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
setDataLength(length: number) {
|
|
121
|
-
this.dataLength = length;
|
|
153
|
+
setNavigationAdapter(adapter: GestureViewerNavigationAdapter | null) {
|
|
154
|
+
this.navigationAdapter = adapter;
|
|
122
155
|
}
|
|
123
156
|
|
|
124
|
-
|
|
125
|
-
this.
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
setCurrentIndex(index: number) {
|
|
129
|
-
if (index !== this.currentIndex) {
|
|
130
|
-
this.currentIndex = index;
|
|
131
|
-
}
|
|
157
|
+
setStateReader(reader: GestureViewerStateReader | null) {
|
|
158
|
+
this.stateReader = reader ?? DEFAULT_STATE_READER;
|
|
132
159
|
}
|
|
133
160
|
|
|
134
161
|
setZoomSharedValues(
|
|
@@ -143,10 +170,6 @@ class GestureViewerManager {
|
|
|
143
170
|
this.maxZoomScale = maxZoomScale;
|
|
144
171
|
}
|
|
145
172
|
|
|
146
|
-
setResetTransformCallback(callback: (() => void) | null) {
|
|
147
|
-
this.resetTransformCallback = callback;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
173
|
notifyStateChange() {
|
|
151
174
|
this.notifyListeners();
|
|
152
175
|
}
|
|
@@ -266,110 +289,42 @@ class GestureViewerManager {
|
|
|
266
289
|
this.translateY.set(withTiming(0));
|
|
267
290
|
};
|
|
268
291
|
|
|
269
|
-
goToIndex = (index: number) => {
|
|
270
|
-
if (!this.
|
|
292
|
+
goToIndex = (index: number, options?: GestureViewerNavigationOptions) => {
|
|
293
|
+
if (!this.navigationAdapter) {
|
|
271
294
|
return;
|
|
272
295
|
}
|
|
273
296
|
|
|
274
|
-
this.
|
|
275
|
-
|
|
276
|
-
const { scrollTo } = createScrollAction(this.listRef, this.width);
|
|
277
|
-
|
|
278
|
-
if (this.enableLoop && this.dataLength > 1) {
|
|
279
|
-
if (index < 0) {
|
|
280
|
-
this.loopCallback = () => {
|
|
281
|
-
scrollTo(this.dataLength, false);
|
|
282
|
-
this.updateCurrentIndex(this.dataLength - 1);
|
|
283
|
-
this.cancelPendingLoopTransition();
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
this.resetTransformCallback?.();
|
|
287
|
-
this.programmaticScrollVersion += 1;
|
|
288
|
-
scrollTo(0, true);
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
if (index >= this.dataLength) {
|
|
293
|
-
this.loopCallback = () => {
|
|
294
|
-
scrollTo(1, false);
|
|
295
|
-
this.updateCurrentIndex(0);
|
|
296
|
-
this.cancelPendingLoopTransition();
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
this.resetTransformCallback?.();
|
|
300
|
-
this.programmaticScrollVersion += 1;
|
|
301
|
-
scrollTo(this.dataLength + 1, true);
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
this.resetTransformCallback?.();
|
|
306
|
-
this.programmaticScrollVersion += 1;
|
|
307
|
-
scrollTo(index + 1, true);
|
|
308
|
-
this.updateCurrentIndex(index);
|
|
309
|
-
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
297
|
+
this.navigationAdapter.goToIndex(index, options);
|
|
298
|
+
};
|
|
312
299
|
|
|
313
|
-
|
|
300
|
+
goToPrevious = () => {
|
|
301
|
+
if (!this.navigationAdapter) {
|
|
314
302
|
return;
|
|
315
303
|
}
|
|
316
304
|
|
|
317
|
-
this.
|
|
318
|
-
this.programmaticScrollVersion += 1;
|
|
319
|
-
scrollTo(index, true);
|
|
320
|
-
this.updateCurrentIndex(index);
|
|
305
|
+
this.navigationAdapter.goToPrevious();
|
|
321
306
|
};
|
|
322
307
|
|
|
323
|
-
|
|
324
|
-
if (!this.
|
|
325
|
-
return
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
if (scrollIndex === 0 || scrollIndex === this.dataLength + 1) {
|
|
329
|
-
this.loopCallback();
|
|
330
|
-
return true;
|
|
308
|
+
goToNext = () => {
|
|
309
|
+
if (!this.navigationAdapter) {
|
|
310
|
+
return;
|
|
331
311
|
}
|
|
332
312
|
|
|
333
|
-
this.
|
|
334
|
-
return true;
|
|
335
|
-
};
|
|
336
|
-
|
|
337
|
-
cancelPendingLoopTransition = () => {
|
|
338
|
-
this.loopCallback = null;
|
|
339
|
-
};
|
|
340
|
-
|
|
341
|
-
handleScrollBeginDrag = () => {
|
|
342
|
-
this.cancelPendingLoopTransition();
|
|
343
|
-
};
|
|
344
|
-
|
|
345
|
-
goToPrevious = () => {
|
|
346
|
-
this.goToIndex(this.currentIndex - 1);
|
|
347
|
-
};
|
|
348
|
-
|
|
349
|
-
goToNext = () => {
|
|
350
|
-
this.goToIndex(this.currentIndex + 1);
|
|
313
|
+
this.navigationAdapter.goToNext();
|
|
351
314
|
};
|
|
352
315
|
|
|
353
316
|
cleanUp() {
|
|
354
|
-
this.cancelPendingLoopTransition();
|
|
355
317
|
this.listeners.clear();
|
|
356
|
-
this.
|
|
357
|
-
this.
|
|
358
|
-
this.currentIndex = 0;
|
|
359
|
-
this.dataLength = 0;
|
|
318
|
+
this.navigationAdapter = null;
|
|
319
|
+
this.stateReader = DEFAULT_STATE_READER;
|
|
360
320
|
this.maxZoomScale = 2;
|
|
361
321
|
this.scale = null;
|
|
362
322
|
this.translateX = null;
|
|
363
323
|
this.translateY = null;
|
|
364
324
|
this.rotation = null;
|
|
365
|
-
this.resetTransformCallback = null;
|
|
366
325
|
this.eventListeners.clear();
|
|
326
|
+
this.eventListenerPresenceSubscribers.clear();
|
|
367
327
|
}
|
|
368
|
-
|
|
369
|
-
private updateCurrentIndex = (targetIndex: number) => {
|
|
370
|
-
this.currentIndex = targetIndex;
|
|
371
|
-
this.notifyListeners();
|
|
372
|
-
};
|
|
373
328
|
}
|
|
374
329
|
|
|
375
330
|
export default GestureViewerManager;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Easing } from 'react-native-reanimated';
|
|
2
|
+
|
|
3
|
+
export const PAGE_TRANSITION_CONFIG = {
|
|
4
|
+
duration: 240,
|
|
5
|
+
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const PAGE_SPRING_CONFIG = {
|
|
9
|
+
damping: 35,
|
|
10
|
+
energyThreshold: 6e-9,
|
|
11
|
+
mass: 1,
|
|
12
|
+
overshootClamping: false,
|
|
13
|
+
stiffness: 360,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const SWIPE_THRESHOLD_RATIO = 0.25;
|
|
17
|
+
export const SWIPE_VELOCITY_THRESHOLD = 800;
|
|
18
|
+
export const EDGE_RESISTANCE = 0.35;
|