react-native-gesture-image-viewer 2.0.0-beta.4 → 2.0.0-beta.5
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 +82 -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 +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 +86 -0
- package/src/GestureViewer.tsx +8 -2
- package/src/GestureViewerRegistry.ts +16 -0
- package/src/index.tsx +2 -0
- package/src/types.ts +56 -1
- package/src/useGestureViewer.ts +141 -10
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,
|