react-native-gesture-image-viewer 2.0.0-beta.4 → 2.0.0-beta.6
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 +32 -15
- package/lib/module/GestureTrigger.js +83 -0
- package/lib/module/GestureTrigger.js.map +1 -0
- package/lib/module/GestureViewer.js +9 -3
- package/lib/module/GestureViewer.js.map +1 -1
- package/lib/module/GestureViewerRegistry.js +11 -0
- package/lib/module/GestureViewerRegistry.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/useGestureViewer.js +102 -10
- package/lib/module/useGestureViewer.js.map +1 -1
- package/lib/typescript/src/GestureTrigger.d.ts +55 -0
- package/lib/typescript/src/GestureTrigger.d.ts.map +1 -0
- package/lib/typescript/src/GestureViewer.d.ts.map +1 -1
- package/lib/typescript/src/GestureViewerRegistry.d.ts +5 -0
- package/lib/typescript/src/GestureViewerRegistry.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +67 -12
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useGestureViewer.d.ts +9 -8
- package/lib/typescript/src/useGestureViewer.d.ts.map +1 -1
- package/package.json +14 -5
- package/src/GestureTrigger.tsx +90 -0
- package/src/GestureViewer.tsx +8 -2
- package/src/GestureViewerRegistry.ts +16 -0
- package/src/index.tsx +2 -0
- package/src/types.ts +67 -12
- package/src/useGestureViewer.ts +141 -10
package/src/types.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
2
|
import type { FlatList as RNFlatList, ScrollView as RNScrollView, StyleProp, ViewStyle } from 'react-native';
|
|
3
3
|
import type { FlatList as GHFlatList, ScrollView as GHScrollView } from 'react-native-gesture-handler';
|
|
4
|
+
import type { WithTimingConfig } from 'react-native-reanimated';
|
|
4
5
|
|
|
5
6
|
export type FlatListComponent = typeof RNFlatList | typeof GHFlatList;
|
|
6
7
|
export type ScrollViewComponent = typeof RNScrollView | typeof GHScrollView;
|
|
@@ -13,10 +14,34 @@ type ConditionalListProps<LC> = LC extends FlatListComponent
|
|
|
13
14
|
? React.ComponentProps<LC>
|
|
14
15
|
: GetComponentProps<LC>;
|
|
15
16
|
|
|
17
|
+
export type TriggerRect = {
|
|
18
|
+
x: number;
|
|
19
|
+
y: number;
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export interface TriggerAnimationConfig extends WithTimingConfig {
|
|
25
|
+
/**
|
|
26
|
+
* Animation duration in milliseconds
|
|
27
|
+
* @defaultValue 300
|
|
28
|
+
*/
|
|
29
|
+
duration?: WithTimingConfig['duration'];
|
|
30
|
+
/**
|
|
31
|
+
* Animation easing function
|
|
32
|
+
* @defaultValue Easing.bezier(0.25, 0.1, 0.25, 1.0)
|
|
33
|
+
*/
|
|
34
|
+
easing?: WithTimingConfig['easing'];
|
|
35
|
+
/**
|
|
36
|
+
* Callback function called after animation completion
|
|
37
|
+
*/
|
|
38
|
+
onAnimationComplete?: () => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
16
41
|
export interface GestureViewerProps<T = any, LC = typeof RNScrollView> {
|
|
17
42
|
/**
|
|
18
43
|
* When you want to efficiently manage multiple `GestureViewer` instances, you can use the `id` prop to use multiple `GestureViewer` components.
|
|
19
|
-
* @
|
|
44
|
+
* @remarks `GestureViewer` automatically removes instances from memory when components are unmounted, so no manual memory management is required.
|
|
20
45
|
* @defaultValue 'default'
|
|
21
46
|
*/
|
|
22
47
|
id?: string;
|
|
@@ -33,27 +58,38 @@ export interface GestureViewerProps<T = any, LC = typeof RNScrollView> {
|
|
|
33
58
|
* A callback function that is called when the `GestureViewer` is dismissed.
|
|
34
59
|
*/
|
|
35
60
|
onDismiss?: () => void;
|
|
61
|
+
/**
|
|
62
|
+
* A callback function that is called when the dismiss interaction starts.
|
|
63
|
+
* @remarks Useful to hide external UI (e.g., headers, buttons) while the dismiss gesture/animation is in progress.
|
|
64
|
+
*/
|
|
65
|
+
onDismissStart?: () => void;
|
|
36
66
|
/**
|
|
37
67
|
* A callback function that is called to render the item.
|
|
38
68
|
*/
|
|
39
69
|
renderItem: (item: T, index: number) => React.ReactElement;
|
|
40
70
|
/**
|
|
41
71
|
* A callback function that is called to render the container.
|
|
72
|
+
* @remarks Useful for composing additional UI (e.g., close button, toolbars) around the viewer.
|
|
73
|
+
* The second argument provides control helpers such as `dismiss()` to close the viewer.
|
|
74
|
+
*
|
|
75
|
+
* @param children - The viewer content to be rendered inside your container.
|
|
76
|
+
* @param helpers - Control helpers for the viewer. Currently includes `dismiss()`.
|
|
77
|
+
* @returns A React element that wraps and renders the provided `children`.
|
|
42
78
|
*/
|
|
43
|
-
renderContainer?: (children: React.ReactElement) => React.ReactElement;
|
|
79
|
+
renderContainer?: (children: React.ReactElement, helpers: { dismiss: () => void }) => React.ReactElement;
|
|
44
80
|
/**
|
|
45
81
|
* Support for any list component like `ScrollView`, `FlatList`, `FlashList` through the `ListComponent` prop.
|
|
46
82
|
*/
|
|
47
83
|
ListComponent: LC;
|
|
48
84
|
/**
|
|
49
85
|
* The width of the `GestureViewer`.
|
|
50
|
-
* @
|
|
86
|
+
* @remarks If you don't set this prop, the width of the `GestureViewer` will be the same as the width of the screen.
|
|
51
87
|
* @defaultValue screen width
|
|
52
88
|
*/
|
|
53
89
|
width?: number;
|
|
54
90
|
/**
|
|
55
91
|
* The props to pass to the list component.
|
|
56
|
-
* @
|
|
92
|
+
* @remarks The `listProps` provides **type inference based on the selected list component**, ensuring accurate autocompletion and type safety in your IDE.
|
|
57
93
|
*/
|
|
58
94
|
listProps?: Partial<ConditionalListProps<LC>>;
|
|
59
95
|
/**
|
|
@@ -66,7 +102,7 @@ export interface GestureViewerProps<T = any, LC = typeof RNScrollView> {
|
|
|
66
102
|
containerStyle?: StyleProp<ViewStyle>;
|
|
67
103
|
/**
|
|
68
104
|
* Dismiss gesture options. Calls `onDismiss` function when swiping down.
|
|
69
|
-
* @
|
|
105
|
+
* @remarks Useful for closing modals with downward swipe gestures.
|
|
70
106
|
*/
|
|
71
107
|
dismiss?: {
|
|
72
108
|
/**
|
|
@@ -86,32 +122,32 @@ export interface GestureViewerProps<T = any, LC = typeof RNScrollView> {
|
|
|
86
122
|
resistance?: number;
|
|
87
123
|
/**
|
|
88
124
|
* By default, the background `opacity` gradually decreases from 1 to 0 during downward swipe gestures.
|
|
89
|
-
* @
|
|
125
|
+
* @remarks When `false`, this animation is disabled.
|
|
90
126
|
* @defaultValue true
|
|
91
127
|
*/
|
|
92
128
|
fadeBackdrop?: boolean;
|
|
93
129
|
};
|
|
94
130
|
/**
|
|
95
131
|
* Controls left/right swipe gestures.
|
|
96
|
-
* @
|
|
132
|
+
* @remarks When `false`, horizontal gestures are disabled.
|
|
97
133
|
* @defaultValue true
|
|
98
134
|
*/
|
|
99
135
|
enableHorizontalSwipe?: boolean;
|
|
100
136
|
/**
|
|
101
137
|
* Only works when zoom is active, allows moving item position when zoomed.
|
|
102
|
-
* @
|
|
138
|
+
* @remarks When `false`, gesture movement is disabled during zoom.
|
|
103
139
|
* @defaultValue true
|
|
104
140
|
*/
|
|
105
141
|
enablePanWhenZoomed?: boolean;
|
|
106
142
|
/**
|
|
107
143
|
* Controls two-finger pinch gestures.
|
|
108
|
-
* @
|
|
144
|
+
* @remarks When `false`, two-finger zoom gestures are disabled.
|
|
109
145
|
* @defaultValue true
|
|
110
146
|
*/
|
|
111
147
|
enablePinchZoom?: boolean;
|
|
112
148
|
/**
|
|
113
149
|
* Controls double-tap zoom gestures.
|
|
114
|
-
* @
|
|
150
|
+
* @remarks When `false`, double-tap zoom gestures are disabled.
|
|
115
151
|
* @defaultValue true
|
|
116
152
|
*/
|
|
117
153
|
enableDoubleTapZoom?: boolean;
|
|
@@ -123,7 +159,7 @@ export interface GestureViewerProps<T = any, LC = typeof RNScrollView> {
|
|
|
123
159
|
/**
|
|
124
160
|
* Enables snap scrolling mode.
|
|
125
161
|
*
|
|
126
|
-
* @
|
|
162
|
+
* @remarks
|
|
127
163
|
* **`false` (default)**: Paging mode (`pagingEnabled: true`)
|
|
128
164
|
* - Scrolls by full screen size increments
|
|
129
165
|
*
|
|
@@ -136,7 +172,7 @@ export interface GestureViewerProps<T = any, LC = typeof RNScrollView> {
|
|
|
136
172
|
enableSnapMode?: boolean;
|
|
137
173
|
/**
|
|
138
174
|
* The spacing between items in pixels.
|
|
139
|
-
* @
|
|
175
|
+
* @remarks Only applied when `enableSnapMode` is `true`.
|
|
140
176
|
* @defaultValue 0
|
|
141
177
|
*/
|
|
142
178
|
itemSpacing?: number;
|
|
@@ -145,6 +181,25 @@ export interface GestureViewerProps<T = any, LC = typeof RNScrollView> {
|
|
|
145
181
|
* @defaultValue 2
|
|
146
182
|
*/
|
|
147
183
|
maxZoomScale?: number;
|
|
184
|
+
/**
|
|
185
|
+
* Trigger-based animation settings
|
|
186
|
+
* @remarks You can customize animation duration, easing, and system reduce-motion behavior.
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* ```tsx
|
|
190
|
+
* <GestureViewer
|
|
191
|
+
* triggerAnimation={{
|
|
192
|
+
* duration: 250,
|
|
193
|
+
* easing: Easing.out(Easing.cubic),
|
|
194
|
+
* reduceMotion: 'system',
|
|
195
|
+
* onAnimationComplete: () => {
|
|
196
|
+
* console.log('Animation complete');
|
|
197
|
+
* },
|
|
198
|
+
* }}
|
|
199
|
+
* />
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
triggerAnimation?: TriggerAnimationConfig;
|
|
148
203
|
}
|
|
149
204
|
|
|
150
205
|
/**
|
package/src/useGestureViewer.ts
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
import { runOnJS } from 'react-native-worklets';
|
|
19
19
|
import type GestureViewerManager from './GestureViewerManager';
|
|
20
20
|
import { registry } from './GestureViewerRegistry';
|
|
21
|
-
import type { GestureViewerProps } from './types';
|
|
21
|
+
import type { GestureViewerProps, TriggerRect } from './types';
|
|
22
22
|
import { createBoundsConstraint, createScrollAction, getLoopAdjustedIndex } from './utils';
|
|
23
23
|
|
|
24
24
|
type UseGestureViewerProps<T = any> = Omit<
|
|
@@ -41,15 +41,20 @@ export const useGestureViewer = <T = any>({
|
|
|
41
41
|
itemSpacing = 0,
|
|
42
42
|
enableSnapMode = false,
|
|
43
43
|
id = 'default',
|
|
44
|
+
onDismissStart,
|
|
45
|
+
triggerAnimation,
|
|
44
46
|
}: UseGestureViewerProps<T>) => {
|
|
45
47
|
const { width: screenWidth, height: screenHeight } = useWindowDimensions();
|
|
46
48
|
const width = enableSnapMode ? customWidth || screenWidth : screenWidth;
|
|
47
49
|
|
|
48
50
|
const [isZoomed, setIsZoomed] = useState(false);
|
|
49
51
|
const [isRotated, setIsRotated] = useState(false);
|
|
52
|
+
const [shouldStartTriggerAnimation, setShouldStartTriggerAnimation] = useState(false);
|
|
50
53
|
const [manager, setManager] = useState<GestureViewerManager | null>(null);
|
|
51
54
|
|
|
52
55
|
const listRef = useRef<any>(null);
|
|
56
|
+
const triggerRectRef = useRef<TriggerRect | null>(null);
|
|
57
|
+
const onAnimationCompleteRef = useRef(triggerAnimation?.onAnimationComplete);
|
|
53
58
|
|
|
54
59
|
const initialTranslateY = useSharedValue(0);
|
|
55
60
|
const initialTranslateX = useSharedValue(0);
|
|
@@ -61,8 +66,22 @@ export const useGestureViewer = <T = any>({
|
|
|
61
66
|
const backdropOpacity = useSharedValue(1);
|
|
62
67
|
const rotation = useSharedValue(0);
|
|
63
68
|
|
|
69
|
+
const triggerScale = useSharedValue(1);
|
|
70
|
+
const triggerTranslateX = useSharedValue(0);
|
|
71
|
+
const triggerTranslateY = useSharedValue(0);
|
|
72
|
+
const triggerOpacity = useSharedValue(1);
|
|
73
|
+
|
|
64
74
|
const dataLength = data?.length || 0;
|
|
65
75
|
|
|
76
|
+
const animationConfig = useMemo(
|
|
77
|
+
() => ({
|
|
78
|
+
duration: triggerAnimation?.duration ?? 300,
|
|
79
|
+
easing: triggerAnimation?.easing ?? Easing.bezier(0.25, 0.1, 0.25, 1.0),
|
|
80
|
+
reduceMotion: triggerAnimation?.reduceMotion,
|
|
81
|
+
}),
|
|
82
|
+
[triggerAnimation?.duration, triggerAnimation?.easing, triggerAnimation?.reduceMotion],
|
|
83
|
+
);
|
|
84
|
+
|
|
66
85
|
const dismissOptions = useMemo(() => {
|
|
67
86
|
return {
|
|
68
87
|
enabled: dismiss?.enabled ?? true,
|
|
@@ -112,6 +131,10 @@ export const useGestureViewer = <T = any>({
|
|
|
112
131
|
[manager],
|
|
113
132
|
);
|
|
114
133
|
|
|
134
|
+
const onAnimationComplete = useCallback(() => {
|
|
135
|
+
onAnimationCompleteRef.current?.();
|
|
136
|
+
}, []);
|
|
137
|
+
|
|
115
138
|
useAnimatedReaction(
|
|
116
139
|
() => scale.value,
|
|
117
140
|
(currentScale, previousScale) => {
|
|
@@ -199,6 +222,105 @@ export const useGestureViewer = <T = any>({
|
|
|
199
222
|
};
|
|
200
223
|
}, [adjustedInitialIndex, translateY, backdropOpacity, translateX, scale, startScale, rotation, scrollTo]);
|
|
201
224
|
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
onAnimationCompleteRef.current = triggerAnimation?.onAnimationComplete;
|
|
227
|
+
}, [triggerAnimation?.onAnimationComplete]);
|
|
228
|
+
|
|
229
|
+
useEffect(() => {
|
|
230
|
+
if (shouldStartTriggerAnimation && triggerRectRef.current) {
|
|
231
|
+
const startX = triggerRectRef.current.x + triggerRectRef.current.width / 2 - screenWidth / 2;
|
|
232
|
+
const startY = triggerRectRef.current.y + triggerRectRef.current.height / 2 - screenHeight / 2;
|
|
233
|
+
const initialScaleFromTrigger = Math.min(
|
|
234
|
+
triggerRectRef.current.width / screenWidth,
|
|
235
|
+
triggerRectRef.current.height / screenHeight,
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
triggerScale.value = initialScaleFromTrigger;
|
|
239
|
+
triggerTranslateX.value = startX;
|
|
240
|
+
triggerTranslateY.value = startY;
|
|
241
|
+
triggerOpacity.value = 0;
|
|
242
|
+
|
|
243
|
+
triggerScale.value = withTiming(1, animationConfig, (finished) => {
|
|
244
|
+
if (finished) {
|
|
245
|
+
runOnJS(onAnimationComplete)();
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
triggerTranslateX.value = withTiming(0, animationConfig);
|
|
249
|
+
triggerTranslateY.value = withTiming(0, animationConfig);
|
|
250
|
+
triggerOpacity.value = withTiming(1, {
|
|
251
|
+
duration: animationConfig.duration / 2,
|
|
252
|
+
easing: animationConfig.easing,
|
|
253
|
+
reduceMotion: animationConfig.reduceMotion,
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
setShouldStartTriggerAnimation(false);
|
|
257
|
+
}
|
|
258
|
+
}, [
|
|
259
|
+
shouldStartTriggerAnimation,
|
|
260
|
+
animationConfig,
|
|
261
|
+
screenWidth,
|
|
262
|
+
screenHeight,
|
|
263
|
+
triggerOpacity,
|
|
264
|
+
triggerScale,
|
|
265
|
+
triggerTranslateX,
|
|
266
|
+
triggerTranslateY,
|
|
267
|
+
onAnimationComplete,
|
|
268
|
+
]);
|
|
269
|
+
|
|
270
|
+
useEffect(() => {
|
|
271
|
+
const node = registry.getTriggerNode(id);
|
|
272
|
+
|
|
273
|
+
if (node && typeof node.measure === 'function') {
|
|
274
|
+
node.measure((_x, _y, width, height, pageX, pageY) => {
|
|
275
|
+
triggerRectRef.current = { x: pageX, y: pageY, width, height };
|
|
276
|
+
triggerOpacity.value = 0;
|
|
277
|
+
setShouldStartTriggerAnimation(true);
|
|
278
|
+
registry.clearTriggerNode(id);
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return () => {
|
|
283
|
+
triggerRectRef.current = null;
|
|
284
|
+
};
|
|
285
|
+
}, [id, triggerOpacity]);
|
|
286
|
+
|
|
287
|
+
const handleDismiss = useCallback(() => {
|
|
288
|
+
onDismissStart?.();
|
|
289
|
+
|
|
290
|
+
if (triggerRectRef.current) {
|
|
291
|
+
const endX = triggerRectRef.current.x + triggerRectRef.current.width / 2 - screenWidth / 2;
|
|
292
|
+
const endY = triggerRectRef.current.y + triggerRectRef.current.height / 2 - screenHeight / 2;
|
|
293
|
+
const endScale = Math.min(
|
|
294
|
+
triggerRectRef.current.width / screenWidth,
|
|
295
|
+
triggerRectRef.current.height / screenHeight,
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
triggerScale.value = withTiming(endScale, animationConfig);
|
|
299
|
+
triggerTranslateX.value = withTiming(endX, animationConfig);
|
|
300
|
+
triggerTranslateY.value = withTiming(endY, animationConfig);
|
|
301
|
+
triggerOpacity.value = withTiming(0, animationConfig, (finished) => {
|
|
302
|
+
if (finished && onDismiss) {
|
|
303
|
+
runOnJS(onDismiss)();
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (onDismiss) {
|
|
310
|
+
runOnJS(onDismiss)();
|
|
311
|
+
}
|
|
312
|
+
}, [
|
|
313
|
+
animationConfig,
|
|
314
|
+
onDismiss,
|
|
315
|
+
onDismissStart,
|
|
316
|
+
screenWidth,
|
|
317
|
+
screenHeight,
|
|
318
|
+
triggerTranslateX,
|
|
319
|
+
triggerScale,
|
|
320
|
+
triggerTranslateY,
|
|
321
|
+
triggerOpacity,
|
|
322
|
+
]);
|
|
323
|
+
|
|
202
324
|
const onMomentumScrollEnd = useCallback(
|
|
203
325
|
(event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
204
326
|
if (!enableHorizontalSwipe) {
|
|
@@ -220,7 +342,7 @@ export const useGestureViewer = <T = any>({
|
|
|
220
342
|
scrollTo(jumpToIndex, false);
|
|
221
343
|
}
|
|
222
344
|
|
|
223
|
-
const currentIndex = manager?.getState()
|
|
345
|
+
const currentIndex = manager?.getState()?.currentIndex;
|
|
224
346
|
|
|
225
347
|
if (realIndex !== currentIndex && realIndex >= 0 && realIndex < dataLength) {
|
|
226
348
|
if (manager) {
|
|
@@ -256,19 +378,21 @@ export const useGestureViewer = <T = any>({
|
|
|
256
378
|
);
|
|
257
379
|
|
|
258
380
|
const dismissGesture = useMemo(() => {
|
|
381
|
+
const canDismiss = !isZoomed && dismissOptions.enabled;
|
|
382
|
+
|
|
259
383
|
return Gesture.Pan()
|
|
260
384
|
.minDistance(10)
|
|
261
385
|
.averageTouches(true)
|
|
262
386
|
.activeCursor('grabbing')
|
|
263
387
|
.activeOffsetY([-10, 10])
|
|
264
388
|
.failOffsetX([-10, 10])
|
|
265
|
-
.enabled(
|
|
389
|
+
.enabled(canDismiss)
|
|
266
390
|
.onUpdate((event) => {
|
|
267
391
|
translateY.value = event.translationY / dismissOptions.resistance;
|
|
268
392
|
})
|
|
269
393
|
.onEnd((event) => {
|
|
270
|
-
if (event.translationY > dismissOptions.threshold
|
|
271
|
-
runOnJS(
|
|
394
|
+
if (canDismiss && event.translationY > dismissOptions.threshold) {
|
|
395
|
+
runOnJS(handleDismiss)();
|
|
272
396
|
return;
|
|
273
397
|
}
|
|
274
398
|
|
|
@@ -280,7 +404,7 @@ export const useGestureViewer = <T = any>({
|
|
|
280
404
|
energyThreshold: 6e-9,
|
|
281
405
|
});
|
|
282
406
|
});
|
|
283
|
-
}, [translateY, dismissOptions
|
|
407
|
+
}, [translateY, dismissOptions, handleDismiss, isZoomed]);
|
|
284
408
|
|
|
285
409
|
const zoomPinchGesture = useMemo(() => {
|
|
286
410
|
return Gesture.Pinch()
|
|
@@ -451,7 +575,12 @@ export const useGestureViewer = <T = any>({
|
|
|
451
575
|
|
|
452
576
|
const animatedStyle = useAnimatedStyle(() => {
|
|
453
577
|
return {
|
|
578
|
+
opacity: triggerOpacity.value,
|
|
454
579
|
transform: [
|
|
580
|
+
{ translateX: triggerTranslateX.value },
|
|
581
|
+
{ translateY: triggerTranslateY.value },
|
|
582
|
+
{ scale: triggerScale.value },
|
|
583
|
+
|
|
455
584
|
{ translateY: translateY.value },
|
|
456
585
|
{ translateX: translateX.value },
|
|
457
586
|
{ scale: scale.value },
|
|
@@ -461,13 +590,15 @@ export const useGestureViewer = <T = any>({
|
|
|
461
590
|
});
|
|
462
591
|
|
|
463
592
|
const backdropStyle = useAnimatedStyle(() => {
|
|
593
|
+
const baseOpacity = triggerOpacity.value;
|
|
594
|
+
|
|
464
595
|
if (!dismissOptions.fadeBackdrop || scale.value !== 1) {
|
|
465
|
-
return { opacity:
|
|
596
|
+
return { opacity: baseOpacity };
|
|
466
597
|
}
|
|
467
598
|
|
|
468
|
-
const
|
|
599
|
+
const dismissOpacity = interpolate(translateY.value, [0, 200], [1, 0], 'clamp');
|
|
469
600
|
|
|
470
|
-
return { opacity };
|
|
601
|
+
return { opacity: baseOpacity * dismissOpacity };
|
|
471
602
|
}, [dismissOptions.fadeBackdrop]);
|
|
472
603
|
|
|
473
604
|
const onScrollBeginDrag = useCallback(() => {
|
|
@@ -476,7 +607,6 @@ export const useGestureViewer = <T = any>({
|
|
|
476
607
|
|
|
477
608
|
return {
|
|
478
609
|
dataLength,
|
|
479
|
-
translateY,
|
|
480
610
|
listRef,
|
|
481
611
|
isZoomed,
|
|
482
612
|
isRotated,
|
|
@@ -485,6 +615,7 @@ export const useGestureViewer = <T = any>({
|
|
|
485
615
|
|
|
486
616
|
onMomentumScrollEnd,
|
|
487
617
|
onScrollBeginDrag,
|
|
618
|
+
handleDismiss,
|
|
488
619
|
|
|
489
620
|
animatedStyle,
|
|
490
621
|
backdropStyle,
|