react-native-gesture-image-viewer 1.6.6 → 1.7.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 +39 -18
- package/lib/module/GestureTrigger.js +83 -0
- package/lib/module/GestureTrigger.js.map +1 -0
- package/lib/module/GestureViewer.js +8 -2
- package/lib/module/GestureViewer.js.map +1 -1
- package/lib/module/GestureViewerRegistry.js +15 -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 +100 -11
- 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 +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +56 -1
- 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 +1 -1
- package/src/GestureTrigger.tsx +90 -0
- package/src/GestureViewer.tsx +8 -2
- package/src/GestureViewerRegistry.ts +20 -0
- package/src/index.tsx +1 -0
- package/src/types.ts +56 -3
- package/src/useGestureViewer.ts +138 -11
package/src/useGestureViewer.ts
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
} from 'react-native-reanimated';
|
|
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<
|
|
@@ -34,8 +34,6 @@ export const useGestureViewer = <T = any>({
|
|
|
34
34
|
width: customWidth,
|
|
35
35
|
dismissThreshold = 80,
|
|
36
36
|
resistance = 2,
|
|
37
|
-
// swipeThreshold = 0.5,
|
|
38
|
-
// velocityThreshold = 200,
|
|
39
37
|
animateBackdrop = true,
|
|
40
38
|
enableDismissGesture = true,
|
|
41
39
|
enableSwipeGesture = true,
|
|
@@ -47,6 +45,8 @@ export const useGestureViewer = <T = any>({
|
|
|
47
45
|
itemSpacing = 0,
|
|
48
46
|
useSnap = false,
|
|
49
47
|
id = 'default',
|
|
48
|
+
onDismissStart,
|
|
49
|
+
triggerAnimation,
|
|
50
50
|
}: UseGestureViewerProps<T>) => {
|
|
51
51
|
const { width: screenWidth, height: screenHeight } = useWindowDimensions();
|
|
52
52
|
const width = useSnap ? customWidth || screenWidth : screenWidth;
|
|
@@ -54,11 +54,14 @@ export const useGestureViewer = <T = any>({
|
|
|
54
54
|
const [isZoomed, setIsZoomed] = useState(false);
|
|
55
55
|
const [isRotated, setIsRotated] = useState(false);
|
|
56
56
|
const [manager, setManager] = useState<GestureViewerManager | null>(null);
|
|
57
|
+
const [shouldStartTriggerAnimation, setShouldStartTriggerAnimation] = useState(false);
|
|
57
58
|
|
|
58
59
|
const listRef = useRef<any>(null);
|
|
59
60
|
const unsubscribeRef = useRef<(() => void) | null>(null);
|
|
60
61
|
const onIndexChangeRef = useRef<((index: number) => void) | null>(null);
|
|
61
62
|
const lastEmittedIndexRef = useRef<number>(null);
|
|
63
|
+
const triggerRectRef = useRef<TriggerRect | null>(null);
|
|
64
|
+
const onAnimationCompleteRef = useRef(triggerAnimation?.onAnimationComplete);
|
|
62
65
|
|
|
63
66
|
const initialTranslateY = useSharedValue(0);
|
|
64
67
|
const initialTranslateX = useSharedValue(0);
|
|
@@ -70,8 +73,22 @@ export const useGestureViewer = <T = any>({
|
|
|
70
73
|
const backdropOpacity = useSharedValue(1);
|
|
71
74
|
const rotation = useSharedValue(0);
|
|
72
75
|
|
|
76
|
+
const triggerScale = useSharedValue(1);
|
|
77
|
+
const triggerTranslateX = useSharedValue(0);
|
|
78
|
+
const triggerTranslateY = useSharedValue(0);
|
|
79
|
+
const triggerOpacity = useSharedValue(1);
|
|
80
|
+
|
|
73
81
|
const dataLength = data?.length || 0;
|
|
74
82
|
|
|
83
|
+
const animationConfig = useMemo(
|
|
84
|
+
() => ({
|
|
85
|
+
duration: triggerAnimation?.duration ?? 300,
|
|
86
|
+
easing: triggerAnimation?.easing ?? Easing.bezier(0.25, 0.1, 0.25, 1.0),
|
|
87
|
+
reduceMotion: triggerAnimation?.reduceMotion,
|
|
88
|
+
}),
|
|
89
|
+
[triggerAnimation?.duration, triggerAnimation?.easing, triggerAnimation?.reduceMotion],
|
|
90
|
+
);
|
|
91
|
+
|
|
75
92
|
const adjustedInitialIndex = useMemo(() => {
|
|
76
93
|
if (enableLoop && data.length > 1) {
|
|
77
94
|
return initialIndex + 1;
|
|
@@ -85,6 +102,10 @@ export const useGestureViewer = <T = any>({
|
|
|
85
102
|
[width, screenHeight],
|
|
86
103
|
);
|
|
87
104
|
|
|
105
|
+
const onAnimationComplete = useCallback(() => {
|
|
106
|
+
onAnimationCompleteRef.current?.();
|
|
107
|
+
}, []);
|
|
108
|
+
|
|
88
109
|
const scrollTo = useCallback(
|
|
89
110
|
(index: number, animated: boolean) => {
|
|
90
111
|
const scrollAction = createScrollAction(listRef.current, width + itemSpacing);
|
|
@@ -140,6 +161,68 @@ export const useGestureViewer = <T = any>({
|
|
|
140
161
|
};
|
|
141
162
|
}, []);
|
|
142
163
|
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
onAnimationCompleteRef.current = triggerAnimation?.onAnimationComplete;
|
|
166
|
+
}, [triggerAnimation?.onAnimationComplete]);
|
|
167
|
+
|
|
168
|
+
useEffect(() => {
|
|
169
|
+
if (shouldStartTriggerAnimation && triggerRectRef.current) {
|
|
170
|
+
const startX = triggerRectRef.current.x + triggerRectRef.current.width / 2 - screenWidth / 2;
|
|
171
|
+
const startY = triggerRectRef.current.y + triggerRectRef.current.height / 2 - screenHeight / 2;
|
|
172
|
+
const initialScaleFromTrigger = Math.min(
|
|
173
|
+
triggerRectRef.current.width / screenWidth,
|
|
174
|
+
triggerRectRef.current.height / screenHeight,
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
triggerScale.value = initialScaleFromTrigger;
|
|
178
|
+
triggerTranslateX.value = startX;
|
|
179
|
+
triggerTranslateY.value = startY;
|
|
180
|
+
triggerOpacity.value = 0;
|
|
181
|
+
|
|
182
|
+
triggerScale.value = withTiming(1, animationConfig, (finished) => {
|
|
183
|
+
if (finished) {
|
|
184
|
+
runOnJS(onAnimationComplete)();
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
triggerTranslateX.value = withTiming(0, animationConfig);
|
|
188
|
+
triggerTranslateY.value = withTiming(0, animationConfig);
|
|
189
|
+
triggerOpacity.value = withTiming(1, {
|
|
190
|
+
duration: animationConfig.duration / 2,
|
|
191
|
+
easing: animationConfig.easing,
|
|
192
|
+
reduceMotion: animationConfig.reduceMotion,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
setShouldStartTriggerAnimation(false);
|
|
196
|
+
}
|
|
197
|
+
}, [
|
|
198
|
+
shouldStartTriggerAnimation,
|
|
199
|
+
animationConfig,
|
|
200
|
+
screenWidth,
|
|
201
|
+
screenHeight,
|
|
202
|
+
triggerOpacity,
|
|
203
|
+
triggerScale,
|
|
204
|
+
triggerTranslateX,
|
|
205
|
+
triggerTranslateY,
|
|
206
|
+
onAnimationComplete,
|
|
207
|
+
]);
|
|
208
|
+
|
|
209
|
+
useEffect(() => {
|
|
210
|
+
const node = registry.getTriggerNode(id);
|
|
211
|
+
|
|
212
|
+
if (node && typeof node.measure === 'function') {
|
|
213
|
+
node.measure((_x, _y, width, height, pageX, pageY) => {
|
|
214
|
+
triggerRectRef.current = { x: pageX, y: pageY, width, height };
|
|
215
|
+
triggerOpacity.value = 0;
|
|
216
|
+
setShouldStartTriggerAnimation(true);
|
|
217
|
+
registry.clearTriggerNode(id);
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return () => {
|
|
222
|
+
triggerRectRef.current = null;
|
|
223
|
+
};
|
|
224
|
+
}, [id, triggerOpacity]);
|
|
225
|
+
|
|
143
226
|
useEffect(() => {
|
|
144
227
|
const handleManagerChange = (manager: GestureViewerManager | null) => {
|
|
145
228
|
unsubscribeRef.current?.();
|
|
@@ -227,6 +310,43 @@ export const useGestureViewer = <T = any>({
|
|
|
227
310
|
};
|
|
228
311
|
}, [adjustedInitialIndex, translateY, backdropOpacity, translateX, scale, startScale, rotation, scrollTo]);
|
|
229
312
|
|
|
313
|
+
const handleDismiss = useCallback(() => {
|
|
314
|
+
onDismissStart?.();
|
|
315
|
+
|
|
316
|
+
if (triggerRectRef.current) {
|
|
317
|
+
const endX = triggerRectRef.current.x + triggerRectRef.current.width / 2 - screenWidth / 2;
|
|
318
|
+
const endY = triggerRectRef.current.y + triggerRectRef.current.height / 2 - screenHeight / 2;
|
|
319
|
+
const endScale = Math.min(
|
|
320
|
+
triggerRectRef.current.width / screenWidth,
|
|
321
|
+
triggerRectRef.current.height / screenHeight,
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
triggerScale.value = withTiming(endScale, animationConfig);
|
|
325
|
+
triggerTranslateX.value = withTiming(endX, animationConfig);
|
|
326
|
+
triggerTranslateY.value = withTiming(endY, animationConfig);
|
|
327
|
+
triggerOpacity.value = withTiming(0, animationConfig, (finished) => {
|
|
328
|
+
if (finished && onDismiss) {
|
|
329
|
+
runOnJS(onDismiss)();
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (onDismiss) {
|
|
336
|
+
runOnJS(onDismiss)();
|
|
337
|
+
}
|
|
338
|
+
}, [
|
|
339
|
+
animationConfig,
|
|
340
|
+
onDismiss,
|
|
341
|
+
onDismissStart,
|
|
342
|
+
screenWidth,
|
|
343
|
+
screenHeight,
|
|
344
|
+
triggerTranslateX,
|
|
345
|
+
triggerScale,
|
|
346
|
+
triggerTranslateY,
|
|
347
|
+
triggerOpacity,
|
|
348
|
+
]);
|
|
349
|
+
|
|
230
350
|
const onMomentumScrollEnd = useCallback(
|
|
231
351
|
(event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
232
352
|
if (!enableSwipeGesture) {
|
|
@@ -248,7 +368,7 @@ export const useGestureViewer = <T = any>({
|
|
|
248
368
|
scrollTo(jumpToIndex, false);
|
|
249
369
|
}
|
|
250
370
|
|
|
251
|
-
const currentIndex = manager?.getState()
|
|
371
|
+
const currentIndex = manager?.getState().currentIndex;
|
|
252
372
|
|
|
253
373
|
if (realIndex !== currentIndex && realIndex >= 0 && realIndex < dataLength) {
|
|
254
374
|
if (manager) {
|
|
@@ -297,8 +417,8 @@ export const useGestureViewer = <T = any>({
|
|
|
297
417
|
translateY.value = event.translationY / resistance;
|
|
298
418
|
})
|
|
299
419
|
.onEnd((event) => {
|
|
300
|
-
if (canDismiss && event.translationY > dismissThreshold &&
|
|
301
|
-
runOnJS(
|
|
420
|
+
if (canDismiss && event.translationY > dismissThreshold && handleDismiss) {
|
|
421
|
+
runOnJS(handleDismiss)();
|
|
302
422
|
return;
|
|
303
423
|
}
|
|
304
424
|
|
|
@@ -307,7 +427,7 @@ export const useGestureViewer = <T = any>({
|
|
|
307
427
|
stiffness: 150,
|
|
308
428
|
});
|
|
309
429
|
});
|
|
310
|
-
}, [translateY, dismissThreshold, enableDismissGesture,
|
|
430
|
+
}, [translateY, dismissThreshold, enableDismissGesture, handleDismiss, resistance, isZoomed]);
|
|
311
431
|
|
|
312
432
|
const zoomPinchGesture = useMemo(() => {
|
|
313
433
|
return Gesture.Pinch()
|
|
@@ -478,7 +598,12 @@ export const useGestureViewer = <T = any>({
|
|
|
478
598
|
|
|
479
599
|
const animatedStyle = useAnimatedStyle(() => {
|
|
480
600
|
return {
|
|
601
|
+
opacity: triggerOpacity.value,
|
|
481
602
|
transform: [
|
|
603
|
+
{ translateX: triggerTranslateX.value },
|
|
604
|
+
{ translateY: triggerTranslateY.value },
|
|
605
|
+
{ scale: triggerScale.value },
|
|
606
|
+
|
|
482
607
|
{ translateY: translateY.value },
|
|
483
608
|
{ translateX: translateX.value },
|
|
484
609
|
{ scale: scale.value },
|
|
@@ -488,13 +613,15 @@ export const useGestureViewer = <T = any>({
|
|
|
488
613
|
});
|
|
489
614
|
|
|
490
615
|
const backdropStyle = useAnimatedStyle(() => {
|
|
616
|
+
const baseOpacity = triggerOpacity.value;
|
|
617
|
+
|
|
491
618
|
if (!animateBackdrop || scale.value !== 1) {
|
|
492
|
-
return { opacity:
|
|
619
|
+
return { opacity: baseOpacity };
|
|
493
620
|
}
|
|
494
621
|
|
|
495
|
-
const
|
|
622
|
+
const dismissOpacity = interpolate(translateY.value, [0, 200], [1, 0], 'clamp');
|
|
496
623
|
|
|
497
|
-
return { opacity };
|
|
624
|
+
return { opacity: dismissOpacity * baseOpacity };
|
|
498
625
|
}, [animateBackdrop]);
|
|
499
626
|
|
|
500
627
|
const onScrollBeginDrag = useCallback(() => {
|
|
@@ -503,7 +630,6 @@ export const useGestureViewer = <T = any>({
|
|
|
503
630
|
|
|
504
631
|
return {
|
|
505
632
|
dataLength,
|
|
506
|
-
translateY,
|
|
507
633
|
listRef,
|
|
508
634
|
isZoomed,
|
|
509
635
|
isRotated,
|
|
@@ -512,6 +638,7 @@ export const useGestureViewer = <T = any>({
|
|
|
512
638
|
|
|
513
639
|
onMomentumScrollEnd,
|
|
514
640
|
onScrollBeginDrag,
|
|
641
|
+
handleDismiss,
|
|
515
642
|
|
|
516
643
|
animatedStyle,
|
|
517
644
|
backdropStyle,
|