react-native-gesture-image-viewer 2.1.1 → 2.1.3
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 +1 -1
- package/lib/module/GestureTrigger.js.map +1 -1
- package/lib/module/GestureViewer.js +37 -30
- package/lib/module/GestureViewer.js.map +1 -1
- package/lib/module/GestureViewerManager.js.map +1 -1
- package/lib/module/GestureViewerRegistry.js.map +1 -1
- package/lib/module/WebPagingFixStyle.js.map +1 -1
- package/lib/module/useGestureViewer.js +172 -169
- package/lib/module/useGestureViewer.js.map +1 -1
- package/lib/module/useGestureViewerController.js.map +1 -1
- package/lib/module/useGestureViewerEvent.js.map +1 -1
- package/lib/module/useGestureViewerState.js.map +1 -1
- package/lib/module/utils/index.js +14 -16
- package/lib/module/utils/index.js.map +1 -1
- package/lib/typescript/src/GestureTrigger.d.ts +1 -1
- package/lib/typescript/src/GestureTrigger.d.ts.map +1 -1
- package/lib/typescript/src/GestureViewer.d.ts.map +1 -1
- package/lib/typescript/src/GestureViewerManager.d.ts.map +1 -1
- package/lib/typescript/src/GestureViewerRegistry.d.ts.map +1 -1
- package/lib/typescript/src/WebPagingFixStyle.d.ts.map +1 -1
- 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.map +1 -1
- package/lib/typescript/src/useGestureViewerEvent.d.ts.map +1 -1
- package/lib/typescript/src/useGestureViewerState.d.ts.map +1 -1
- package/lib/typescript/src/utils/index.d.ts +1 -1
- package/lib/typescript/src/utils/index.d.ts.map +1 -1
- package/package.json +86 -80
- package/src/GestureTrigger.tsx +9 -2
- package/src/GestureViewer.tsx +46 -21
- package/src/GestureViewerManager.ts +33 -6
- package/src/GestureViewerRegistry.ts +1 -0
- package/src/WebPagingFixStyle.tsx +1 -0
- package/src/types.ts +44 -32
- package/src/useGestureViewer.ts +272 -213
- package/src/useGestureViewerController.ts +1 -0
- package/src/useGestureViewerEvent.ts +5 -1
- package/src/useGestureViewerState.ts +1 -0
- package/src/utils/index.ts +31 -15
package/src/useGestureViewer.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
type NativeSyntheticEvent,
|
|
6
6
|
useWindowDimensions,
|
|
7
7
|
} from 'react-native';
|
|
8
|
-
import { Gesture } from 'react-native-gesture-handler';
|
|
8
|
+
import { Gesture, type GestureType } from 'react-native-gesture-handler';
|
|
9
9
|
import {
|
|
10
10
|
Easing,
|
|
11
11
|
interpolate,
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
withTiming,
|
|
17
17
|
} from 'react-native-reanimated';
|
|
18
18
|
import { scheduleOnRN } from 'react-native-worklets';
|
|
19
|
+
|
|
19
20
|
import type GestureViewerManager from './GestureViewerManager';
|
|
20
21
|
import { registry } from './GestureViewerRegistry';
|
|
21
22
|
import type { GestureViewerProps, TriggerRect } from './types';
|
|
@@ -56,6 +57,8 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
56
57
|
const width = customWidth || screenWidth;
|
|
57
58
|
const height = customHeight || screenHeight;
|
|
58
59
|
|
|
60
|
+
const dismissGestureRef = useRef<GestureType>(undefined);
|
|
61
|
+
|
|
59
62
|
const [isZoomed, setIsZoomed] = useState(false);
|
|
60
63
|
const [isRotated, setIsRotated] = useState(false);
|
|
61
64
|
const [isPinching, setIsPinching] = useState(false);
|
|
@@ -82,25 +85,29 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
82
85
|
const triggerTranslateY = useSharedValue(0);
|
|
83
86
|
const triggerOpacity = useSharedValue(1);
|
|
84
87
|
|
|
88
|
+
const lastFocalX = useSharedValue(0);
|
|
89
|
+
const lastFocalY = useSharedValue(0);
|
|
90
|
+
|
|
85
91
|
const dataLength = data?.length || 0;
|
|
86
92
|
|
|
87
93
|
const animationConfig = useMemo(
|
|
88
94
|
() => ({
|
|
89
95
|
duration: triggerAnimation?.duration ?? 300,
|
|
90
|
-
easing: triggerAnimation?.easing ?? Easing.bezier(0.25, 0.1, 0.25, 1
|
|
96
|
+
easing: triggerAnimation?.easing ?? Easing.bezier(0.25, 0.1, 0.25, 1),
|
|
91
97
|
reduceMotion: triggerAnimation?.reduceMotion,
|
|
92
98
|
}),
|
|
93
99
|
[triggerAnimation?.duration, triggerAnimation?.easing, triggerAnimation?.reduceMotion],
|
|
94
100
|
);
|
|
95
101
|
|
|
96
|
-
const dismissOptions = useMemo(
|
|
97
|
-
|
|
102
|
+
const dismissOptions = useMemo(
|
|
103
|
+
() => ({
|
|
98
104
|
enabled: dismiss?.enabled ?? true,
|
|
99
|
-
threshold: dismiss?.threshold ?? 80,
|
|
100
|
-
resistance: dismiss?.resistance ?? 2,
|
|
101
105
|
fadeBackdrop: dismiss?.fadeBackdrop ?? true,
|
|
102
|
-
|
|
103
|
-
|
|
106
|
+
resistance: dismiss?.resistance ?? 2,
|
|
107
|
+
threshold: dismiss?.threshold ?? 80,
|
|
108
|
+
}),
|
|
109
|
+
[dismiss?.enabled, dismiss?.threshold, dismiss?.resistance, dismiss?.fadeBackdrop],
|
|
110
|
+
);
|
|
104
111
|
|
|
105
112
|
const adjustedInitialIndex = useMemo(() => {
|
|
106
113
|
if (enableLoop && dataLength > 1) {
|
|
@@ -110,7 +117,10 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
110
117
|
return initialIndex;
|
|
111
118
|
}, [enableLoop, dataLength, initialIndex]);
|
|
112
119
|
|
|
113
|
-
const constrainTranslation = useMemo(
|
|
120
|
+
const constrainTranslation = useMemo(
|
|
121
|
+
() => createBoundsConstraint({ height, width }),
|
|
122
|
+
[width, height],
|
|
123
|
+
);
|
|
114
124
|
|
|
115
125
|
const scrollTo = useCallback(
|
|
116
126
|
(index: number, animated: boolean) => {
|
|
@@ -236,7 +246,16 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
236
246
|
return () => {
|
|
237
247
|
runAfterInteractions?.cancel();
|
|
238
248
|
};
|
|
239
|
-
}, [
|
|
249
|
+
}, [
|
|
250
|
+
adjustedInitialIndex,
|
|
251
|
+
translateY,
|
|
252
|
+
backdropOpacity,
|
|
253
|
+
translateX,
|
|
254
|
+
scale,
|
|
255
|
+
startScale,
|
|
256
|
+
rotation,
|
|
257
|
+
scrollTo,
|
|
258
|
+
]);
|
|
240
259
|
|
|
241
260
|
useEffect(() => {
|
|
242
261
|
if (
|
|
@@ -265,7 +284,16 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
265
284
|
}, intervalMs);
|
|
266
285
|
|
|
267
286
|
return () => clearInterval(interval);
|
|
268
|
-
}, [
|
|
287
|
+
}, [
|
|
288
|
+
autoPlay,
|
|
289
|
+
autoPlayInterval,
|
|
290
|
+
manager,
|
|
291
|
+
dataLength,
|
|
292
|
+
currentIndex,
|
|
293
|
+
enableLoop,
|
|
294
|
+
isZoomed,
|
|
295
|
+
isRotated,
|
|
296
|
+
]);
|
|
269
297
|
|
|
270
298
|
useEffect(() => {
|
|
271
299
|
onAnimationCompleteRef.current = triggerAnimation?.onAnimationComplete;
|
|
@@ -317,7 +345,7 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
317
345
|
|
|
318
346
|
if (node && typeof node.measure === 'function') {
|
|
319
347
|
node.measure((_x, _y, width, height, pageX, pageY) => {
|
|
320
|
-
triggerRectRef.current = { x: pageX, y: pageY
|
|
348
|
+
triggerRectRef.current = { height, width, x: pageX, y: pageY };
|
|
321
349
|
triggerOpacity.value = 0;
|
|
322
350
|
setShouldStartTriggerAnimation(true);
|
|
323
351
|
registry.clearTriggerNode(id);
|
|
@@ -335,7 +363,10 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
335
363
|
if (triggerRectRef.current) {
|
|
336
364
|
const endX = triggerRectRef.current.x + triggerRectRef.current.width / 2 - width / 2;
|
|
337
365
|
const endY = triggerRectRef.current.y + triggerRectRef.current.height / 2 - height / 2;
|
|
338
|
-
const endScale = Math.min(
|
|
366
|
+
const endScale = Math.min(
|
|
367
|
+
triggerRectRef.current.width / width,
|
|
368
|
+
triggerRectRef.current.height / height,
|
|
369
|
+
);
|
|
339
370
|
|
|
340
371
|
triggerScale.value = withTiming(endScale, animationConfig);
|
|
341
372
|
triggerTranslateX.value = withTiming(endX, animationConfig);
|
|
@@ -369,7 +400,7 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
369
400
|
return;
|
|
370
401
|
}
|
|
371
402
|
|
|
372
|
-
const contentOffset = event.nativeEvent
|
|
403
|
+
const { contentOffset } = event.nativeEvent;
|
|
373
404
|
const scrollIndex = Math.round(contentOffset.x / (width + itemSpacing));
|
|
374
405
|
|
|
375
406
|
const isLoopHandled = manager?.handleMomentumScrollEnd(scrollIndex);
|
|
@@ -378,7 +409,11 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
378
409
|
return;
|
|
379
410
|
}
|
|
380
411
|
|
|
381
|
-
const { realIndex, needsJump, jumpToIndex } = getLoopAdjustedIndex(
|
|
412
|
+
const { realIndex, needsJump, jumpToIndex } = getLoopAdjustedIndex(
|
|
413
|
+
scrollIndex,
|
|
414
|
+
dataLength,
|
|
415
|
+
enableLoop,
|
|
416
|
+
);
|
|
382
417
|
|
|
383
418
|
if (needsJump && jumpToIndex !== undefined) {
|
|
384
419
|
scrollTo(jumpToIndex, false);
|
|
@@ -428,6 +463,7 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
428
463
|
.activeCursor('grabbing')
|
|
429
464
|
.activeOffsetY([-10, 10])
|
|
430
465
|
.failOffsetX([-10, 10])
|
|
466
|
+
.withRef(dismissGestureRef)
|
|
431
467
|
.enabled(canDismiss)
|
|
432
468
|
.onUpdate((event) => {
|
|
433
469
|
translateY.value = event.translationY / dismissOptions.resistance;
|
|
@@ -439,212 +475,230 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
439
475
|
}
|
|
440
476
|
|
|
441
477
|
translateY.value = withSpring(0, {
|
|
442
|
-
stiffness: 600,
|
|
443
478
|
damping: 50,
|
|
479
|
+
energyThreshold: 6e-9,
|
|
444
480
|
mass: 4,
|
|
445
481
|
overshootClamping: false,
|
|
446
|
-
|
|
482
|
+
stiffness: 600,
|
|
447
483
|
});
|
|
448
484
|
});
|
|
449
485
|
}, [translateY, dismissOptions, handleDismiss, isZoomed]);
|
|
450
486
|
|
|
451
|
-
const zoomPinchGesture = useMemo(
|
|
452
|
-
|
|
453
|
-
.
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
487
|
+
const zoomPinchGesture = useMemo(
|
|
488
|
+
() =>
|
|
489
|
+
Gesture.Pinch()
|
|
490
|
+
.enabled(enablePinchZoom)
|
|
491
|
+
.onTouchesDown((event) => {
|
|
492
|
+
if (event.numberOfTouches === 2) {
|
|
493
|
+
scheduleOnRN(setIsPinching, true);
|
|
494
|
+
}
|
|
495
|
+
})
|
|
496
|
+
.onBegin((event) => {
|
|
497
|
+
startScale.value = scale.value;
|
|
498
|
+
initialTranslateX.value = translateX.value;
|
|
499
|
+
initialTranslateY.value = translateY.value;
|
|
500
|
+
lastFocalX.value = event.focalX;
|
|
501
|
+
lastFocalY.value = event.focalY;
|
|
502
|
+
})
|
|
503
|
+
.onUpdate((event) => {
|
|
504
|
+
const newScale = startScale.value * event.scale;
|
|
505
|
+
|
|
506
|
+
scale.value = newScale;
|
|
507
|
+
|
|
508
|
+
if (newScale <= 1) {
|
|
509
|
+
translateX.value = withTiming(0);
|
|
510
|
+
translateY.value = withTiming(0);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const focalDeltaX = Math.abs(event.focalX - lastFocalX.value);
|
|
515
|
+
const focalDeltaY = Math.abs(event.focalY - lastFocalY.value);
|
|
516
|
+
const threshold = 50;
|
|
517
|
+
|
|
518
|
+
if (focalDeltaX < threshold && focalDeltaY < threshold) {
|
|
519
|
+
lastFocalX.value = event.focalX;
|
|
520
|
+
lastFocalY.value = event.focalY;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const deltaScale = newScale - startScale.value;
|
|
524
|
+
const centerX = lastFocalX.value - width / 2;
|
|
525
|
+
const centerY = lastFocalY.value - height / 2;
|
|
526
|
+
|
|
527
|
+
// NOTE 새로운 이동값 = 기존 이동값 - (중심점 거리 × 스케일 변화량) / 원래 스케일 (중심점이 화면 중심에서 멀수록, 확대 배율이 클수록 더 많이 이동)
|
|
528
|
+
const newTranslateX = initialTranslateX.value - (centerX * deltaScale) / startScale.value;
|
|
529
|
+
const newTranslateY = initialTranslateY.value - (centerY * deltaScale) / startScale.value;
|
|
530
|
+
|
|
531
|
+
const { translateX: constrainedTranslateX, translateY: constrainedTranslateY } =
|
|
532
|
+
constrainTranslation({
|
|
533
|
+
scale: newScale,
|
|
534
|
+
translateX: newTranslateX,
|
|
535
|
+
translateY: newTranslateY,
|
|
536
|
+
});
|
|
466
537
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
scale
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
538
|
+
translateX.value = constrainedTranslateX;
|
|
539
|
+
translateY.value = constrainedTranslateY;
|
|
540
|
+
})
|
|
541
|
+
.onEnd(() => {
|
|
542
|
+
if (scale.value > maxZoomScale) {
|
|
543
|
+
scale.value = withTiming(maxZoomScale, {
|
|
544
|
+
duration: 300,
|
|
545
|
+
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
const { translateX: constrainedTranslateX, translateY: constrainedTranslateY } =
|
|
549
|
+
constrainTranslation({
|
|
550
|
+
scale: maxZoomScale,
|
|
551
|
+
translateX: translateX.value,
|
|
552
|
+
translateY: translateY.value,
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
translateX.value = withTiming(constrainedTranslateX);
|
|
556
|
+
translateY.value = withTiming(constrainedTranslateY);
|
|
557
|
+
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (scale.value < 1) {
|
|
562
|
+
scale.value = withTiming(1, {
|
|
563
|
+
duration: 300,
|
|
564
|
+
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
|
|
565
|
+
});
|
|
566
|
+
translateX.value = withTiming(0);
|
|
567
|
+
translateY.value = withTiming(0);
|
|
568
|
+
initialTranslateX.value = withTiming(0);
|
|
569
|
+
initialTranslateY.value = withTiming(0);
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const { translateX: constrainedTranslateX, translateY: constrainedTranslateY } =
|
|
574
|
+
constrainTranslation({
|
|
575
|
+
scale: scale.value,
|
|
576
|
+
translateX: translateX.value,
|
|
577
|
+
translateY: translateY.value,
|
|
578
|
+
});
|
|
508
579
|
|
|
509
580
|
translateX.value = withTiming(constrainedTranslateX);
|
|
510
581
|
translateY.value = withTiming(constrainedTranslateY);
|
|
582
|
+
})
|
|
583
|
+
.onTouchesUp(() => {
|
|
584
|
+
scheduleOnRN(setIsPinching, false);
|
|
585
|
+
})
|
|
586
|
+
.onFinalize(() => {
|
|
587
|
+
scheduleOnRN(setIsPinching, false);
|
|
588
|
+
}),
|
|
589
|
+
[
|
|
590
|
+
scale,
|
|
591
|
+
enablePinchZoom,
|
|
592
|
+
maxZoomScale,
|
|
593
|
+
translateX,
|
|
594
|
+
translateY,
|
|
595
|
+
startScale,
|
|
596
|
+
initialTranslateX,
|
|
597
|
+
initialTranslateY,
|
|
598
|
+
width,
|
|
599
|
+
height,
|
|
600
|
+
constrainTranslation,
|
|
601
|
+
lastFocalX,
|
|
602
|
+
lastFocalY,
|
|
603
|
+
],
|
|
604
|
+
);
|
|
511
605
|
|
|
512
|
-
|
|
513
|
-
|
|
606
|
+
const zoomPanGesture = useMemo(
|
|
607
|
+
() =>
|
|
608
|
+
Gesture.Pan()
|
|
609
|
+
.enabled(enablePanWhenZoomed && isZoomed)
|
|
610
|
+
.activeCursor('grabbing')
|
|
611
|
+
.averageTouches(true)
|
|
612
|
+
.onBegin(() => {
|
|
613
|
+
initialTranslateX.value = translateX.value;
|
|
614
|
+
initialTranslateY.value = translateY.value;
|
|
615
|
+
})
|
|
616
|
+
.onUpdate((event) => {
|
|
617
|
+
if (scale.value > 1) {
|
|
618
|
+
const newTranslateX = initialTranslateX.value + event.translationX;
|
|
619
|
+
const newTranslateY = initialTranslateY.value + event.translationY;
|
|
620
|
+
|
|
621
|
+
const { translateX: constrainedTranslateX, translateY: constrainedTranslateY } =
|
|
622
|
+
constrainTranslation({
|
|
623
|
+
scale: scale.value,
|
|
624
|
+
translateX: newTranslateX,
|
|
625
|
+
translateY: newTranslateY,
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
translateX.value = constrainedTranslateX;
|
|
629
|
+
translateY.value = constrainedTranslateY;
|
|
630
|
+
}
|
|
631
|
+
}),
|
|
632
|
+
[
|
|
633
|
+
translateX,
|
|
634
|
+
translateY,
|
|
635
|
+
enablePanWhenZoomed,
|
|
636
|
+
isZoomed,
|
|
637
|
+
scale,
|
|
638
|
+
initialTranslateX,
|
|
639
|
+
initialTranslateY,
|
|
640
|
+
constrainTranslation,
|
|
641
|
+
],
|
|
642
|
+
);
|
|
514
643
|
|
|
515
|
-
|
|
516
|
-
|
|
644
|
+
const doubleTapGesture = useMemo(
|
|
645
|
+
() =>
|
|
646
|
+
Gesture.Tap()
|
|
647
|
+
.enabled(enableDoubleTapZoom)
|
|
648
|
+
.numberOfTaps(2)
|
|
649
|
+
.onEnd((event) => {
|
|
650
|
+
const nextScale = scale.value > 1 ? 1 : maxZoomScale;
|
|
651
|
+
|
|
652
|
+
if (nextScale > 1) {
|
|
653
|
+
const centerX = event.x - width / 2;
|
|
654
|
+
const centerY = event.y - height / 2;
|
|
655
|
+
|
|
656
|
+
// NOTE 확대로 밀려난 거리만큼 반대로 이동해서 탭 지점을 제자리에 유지
|
|
657
|
+
translateX.value = withTiming(-centerX * (nextScale - 1), {
|
|
658
|
+
duration: 300,
|
|
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, {
|
|
517
677
|
duration: 300,
|
|
518
|
-
easing: Easing.bezier(0.25, 0.1, 0.25, 1
|
|
678
|
+
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
|
|
519
679
|
});
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
initialTranslateY.value = withTiming(0);
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
const { translateX: constrainedTranslateX, translateY: constrainedTranslateY } = constrainTranslation({
|
|
528
|
-
translateX: translateX.value,
|
|
529
|
-
translateY: translateY.value,
|
|
530
|
-
scale: scale.value,
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
translateX.value = withTiming(constrainedTranslateX);
|
|
534
|
-
translateY.value = withTiming(constrainedTranslateY);
|
|
535
|
-
})
|
|
536
|
-
.onTouchesUp(() => {
|
|
537
|
-
scheduleOnRN(setIsPinching, false);
|
|
538
|
-
})
|
|
539
|
-
.onFinalize(() => {
|
|
540
|
-
scheduleOnRN(setIsPinching, false);
|
|
541
|
-
});
|
|
542
|
-
}, [
|
|
543
|
-
scale,
|
|
544
|
-
enablePinchZoom,
|
|
545
|
-
maxZoomScale,
|
|
546
|
-
translateX,
|
|
547
|
-
translateY,
|
|
548
|
-
startScale,
|
|
549
|
-
initialTranslateX,
|
|
550
|
-
initialTranslateY,
|
|
551
|
-
width,
|
|
552
|
-
height,
|
|
553
|
-
constrainTranslation,
|
|
554
|
-
]);
|
|
555
|
-
|
|
556
|
-
const zoomPanGesture = useMemo(() => {
|
|
557
|
-
return Gesture.Pan()
|
|
558
|
-
.enabled(enablePanWhenZoomed && isZoomed)
|
|
559
|
-
.activeCursor('grabbing')
|
|
560
|
-
.averageTouches(true)
|
|
561
|
-
.onBegin(() => {
|
|
562
|
-
initialTranslateX.value = translateX.value;
|
|
563
|
-
initialTranslateY.value = translateY.value;
|
|
564
|
-
})
|
|
565
|
-
.onUpdate((event) => {
|
|
566
|
-
if (scale.value > 1) {
|
|
567
|
-
const newTranslateX = initialTranslateX.value + event.translationX;
|
|
568
|
-
const newTranslateY = initialTranslateY.value + event.translationY;
|
|
569
|
-
|
|
570
|
-
const { translateX: constrainedTranslateX, translateY: constrainedTranslateY } = constrainTranslation({
|
|
571
|
-
translateX: newTranslateX,
|
|
572
|
-
translateY: newTranslateY,
|
|
573
|
-
scale: scale.value,
|
|
574
|
-
});
|
|
575
|
-
|
|
576
|
-
translateX.value = constrainedTranslateX;
|
|
577
|
-
translateY.value = constrainedTranslateY;
|
|
578
|
-
}
|
|
579
|
-
});
|
|
580
|
-
}, [
|
|
581
|
-
translateX,
|
|
582
|
-
translateY,
|
|
583
|
-
enablePanWhenZoomed,
|
|
584
|
-
isZoomed,
|
|
585
|
-
scale,
|
|
586
|
-
initialTranslateX,
|
|
587
|
-
initialTranslateY,
|
|
588
|
-
constrainTranslation,
|
|
589
|
-
]);
|
|
590
|
-
|
|
591
|
-
const doubleTapGesture = useMemo(() => {
|
|
592
|
-
return Gesture.Tap()
|
|
593
|
-
.enabled(enableDoubleTapZoom)
|
|
594
|
-
.numberOfTaps(2)
|
|
595
|
-
.onEnd((event) => {
|
|
596
|
-
const nextScale = scale.value > 1 ? 1 : maxZoomScale;
|
|
597
|
-
|
|
598
|
-
if (nextScale > 1) {
|
|
599
|
-
const centerX = event.x - width / 2;
|
|
600
|
-
const centerY = event.y - height / 2;
|
|
680
|
+
}),
|
|
681
|
+
[scale, enableDoubleTapZoom, maxZoomScale, translateX, translateY, width, height],
|
|
682
|
+
);
|
|
601
683
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
});
|
|
607
|
-
translateY.value = withTiming(-centerY * (nextScale - 1), {
|
|
608
|
-
duration: 300,
|
|
609
|
-
easing: Easing.bezier(0.25, 0.1, 0.25, 1.0),
|
|
610
|
-
});
|
|
611
|
-
} else {
|
|
612
|
-
translateX.value = withTiming(0, {
|
|
613
|
-
duration: 300,
|
|
614
|
-
easing: Easing.bezier(0.25, 0.1, 0.25, 1.0),
|
|
615
|
-
});
|
|
616
|
-
translateY.value = withTiming(0, {
|
|
617
|
-
duration: 300,
|
|
618
|
-
easing: Easing.bezier(0.25, 0.1, 0.25, 1.0),
|
|
619
|
-
});
|
|
620
|
-
}
|
|
684
|
+
const zoomGesture = useMemo(
|
|
685
|
+
() => Gesture.Race(zoomPinchGesture, Gesture.Exclusive(zoomPanGesture, doubleTapGesture)),
|
|
686
|
+
[zoomPinchGesture, zoomPanGesture, doubleTapGesture],
|
|
687
|
+
);
|
|
621
688
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
opacity: triggerOpacity.value,
|
|
636
|
-
transform: [
|
|
637
|
-
{ translateX: triggerTranslateX.value },
|
|
638
|
-
{ translateY: triggerTranslateY.value },
|
|
639
|
-
{ scale: triggerScale.value },
|
|
640
|
-
|
|
641
|
-
{ translateY: translateY.value },
|
|
642
|
-
{ translateX: translateX.value },
|
|
643
|
-
{ scale: scale.value },
|
|
644
|
-
{ rotate: `${rotation.value}deg` },
|
|
645
|
-
],
|
|
646
|
-
};
|
|
647
|
-
});
|
|
689
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
690
|
+
opacity: triggerOpacity.value,
|
|
691
|
+
transform: [
|
|
692
|
+
{ translateX: triggerTranslateX.value },
|
|
693
|
+
{ translateY: triggerTranslateY.value },
|
|
694
|
+
{ scale: triggerScale.value },
|
|
695
|
+
|
|
696
|
+
{ translateY: translateY.value },
|
|
697
|
+
{ translateX: translateX.value },
|
|
698
|
+
{ scale: scale.value },
|
|
699
|
+
{ rotate: `${rotation.value}deg` },
|
|
700
|
+
],
|
|
701
|
+
}));
|
|
648
702
|
|
|
649
703
|
const backdropStyle = useAnimatedStyle(() => {
|
|
650
704
|
const baseOpacity = triggerOpacity.value;
|
|
@@ -658,24 +712,29 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
658
712
|
return { opacity: baseOpacity * dismissOpacity };
|
|
659
713
|
}, [dismissOptions.fadeBackdrop]);
|
|
660
714
|
|
|
715
|
+
const nativeScrollGesture = useMemo(() => {
|
|
716
|
+
return Gesture.Native().requireExternalGestureToFail(dismissGestureRef);
|
|
717
|
+
}, []);
|
|
718
|
+
|
|
661
719
|
const onScrollBeginDrag = useCallback(() => {
|
|
662
720
|
manager?.handleScrollBeginDrag();
|
|
663
721
|
}, [manager]);
|
|
664
722
|
|
|
665
723
|
return {
|
|
724
|
+
animatedStyle,
|
|
725
|
+
backdropStyle,
|
|
666
726
|
dataLength,
|
|
667
|
-
listRef,
|
|
668
|
-
isZoomed,
|
|
669
|
-
isRotated,
|
|
670
|
-
isPinching,
|
|
671
727
|
dismissGesture,
|
|
672
|
-
|
|
728
|
+
handleDismiss,
|
|
729
|
+
isPinching,
|
|
730
|
+
isRotated,
|
|
673
731
|
|
|
732
|
+
isZoomed,
|
|
733
|
+
listRef,
|
|
734
|
+
nativeScrollGesture,
|
|
674
735
|
onMomentumScrollEnd,
|
|
675
|
-
onScrollBeginDrag,
|
|
676
|
-
handleDismiss,
|
|
677
736
|
|
|
678
|
-
|
|
679
|
-
|
|
737
|
+
onScrollBeginDrag,
|
|
738
|
+
zoomGesture,
|
|
680
739
|
};
|
|
681
740
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
|
+
|
|
2
3
|
import type GestureViewerManager from './GestureViewerManager';
|
|
3
4
|
import { registry } from './GestureViewerRegistry';
|
|
4
5
|
import type { GestureViewerEventCallback, GestureViewerEventType } from './types';
|
|
@@ -71,7 +72,10 @@ export function useGestureViewerEvent<T extends GestureViewerEventType>(
|
|
|
71
72
|
callback?: GestureViewerEventCallback<T>,
|
|
72
73
|
) {
|
|
73
74
|
const id = typeof idOrEventType === 'string' && callback ? idOrEventType : 'default';
|
|
74
|
-
const eventType =
|
|
75
|
+
const eventType =
|
|
76
|
+
typeof idOrEventType === 'string' && callback
|
|
77
|
+
? (eventTypeOrCallback as T)
|
|
78
|
+
: (idOrEventType as T);
|
|
75
79
|
const finalCallback = callback || (eventTypeOrCallback as GestureViewerEventCallback<T>);
|
|
76
80
|
|
|
77
81
|
useEffect(() => {
|