react-native-gesture-image-viewer 2.1.3 → 2.1.4
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/lib/module/GestureViewer.js +50 -40
- package/lib/module/GestureViewer.js.map +1 -1
- package/lib/module/GestureViewerManager.js +26 -6
- package/lib/module/GestureViewerManager.js.map +1 -1
- package/lib/module/useGestureViewer.js +67 -86
- package/lib/module/useGestureViewer.js.map +1 -1
- package/lib/module/useGestureViewerPaging.js +68 -0
- package/lib/module/useGestureViewerPaging.js.map +1 -0
- package/lib/module/useGestureViewerPaging.types.js +4 -0
- package/lib/module/useGestureViewerPaging.types.js.map +1 -0
- package/lib/module/useGestureViewerPaging.web.js +241 -0
- package/lib/module/useGestureViewerPaging.web.js.map +1 -0
- package/lib/module/utils/index.js +9 -0
- package/lib/module/utils/index.js.map +1 -1
- package/lib/module/utils/tapZoom.js +34 -0
- package/lib/module/utils/tapZoom.js.map +1 -0
- package/lib/module/utils/webScroll.js +70 -0
- package/lib/module/utils/webScroll.js.map +1 -0
- package/lib/typescript/src/GestureViewer.d.ts.map +1 -1
- package/lib/typescript/src/GestureViewerManager.d.ts +5 -0
- package/lib/typescript/src/GestureViewerManager.d.ts.map +1 -1
- package/lib/typescript/src/useGestureViewer.d.ts +4 -3
- package/lib/typescript/src/useGestureViewer.d.ts.map +1 -1
- package/lib/typescript/src/useGestureViewerPaging.d.ts +3 -0
- package/lib/typescript/src/useGestureViewerPaging.d.ts.map +1 -0
- package/lib/typescript/src/useGestureViewerPaging.types.d.ts +32 -0
- package/lib/typescript/src/useGestureViewerPaging.types.d.ts.map +1 -0
- package/lib/typescript/src/useGestureViewerPaging.web.d.ts +3 -0
- package/lib/typescript/src/useGestureViewerPaging.web.d.ts.map +1 -0
- package/lib/typescript/src/utils/index.d.ts +7 -0
- package/lib/typescript/src/utils/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/tapZoom.d.ts +12 -0
- package/lib/typescript/src/utils/tapZoom.d.ts.map +1 -0
- package/lib/typescript/src/utils/webScroll.d.ts +18 -0
- package/lib/typescript/src/utils/webScroll.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/GestureViewer.tsx +39 -15
- package/src/GestureViewerManager.ts +29 -6
- package/src/useGestureViewer.ts +71 -139
- package/src/useGestureViewerPaging.ts +112 -0
- package/src/useGestureViewerPaging.types.ts +34 -0
- package/src/useGestureViewerPaging.web.ts +390 -0
- package/src/utils/index.ts +17 -1
- package/src/utils/tapZoom.ts +44 -0
- package/src/utils/webScroll.ts +96 -0
package/src/useGestureViewer.ts
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
InteractionManager,
|
|
4
|
-
type NativeScrollEvent,
|
|
5
|
-
type NativeSyntheticEvent,
|
|
6
|
-
useWindowDimensions,
|
|
7
|
-
} from 'react-native';
|
|
2
|
+
import { InteractionManager, Platform, useWindowDimensions } from 'react-native';
|
|
8
3
|
import { Gesture, type GestureType } from 'react-native-gesture-handler';
|
|
9
4
|
import {
|
|
10
5
|
Easing,
|
|
@@ -20,7 +15,9 @@ import { scheduleOnRN } from 'react-native-worklets';
|
|
|
20
15
|
import type GestureViewerManager from './GestureViewerManager';
|
|
21
16
|
import { registry } from './GestureViewerRegistry';
|
|
22
17
|
import type { GestureViewerProps, TriggerRect } from './types';
|
|
23
|
-
import {
|
|
18
|
+
import { useGestureViewerPaging } from './useGestureViewerPaging';
|
|
19
|
+
import { createBoundsConstraint, createScrollAction } from './utils';
|
|
20
|
+
import { applyTapZoomAtPoint } from './utils/tapZoom';
|
|
24
21
|
|
|
25
22
|
type UseGestureViewerProps<ItemT, LC> = Omit<
|
|
26
23
|
GestureViewerProps<ItemT, LC>,
|
|
@@ -131,6 +128,35 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
131
128
|
[width, itemSpacing],
|
|
132
129
|
);
|
|
133
130
|
|
|
131
|
+
const resetTransformState = useCallback(() => {
|
|
132
|
+
translateX.value = withTiming(0);
|
|
133
|
+
translateY.value = withTiming(0);
|
|
134
|
+
initialTranslateX.value = withTiming(0);
|
|
135
|
+
initialTranslateY.value = withTiming(0);
|
|
136
|
+
startScale.value = withTiming(1);
|
|
137
|
+
scale.value = withTiming(1);
|
|
138
|
+
rotation.value = 0;
|
|
139
|
+
}, [initialTranslateX, initialTranslateY, rotation, scale, startScale, translateX, translateY]);
|
|
140
|
+
|
|
141
|
+
const syncCurrentIndex = useCallback(
|
|
142
|
+
(nextIndex: number) => {
|
|
143
|
+
if (!manager || nextIndex < 0 || nextIndex >= dataLength) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const managerCurrentIndex = manager.getState().currentIndex;
|
|
148
|
+
|
|
149
|
+
if (nextIndex === managerCurrentIndex) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
manager.setCurrentIndex(nextIndex);
|
|
154
|
+
manager.notifyStateChange();
|
|
155
|
+
resetTransformState();
|
|
156
|
+
},
|
|
157
|
+
[dataLength, manager, resetTransformState],
|
|
158
|
+
);
|
|
159
|
+
|
|
134
160
|
const emitZoomChange = useCallback(
|
|
135
161
|
(currentScale: number, prevScale: number | null) => {
|
|
136
162
|
if (manager) {
|
|
@@ -200,6 +226,7 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
200
226
|
manager.setWidth(width + itemSpacing);
|
|
201
227
|
manager.setHeight(height);
|
|
202
228
|
manager.setZoomSharedValues(scale, translateX, translateY, maxZoomScale);
|
|
229
|
+
manager.setResetTransformCallback(resetTransformState);
|
|
203
230
|
manager.setRotation(rotation);
|
|
204
231
|
manager.setEnableLoop(enableLoop);
|
|
205
232
|
manager.notifyStateChange();
|
|
@@ -214,6 +241,7 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
214
241
|
enableLoop,
|
|
215
242
|
scale,
|
|
216
243
|
height,
|
|
244
|
+
resetTransformState,
|
|
217
245
|
translateX,
|
|
218
246
|
translateY,
|
|
219
247
|
rotation,
|
|
@@ -257,44 +285,6 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
257
285
|
scrollTo,
|
|
258
286
|
]);
|
|
259
287
|
|
|
260
|
-
useEffect(() => {
|
|
261
|
-
if (
|
|
262
|
-
!autoPlay ||
|
|
263
|
-
!manager ||
|
|
264
|
-
dataLength <= 1 ||
|
|
265
|
-
isZoomed ||
|
|
266
|
-
isRotated ||
|
|
267
|
-
(!enableLoop && currentIndex === dataLength - 1)
|
|
268
|
-
) {
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
const intervalMs = Math.max(250, Math.floor(autoPlayInterval || 0));
|
|
273
|
-
|
|
274
|
-
if (!Number.isFinite(intervalMs)) {
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
const interval = setInterval(() => {
|
|
279
|
-
if (isZoomed || isRotated) {
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
manager.goToNext();
|
|
284
|
-
}, intervalMs);
|
|
285
|
-
|
|
286
|
-
return () => clearInterval(interval);
|
|
287
|
-
}, [
|
|
288
|
-
autoPlay,
|
|
289
|
-
autoPlayInterval,
|
|
290
|
-
manager,
|
|
291
|
-
dataLength,
|
|
292
|
-
currentIndex,
|
|
293
|
-
enableLoop,
|
|
294
|
-
isZoomed,
|
|
295
|
-
isRotated,
|
|
296
|
-
]);
|
|
297
|
-
|
|
298
288
|
useEffect(() => {
|
|
299
289
|
onAnimationCompleteRef.current = triggerAnimation?.onAnimationComplete;
|
|
300
290
|
}, [triggerAnimation?.onAnimationComplete]);
|
|
@@ -394,66 +384,6 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
394
384
|
triggerOpacity,
|
|
395
385
|
]);
|
|
396
386
|
|
|
397
|
-
const onMomentumScrollEnd = useCallback(
|
|
398
|
-
(event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
399
|
-
if (!enableHorizontalSwipe) {
|
|
400
|
-
return;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
const { contentOffset } = event.nativeEvent;
|
|
404
|
-
const scrollIndex = Math.round(contentOffset.x / (width + itemSpacing));
|
|
405
|
-
|
|
406
|
-
const isLoopHandled = manager?.handleMomentumScrollEnd(scrollIndex);
|
|
407
|
-
|
|
408
|
-
if (isLoopHandled) {
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
const { realIndex, needsJump, jumpToIndex } = getLoopAdjustedIndex(
|
|
413
|
-
scrollIndex,
|
|
414
|
-
dataLength,
|
|
415
|
-
enableLoop,
|
|
416
|
-
);
|
|
417
|
-
|
|
418
|
-
if (needsJump && jumpToIndex !== undefined) {
|
|
419
|
-
scrollTo(jumpToIndex, false);
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
const currentIndex = manager?.getState()?.currentIndex;
|
|
423
|
-
|
|
424
|
-
if (realIndex !== currentIndex && realIndex >= 0 && realIndex < dataLength) {
|
|
425
|
-
if (manager) {
|
|
426
|
-
manager.setCurrentIndex(realIndex);
|
|
427
|
-
manager.notifyStateChange();
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
translateX.value = withTiming(0);
|
|
431
|
-
translateY.value = withTiming(0);
|
|
432
|
-
initialTranslateX.value = withTiming(0);
|
|
433
|
-
initialTranslateY.value = withTiming(0);
|
|
434
|
-
startScale.value = withTiming(1);
|
|
435
|
-
scale.value = withTiming(1);
|
|
436
|
-
rotation.value = 0;
|
|
437
|
-
}
|
|
438
|
-
},
|
|
439
|
-
[
|
|
440
|
-
scrollTo,
|
|
441
|
-
manager,
|
|
442
|
-
dataLength,
|
|
443
|
-
width,
|
|
444
|
-
itemSpacing,
|
|
445
|
-
enableHorizontalSwipe,
|
|
446
|
-
enableLoop,
|
|
447
|
-
translateX,
|
|
448
|
-
translateY,
|
|
449
|
-
scale,
|
|
450
|
-
initialTranslateX,
|
|
451
|
-
initialTranslateY,
|
|
452
|
-
startScale,
|
|
453
|
-
rotation,
|
|
454
|
-
],
|
|
455
|
-
);
|
|
456
|
-
|
|
457
387
|
const dismissGesture = useMemo(() => {
|
|
458
388
|
const canDismiss = !isZoomed && dismissOptions.enabled;
|
|
459
389
|
|
|
@@ -644,41 +574,21 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
644
574
|
const doubleTapGesture = useMemo(
|
|
645
575
|
() =>
|
|
646
576
|
Gesture.Tap()
|
|
647
|
-
.enabled(enableDoubleTapZoom)
|
|
577
|
+
.enabled(enableDoubleTapZoom && Platform.OS !== 'web')
|
|
648
578
|
.numberOfTaps(2)
|
|
649
579
|
.onEnd((event) => {
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
translateX
|
|
658
|
-
|
|
659
|
-
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
|
|
660
|
-
});
|
|
661
|
-
translateY.value = withTiming(-centerY * (nextScale - 1), {
|
|
662
|
-
duration: 300,
|
|
663
|
-
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
|
|
664
|
-
});
|
|
665
|
-
} else {
|
|
666
|
-
translateX.value = withTiming(0, {
|
|
667
|
-
duration: 300,
|
|
668
|
-
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
|
|
669
|
-
});
|
|
670
|
-
translateY.value = withTiming(0, {
|
|
671
|
-
duration: 300,
|
|
672
|
-
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
|
|
673
|
-
});
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
scale.value = withTiming(nextScale, {
|
|
677
|
-
duration: 300,
|
|
678
|
-
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
|
|
580
|
+
applyTapZoomAtPoint({
|
|
581
|
+
x: event.x,
|
|
582
|
+
y: event.y,
|
|
583
|
+
width,
|
|
584
|
+
height,
|
|
585
|
+
maxZoomScale,
|
|
586
|
+
scale,
|
|
587
|
+
translateX,
|
|
588
|
+
translateY,
|
|
679
589
|
});
|
|
680
590
|
}),
|
|
681
|
-
[
|
|
591
|
+
[enableDoubleTapZoom, height, maxZoomScale, scale, translateX, translateY, width],
|
|
682
592
|
);
|
|
683
593
|
|
|
684
594
|
const zoomGesture = useMemo(
|
|
@@ -716,9 +626,29 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
716
626
|
return Gesture.Native().requireExternalGestureToFail(dismissGestureRef);
|
|
717
627
|
}, []);
|
|
718
628
|
|
|
719
|
-
const onScrollBeginDrag
|
|
720
|
-
|
|
721
|
-
|
|
629
|
+
const { onMomentumScrollEnd, onScroll, onScrollBeginDrag, onWebDoubleClick } =
|
|
630
|
+
useGestureViewerPaging({
|
|
631
|
+
adjustedInitialIndex,
|
|
632
|
+
autoPlay,
|
|
633
|
+
autoPlayInterval,
|
|
634
|
+
currentIndex,
|
|
635
|
+
dataLength,
|
|
636
|
+
enableDoubleTapZoom,
|
|
637
|
+
enableHorizontalSwipe,
|
|
638
|
+
enableLoop,
|
|
639
|
+
height,
|
|
640
|
+
isRotated,
|
|
641
|
+
isZoomed,
|
|
642
|
+
itemSpacing,
|
|
643
|
+
manager,
|
|
644
|
+
maxZoomScale,
|
|
645
|
+
scale,
|
|
646
|
+
scrollTo,
|
|
647
|
+
syncCurrentIndex,
|
|
648
|
+
translateX,
|
|
649
|
+
translateY,
|
|
650
|
+
width,
|
|
651
|
+
});
|
|
722
652
|
|
|
723
653
|
return {
|
|
724
654
|
animatedStyle,
|
|
@@ -732,7 +662,9 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
732
662
|
isZoomed,
|
|
733
663
|
listRef,
|
|
734
664
|
nativeScrollGesture,
|
|
665
|
+
onWebDoubleClick,
|
|
735
666
|
onMomentumScrollEnd,
|
|
667
|
+
onScroll,
|
|
736
668
|
|
|
737
669
|
onScrollBeginDrag,
|
|
738
670
|
zoomGesture,
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { useCallback, useEffect } from 'react';
|
|
2
|
+
import type { NativeScrollEvent, NativeSyntheticEvent } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import type {
|
|
5
|
+
UseGestureViewerPagingArgs,
|
|
6
|
+
UseGestureViewerPagingResult,
|
|
7
|
+
} from './useGestureViewerPaging.types';
|
|
8
|
+
import { getLoopAdjustedIndex } from './utils';
|
|
9
|
+
|
|
10
|
+
export function useGestureViewerPaging({
|
|
11
|
+
autoPlay,
|
|
12
|
+
autoPlayInterval,
|
|
13
|
+
currentIndex,
|
|
14
|
+
dataLength,
|
|
15
|
+
enableHorizontalSwipe,
|
|
16
|
+
enableLoop,
|
|
17
|
+
isRotated,
|
|
18
|
+
isZoomed,
|
|
19
|
+
itemSpacing,
|
|
20
|
+
manager,
|
|
21
|
+
scrollTo,
|
|
22
|
+
syncCurrentIndex,
|
|
23
|
+
width,
|
|
24
|
+
}: UseGestureViewerPagingArgs): UseGestureViewerPagingResult {
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (
|
|
27
|
+
!autoPlay ||
|
|
28
|
+
!manager ||
|
|
29
|
+
dataLength <= 1 ||
|
|
30
|
+
isZoomed ||
|
|
31
|
+
isRotated ||
|
|
32
|
+
(!enableLoop && currentIndex === dataLength - 1)
|
|
33
|
+
) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const intervalMs = Math.max(250, Math.floor(autoPlayInterval || 0));
|
|
38
|
+
|
|
39
|
+
if (!Number.isFinite(intervalMs)) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const interval = setInterval(() => {
|
|
44
|
+
if (isZoomed || isRotated) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
manager.goToNext();
|
|
49
|
+
}, intervalMs);
|
|
50
|
+
|
|
51
|
+
return () => clearInterval(interval);
|
|
52
|
+
}, [
|
|
53
|
+
autoPlay,
|
|
54
|
+
autoPlayInterval,
|
|
55
|
+
currentIndex,
|
|
56
|
+
dataLength,
|
|
57
|
+
enableLoop,
|
|
58
|
+
isRotated,
|
|
59
|
+
isZoomed,
|
|
60
|
+
manager,
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
const onMomentumScrollEnd = useCallback(
|
|
64
|
+
(event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
65
|
+
if (!enableHorizontalSwipe) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const { contentOffset } = event.nativeEvent;
|
|
70
|
+
const scrollIndex = Math.round(contentOffset.x / (width + itemSpacing));
|
|
71
|
+
|
|
72
|
+
const isLoopHandled = manager?.handleMomentumScrollEnd(scrollIndex);
|
|
73
|
+
|
|
74
|
+
if (isLoopHandled) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const { realIndex, needsJump, jumpToIndex } = getLoopAdjustedIndex(
|
|
79
|
+
scrollIndex,
|
|
80
|
+
dataLength,
|
|
81
|
+
enableLoop,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
if (needsJump && jumpToIndex !== undefined) {
|
|
85
|
+
scrollTo(jumpToIndex, false);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
syncCurrentIndex(realIndex);
|
|
89
|
+
},
|
|
90
|
+
[
|
|
91
|
+
dataLength,
|
|
92
|
+
enableHorizontalSwipe,
|
|
93
|
+
enableLoop,
|
|
94
|
+
itemSpacing,
|
|
95
|
+
manager,
|
|
96
|
+
scrollTo,
|
|
97
|
+
syncCurrentIndex,
|
|
98
|
+
width,
|
|
99
|
+
],
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
const onScrollBeginDrag = useCallback(() => {
|
|
103
|
+
manager?.handleScrollBeginDrag();
|
|
104
|
+
}, [manager]);
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
onMomentumScrollEnd,
|
|
108
|
+
onScroll: undefined,
|
|
109
|
+
onScrollBeginDrag,
|
|
110
|
+
onWebDoubleClick: undefined,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ScrollViewProps } from 'react-native';
|
|
2
|
+
import type { SharedValue } from 'react-native-reanimated';
|
|
3
|
+
|
|
4
|
+
import type GestureViewerManager from './GestureViewerManager';
|
|
5
|
+
|
|
6
|
+
export type UseGestureViewerPagingArgs = {
|
|
7
|
+
adjustedInitialIndex: number;
|
|
8
|
+
autoPlay: boolean;
|
|
9
|
+
autoPlayInterval: number;
|
|
10
|
+
currentIndex: number;
|
|
11
|
+
dataLength: number;
|
|
12
|
+
enableDoubleTapZoom: boolean;
|
|
13
|
+
enableHorizontalSwipe: boolean;
|
|
14
|
+
enableLoop: boolean;
|
|
15
|
+
height: number;
|
|
16
|
+
isRotated: boolean;
|
|
17
|
+
isZoomed: boolean;
|
|
18
|
+
itemSpacing: number;
|
|
19
|
+
manager: GestureViewerManager | null;
|
|
20
|
+
maxZoomScale: number;
|
|
21
|
+
scale: SharedValue<number>;
|
|
22
|
+
scrollTo: (index: number, animated: boolean) => void;
|
|
23
|
+
syncCurrentIndex: (nextIndex: number) => void;
|
|
24
|
+
translateX: SharedValue<number>;
|
|
25
|
+
translateY: SharedValue<number>;
|
|
26
|
+
width: number;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type UseGestureViewerPagingResult = {
|
|
30
|
+
onMomentumScrollEnd?: ScrollViewProps['onMomentumScrollEnd'];
|
|
31
|
+
onScroll?: ScrollViewProps['onScroll'];
|
|
32
|
+
onScrollBeginDrag?: ScrollViewProps['onScrollBeginDrag'];
|
|
33
|
+
onWebDoubleClick?: (event: any) => void;
|
|
34
|
+
};
|