react-native-gesture-image-viewer 2.3.0 → 2.3.2
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/GestureViewerManager.js +24 -23
- package/lib/module/GestureViewerManager.js.map +1 -1
- package/lib/module/useGestureViewer.js +129 -97
- package/lib/module/useGestureViewer.js.map +1 -1
- package/lib/module/utils/tapZoom.js +6 -6
- package/lib/module/utils/tapZoom.js.map +1 -1
- package/lib/module/utils/zoom.js +42 -0
- package/lib/module/utils/zoom.js.map +1 -0
- package/lib/typescript/src/GestureViewerManager.d.ts.map +1 -1
- package/lib/typescript/src/useGestureViewer.d.ts.map +1 -1
- package/lib/typescript/src/utils/zoom.d.ts +24 -0
- package/lib/typescript/src/utils/zoom.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/GestureViewerManager.ts +27 -25
- package/src/useGestureViewer.ts +166 -115
- package/src/utils/tapZoom.ts +6 -6
- package/src/utils/zoom.ts +60 -0
package/src/useGestureViewer.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { useGestureViewerPaging } from './useGestureViewerPaging';
|
|
|
19
19
|
import { createBoundsConstraint, createScrollAction } from './utils';
|
|
20
20
|
import { getDismissDistance, shouldDismissByDirection } from './utils/dismiss';
|
|
21
21
|
import { applyTapZoomAtPoint } from './utils/tapZoom';
|
|
22
|
+
import { calculateFocalPointTranslation, shouldAcceptFocalPoint } from './utils/zoom';
|
|
22
23
|
|
|
23
24
|
type UseGestureViewerProps<ItemT, LC> = Omit<
|
|
24
25
|
GestureViewerProps<ItemT, LC>,
|
|
@@ -95,6 +96,9 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
95
96
|
|
|
96
97
|
const lastFocalX = useSharedValue(0);
|
|
97
98
|
const lastFocalY = useSharedValue(0);
|
|
99
|
+
const startFocalX = useSharedValue(0);
|
|
100
|
+
const startFocalY = useSharedValue(0);
|
|
101
|
+
const hasActiveFocal = useSharedValue(false);
|
|
98
102
|
|
|
99
103
|
const dataLength = data?.length || 0;
|
|
100
104
|
|
|
@@ -147,13 +151,13 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
147
151
|
);
|
|
148
152
|
|
|
149
153
|
const resetTransformState = useCallback(() => {
|
|
150
|
-
translateX.
|
|
151
|
-
translateY.
|
|
152
|
-
initialTranslateX.
|
|
153
|
-
initialTranslateY.
|
|
154
|
-
startScale.
|
|
155
|
-
scale.
|
|
156
|
-
rotation.
|
|
154
|
+
translateX.set(withTiming(0));
|
|
155
|
+
translateY.set(withTiming(0));
|
|
156
|
+
initialTranslateX.set(withTiming(0));
|
|
157
|
+
initialTranslateY.set(withTiming(0));
|
|
158
|
+
startScale.set(withTiming(1));
|
|
159
|
+
scale.set(withTiming(1));
|
|
160
|
+
rotation.set(0);
|
|
157
161
|
}, [initialTranslateX, initialTranslateY, rotation, scale, startScale, translateX, translateY]);
|
|
158
162
|
|
|
159
163
|
const syncPendingIndex = useCallback(
|
|
@@ -201,7 +205,7 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
201
205
|
}, []);
|
|
202
206
|
|
|
203
207
|
useAnimatedReaction(
|
|
204
|
-
() => scale.
|
|
208
|
+
() => scale.get(),
|
|
205
209
|
(currentScale, previousScale) => {
|
|
206
210
|
if (currentScale !== previousScale) {
|
|
207
211
|
scheduleOnRN(emitZoomChange, currentScale, previousScale);
|
|
@@ -212,7 +216,7 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
212
216
|
);
|
|
213
217
|
|
|
214
218
|
useAnimatedReaction(
|
|
215
|
-
() => rotation.
|
|
219
|
+
() => rotation.get(),
|
|
216
220
|
(currentRotation, previousRotation) => {
|
|
217
221
|
if (currentRotation !== previousRotation) {
|
|
218
222
|
scheduleOnRN(emitRotationChange, currentRotation, previousRotation);
|
|
@@ -286,12 +290,12 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
286
290
|
}, [manager]);
|
|
287
291
|
|
|
288
292
|
useEffect(() => {
|
|
289
|
-
translateY.
|
|
290
|
-
translateX.
|
|
291
|
-
scale.
|
|
292
|
-
backdropOpacity.
|
|
293
|
-
startScale.
|
|
294
|
-
rotation.
|
|
293
|
+
translateY.set(0);
|
|
294
|
+
translateX.set(0);
|
|
295
|
+
scale.set(1);
|
|
296
|
+
backdropOpacity.set(1);
|
|
297
|
+
startScale.set(1);
|
|
298
|
+
rotation.set(0);
|
|
295
299
|
|
|
296
300
|
if (adjustedInitialIndex <= 0 || !listRef.current) {
|
|
297
301
|
return;
|
|
@@ -340,23 +344,27 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
340
344
|
triggerRectRef.current.height / height,
|
|
341
345
|
);
|
|
342
346
|
|
|
343
|
-
triggerScale.
|
|
344
|
-
triggerTranslateX.
|
|
345
|
-
triggerTranslateY.
|
|
346
|
-
triggerOpacity.
|
|
347
|
+
triggerScale.set(initialScaleFromTrigger);
|
|
348
|
+
triggerTranslateX.set(startX);
|
|
349
|
+
triggerTranslateY.set(startY);
|
|
350
|
+
triggerOpacity.set(0);
|
|
347
351
|
|
|
348
|
-
triggerScale.
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
352
|
+
triggerScale.set(
|
|
353
|
+
withTiming(1, animationConfig, (finished) => {
|
|
354
|
+
if (finished) {
|
|
355
|
+
scheduleOnRN(onAnimationComplete);
|
|
356
|
+
}
|
|
357
|
+
}),
|
|
358
|
+
);
|
|
359
|
+
triggerTranslateX.set(withTiming(0, animationConfig));
|
|
360
|
+
triggerTranslateY.set(withTiming(0, animationConfig));
|
|
361
|
+
triggerOpacity.set(
|
|
362
|
+
withTiming(1, {
|
|
363
|
+
duration: animationConfig.duration / 2,
|
|
364
|
+
easing: animationConfig.easing,
|
|
365
|
+
reduceMotion: animationConfig.reduceMotion,
|
|
366
|
+
}),
|
|
367
|
+
);
|
|
360
368
|
|
|
361
369
|
setShouldStartTriggerAnimation(false);
|
|
362
370
|
}
|
|
@@ -391,7 +399,7 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
391
399
|
}
|
|
392
400
|
|
|
393
401
|
triggerRectRef.current = nextTriggerRect;
|
|
394
|
-
triggerOpacity.
|
|
402
|
+
triggerOpacity.set(0);
|
|
395
403
|
setShouldStartTriggerAnimation(true);
|
|
396
404
|
registry.clearActiveTriggerNode(id);
|
|
397
405
|
});
|
|
@@ -409,14 +417,16 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
409
417
|
const endY = rect.y + rect.height / 2 - height / 2;
|
|
410
418
|
const endScale = Math.min(rect.width / width, rect.height / height);
|
|
411
419
|
|
|
412
|
-
triggerScale.
|
|
413
|
-
triggerTranslateX.
|
|
414
|
-
triggerTranslateY.
|
|
415
|
-
triggerOpacity.
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
+
triggerScale.set(withTiming(endScale, animationConfig));
|
|
421
|
+
triggerTranslateX.set(withTiming(endX, animationConfig));
|
|
422
|
+
triggerTranslateY.set(withTiming(endY, animationConfig));
|
|
423
|
+
triggerOpacity.set(
|
|
424
|
+
withTiming(0, animationConfig, (finished) => {
|
|
425
|
+
if (finished && onDismiss) {
|
|
426
|
+
scheduleOnRN(onDismiss);
|
|
427
|
+
}
|
|
428
|
+
}),
|
|
429
|
+
);
|
|
420
430
|
},
|
|
421
431
|
[
|
|
422
432
|
animationConfig,
|
|
@@ -486,7 +496,7 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
486
496
|
.withRef(dismissGestureRef)
|
|
487
497
|
.enabled(canDismiss)
|
|
488
498
|
.onUpdate((event) => {
|
|
489
|
-
translateY.
|
|
499
|
+
translateY.set(event.translationY / dismissOptions.resistance);
|
|
490
500
|
})
|
|
491
501
|
.onEnd((event) => {
|
|
492
502
|
if (
|
|
@@ -501,13 +511,15 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
501
511
|
return;
|
|
502
512
|
}
|
|
503
513
|
|
|
504
|
-
translateY.
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
514
|
+
translateY.set(
|
|
515
|
+
withSpring(0, {
|
|
516
|
+
damping: 50,
|
|
517
|
+
energyThreshold: 6e-9,
|
|
518
|
+
mass: 4,
|
|
519
|
+
overshootClamping: false,
|
|
520
|
+
stiffness: 600,
|
|
521
|
+
}),
|
|
522
|
+
);
|
|
511
523
|
});
|
|
512
524
|
}, [translateY, dismissOptions, handleDismiss, isZoomed]);
|
|
513
525
|
|
|
@@ -520,40 +532,66 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
520
532
|
scheduleOnRN(setIsPinching, true);
|
|
521
533
|
}
|
|
522
534
|
})
|
|
523
|
-
.
|
|
524
|
-
startScale.
|
|
525
|
-
initialTranslateX.
|
|
526
|
-
initialTranslateY.
|
|
527
|
-
|
|
528
|
-
|
|
535
|
+
.onStart((event) => {
|
|
536
|
+
startScale.set(scale.get());
|
|
537
|
+
initialTranslateX.set(translateX.get());
|
|
538
|
+
initialTranslateY.set(translateY.get());
|
|
539
|
+
startFocalX.set(event.focalX);
|
|
540
|
+
startFocalY.set(event.focalY);
|
|
541
|
+
lastFocalX.set(event.focalX);
|
|
542
|
+
lastFocalY.set(event.focalY);
|
|
543
|
+
hasActiveFocal.set(false);
|
|
529
544
|
})
|
|
530
545
|
.onUpdate((event) => {
|
|
531
|
-
const
|
|
546
|
+
const initialScale = startScale.get();
|
|
547
|
+
const newScale = initialScale * event.scale;
|
|
532
548
|
|
|
533
|
-
scale.
|
|
549
|
+
scale.set(newScale);
|
|
534
550
|
|
|
535
551
|
if (newScale <= 1) {
|
|
536
|
-
translateX.
|
|
537
|
-
translateY.
|
|
552
|
+
translateX.set(withTiming(0));
|
|
553
|
+
translateY.set(withTiming(0));
|
|
538
554
|
return;
|
|
539
555
|
}
|
|
540
556
|
|
|
541
|
-
const focalDeltaX = Math.abs(event.focalX - lastFocalX.value);
|
|
542
|
-
const focalDeltaY = Math.abs(event.focalY - lastFocalY.value);
|
|
543
557
|
const threshold = 50;
|
|
544
558
|
|
|
545
|
-
if (
|
|
546
|
-
|
|
547
|
-
|
|
559
|
+
if (
|
|
560
|
+
shouldAcceptFocalPoint({
|
|
561
|
+
focalX: event.focalX,
|
|
562
|
+
focalY: event.focalY,
|
|
563
|
+
hasActiveFocal: hasActiveFocal.get(),
|
|
564
|
+
lastFocalX: lastFocalX.get(),
|
|
565
|
+
lastFocalY: lastFocalY.get(),
|
|
566
|
+
threshold,
|
|
567
|
+
})
|
|
568
|
+
) {
|
|
569
|
+
if (!hasActiveFocal.get()) {
|
|
570
|
+
startFocalX.set(event.focalX);
|
|
571
|
+
startFocalY.set(event.focalY);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
lastFocalX.set(event.focalX);
|
|
575
|
+
lastFocalY.set(event.focalY);
|
|
576
|
+
hasActiveFocal.set(true);
|
|
548
577
|
}
|
|
549
578
|
|
|
550
|
-
const
|
|
551
|
-
const
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
579
|
+
const nextLastFocalX = lastFocalX.get();
|
|
580
|
+
const nextLastFocalY = lastFocalY.get();
|
|
581
|
+
|
|
582
|
+
const { translateX: newTranslateX, translateY: newTranslateY } =
|
|
583
|
+
calculateFocalPointTranslation({
|
|
584
|
+
currentFocalX: nextLastFocalX,
|
|
585
|
+
currentFocalY: nextLastFocalY,
|
|
586
|
+
height,
|
|
587
|
+
initialScale,
|
|
588
|
+
initialTranslateX: initialTranslateX.get(),
|
|
589
|
+
initialTranslateY: initialTranslateY.get(),
|
|
590
|
+
nextScale: newScale,
|
|
591
|
+
startFocalX: startFocalX.get(),
|
|
592
|
+
startFocalY: startFocalY.get(),
|
|
593
|
+
width,
|
|
594
|
+
});
|
|
557
595
|
|
|
558
596
|
const { translateX: constrainedTranslateX, translateY: constrainedTranslateY } =
|
|
559
597
|
constrainTranslation({
|
|
@@ -562,55 +600,63 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
562
600
|
translateY: newTranslateY,
|
|
563
601
|
});
|
|
564
602
|
|
|
565
|
-
translateX.
|
|
566
|
-
translateY.
|
|
603
|
+
translateX.set(constrainedTranslateX);
|
|
604
|
+
translateY.set(constrainedTranslateY);
|
|
567
605
|
})
|
|
568
606
|
.onEnd(() => {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
607
|
+
const currentScale = scale.get();
|
|
608
|
+
|
|
609
|
+
if (currentScale > maxZoomScale) {
|
|
610
|
+
scale.set(
|
|
611
|
+
withTiming(maxZoomScale, {
|
|
612
|
+
duration: 300,
|
|
613
|
+
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
|
|
614
|
+
}),
|
|
615
|
+
);
|
|
574
616
|
|
|
575
617
|
const { translateX: constrainedTranslateX, translateY: constrainedTranslateY } =
|
|
576
618
|
constrainTranslation({
|
|
577
619
|
scale: maxZoomScale,
|
|
578
|
-
translateX: translateX.
|
|
579
|
-
translateY: translateY.
|
|
620
|
+
translateX: translateX.get(),
|
|
621
|
+
translateY: translateY.get(),
|
|
580
622
|
});
|
|
581
623
|
|
|
582
|
-
translateX.
|
|
583
|
-
translateY.
|
|
624
|
+
translateX.set(withTiming(constrainedTranslateX));
|
|
625
|
+
translateY.set(withTiming(constrainedTranslateY));
|
|
584
626
|
|
|
585
627
|
return;
|
|
586
628
|
}
|
|
587
629
|
|
|
588
|
-
if (
|
|
589
|
-
scale.
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
630
|
+
if (currentScale < 1) {
|
|
631
|
+
scale.set(
|
|
632
|
+
withTiming(1, {
|
|
633
|
+
duration: 300,
|
|
634
|
+
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
|
|
635
|
+
}),
|
|
636
|
+
);
|
|
637
|
+
translateX.set(withTiming(0));
|
|
638
|
+
translateY.set(withTiming(0));
|
|
639
|
+
initialTranslateX.set(withTiming(0));
|
|
640
|
+
initialTranslateY.set(withTiming(0));
|
|
641
|
+
hasActiveFocal.set(false);
|
|
597
642
|
return;
|
|
598
643
|
}
|
|
599
644
|
|
|
600
645
|
const { translateX: constrainedTranslateX, translateY: constrainedTranslateY } =
|
|
601
646
|
constrainTranslation({
|
|
602
|
-
scale:
|
|
603
|
-
translateX: translateX.
|
|
604
|
-
translateY: translateY.
|
|
647
|
+
scale: currentScale,
|
|
648
|
+
translateX: translateX.get(),
|
|
649
|
+
translateY: translateY.get(),
|
|
605
650
|
});
|
|
606
651
|
|
|
607
|
-
translateX.
|
|
608
|
-
translateY.
|
|
652
|
+
translateX.set(withTiming(constrainedTranslateX));
|
|
653
|
+
translateY.set(withTiming(constrainedTranslateY));
|
|
609
654
|
})
|
|
610
655
|
.onTouchesUp(() => {
|
|
611
656
|
scheduleOnRN(setIsPinching, false);
|
|
612
657
|
})
|
|
613
658
|
.onFinalize(() => {
|
|
659
|
+
hasActiveFocal.set(false);
|
|
614
660
|
scheduleOnRN(setIsPinching, false);
|
|
615
661
|
}),
|
|
616
662
|
[
|
|
@@ -627,6 +673,9 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
627
673
|
constrainTranslation,
|
|
628
674
|
lastFocalX,
|
|
629
675
|
lastFocalY,
|
|
676
|
+
startFocalX,
|
|
677
|
+
startFocalY,
|
|
678
|
+
hasActiveFocal,
|
|
630
679
|
],
|
|
631
680
|
);
|
|
632
681
|
|
|
@@ -637,23 +686,25 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
637
686
|
.activeCursor('grabbing')
|
|
638
687
|
.averageTouches(true)
|
|
639
688
|
.onBegin(() => {
|
|
640
|
-
initialTranslateX.
|
|
641
|
-
initialTranslateY.
|
|
689
|
+
initialTranslateX.set(translateX.get());
|
|
690
|
+
initialTranslateY.set(translateY.get());
|
|
642
691
|
})
|
|
643
692
|
.onUpdate((event) => {
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
693
|
+
const currentScale = scale.get();
|
|
694
|
+
|
|
695
|
+
if (currentScale > 1) {
|
|
696
|
+
const newTranslateX = initialTranslateX.get() + event.translationX;
|
|
697
|
+
const newTranslateY = initialTranslateY.get() + event.translationY;
|
|
647
698
|
|
|
648
699
|
const { translateX: constrainedTranslateX, translateY: constrainedTranslateY } =
|
|
649
700
|
constrainTranslation({
|
|
650
|
-
scale:
|
|
701
|
+
scale: currentScale,
|
|
651
702
|
translateX: newTranslateX,
|
|
652
703
|
translateY: newTranslateY,
|
|
653
704
|
});
|
|
654
705
|
|
|
655
|
-
translateX.
|
|
656
|
-
translateY.
|
|
706
|
+
translateX.set(constrainedTranslateX);
|
|
707
|
+
translateY.set(constrainedTranslateY);
|
|
657
708
|
}
|
|
658
709
|
}),
|
|
659
710
|
[
|
|
@@ -733,27 +784,27 @@ export const useGestureViewer = <ItemT, LC>({
|
|
|
733
784
|
);
|
|
734
785
|
|
|
735
786
|
const animatedStyle = useAnimatedStyle(() => ({
|
|
736
|
-
opacity: triggerOpacity.
|
|
787
|
+
opacity: triggerOpacity.get(),
|
|
737
788
|
transform: [
|
|
738
|
-
{ translateX: triggerTranslateX.
|
|
739
|
-
{ translateY: triggerTranslateY.
|
|
740
|
-
{ scale: triggerScale.
|
|
741
|
-
|
|
742
|
-
{ translateY: translateY.
|
|
743
|
-
{ translateX: translateX.
|
|
744
|
-
{ scale: scale.
|
|
745
|
-
{ rotate: `${rotation.
|
|
789
|
+
{ translateX: triggerTranslateX.get() },
|
|
790
|
+
{ translateY: triggerTranslateY.get() },
|
|
791
|
+
{ scale: triggerScale.get() },
|
|
792
|
+
|
|
793
|
+
{ translateY: translateY.get() },
|
|
794
|
+
{ translateX: translateX.get() },
|
|
795
|
+
{ scale: scale.get() },
|
|
796
|
+
{ rotate: `${rotation.get()}deg` },
|
|
746
797
|
],
|
|
747
798
|
}));
|
|
748
799
|
|
|
749
800
|
const backdropStyle = useAnimatedStyle(() => {
|
|
750
|
-
const baseOpacity = triggerOpacity.
|
|
801
|
+
const baseOpacity = triggerOpacity.get();
|
|
751
802
|
|
|
752
|
-
if (!dismissOptions.fadeBackdrop || scale.
|
|
803
|
+
if (!dismissOptions.fadeBackdrop || scale.get() !== 1) {
|
|
753
804
|
return { opacity: baseOpacity };
|
|
754
805
|
}
|
|
755
806
|
|
|
756
|
-
const dismissDistance = getDismissDistance(translateY.
|
|
807
|
+
const dismissDistance = getDismissDistance(translateY.get(), dismissOptions.direction);
|
|
757
808
|
const dismissOpacity = interpolate(dismissDistance, [0, 200], [1, 0], 'clamp');
|
|
758
809
|
|
|
759
810
|
return { opacity: baseOpacity * dismissOpacity };
|
package/src/utils/tapZoom.ts
CHANGED
|
@@ -22,7 +22,7 @@ export const applyTapZoomAtPoint = ({
|
|
|
22
22
|
}) => {
|
|
23
23
|
'worklet';
|
|
24
24
|
|
|
25
|
-
const nextScale = scale.
|
|
25
|
+
const nextScale = scale.get() > 1 ? 1 : maxZoomScale;
|
|
26
26
|
const timingConfig = {
|
|
27
27
|
duration: 300,
|
|
28
28
|
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
|
|
@@ -33,12 +33,12 @@ export const applyTapZoomAtPoint = ({
|
|
|
33
33
|
const centerY = y - height / 2;
|
|
34
34
|
|
|
35
35
|
// NOTE 확대로 밀려난 거리만큼 반대로 이동해서 탭 지점을 제자리에 유지
|
|
36
|
-
translateX.
|
|
37
|
-
translateY.
|
|
36
|
+
translateX.set(withTiming(-centerX * (nextScale - 1), timingConfig));
|
|
37
|
+
translateY.set(withTiming(-centerY * (nextScale - 1), timingConfig));
|
|
38
38
|
} else {
|
|
39
|
-
translateX.
|
|
40
|
-
translateY.
|
|
39
|
+
translateX.set(withTiming(0, timingConfig));
|
|
40
|
+
translateY.set(withTiming(0, timingConfig));
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
scale.
|
|
43
|
+
scale.set(withTiming(nextScale, timingConfig));
|
|
44
44
|
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export const calculateFocalPointTranslation = ({
|
|
2
|
+
currentFocalX,
|
|
3
|
+
currentFocalY,
|
|
4
|
+
height,
|
|
5
|
+
initialScale,
|
|
6
|
+
initialTranslateX,
|
|
7
|
+
initialTranslateY,
|
|
8
|
+
nextScale,
|
|
9
|
+
startFocalX,
|
|
10
|
+
startFocalY,
|
|
11
|
+
width,
|
|
12
|
+
}: {
|
|
13
|
+
currentFocalX: number;
|
|
14
|
+
currentFocalY: number;
|
|
15
|
+
height: number;
|
|
16
|
+
initialScale: number;
|
|
17
|
+
initialTranslateX: number;
|
|
18
|
+
initialTranslateY: number;
|
|
19
|
+
nextScale: number;
|
|
20
|
+
startFocalX: number;
|
|
21
|
+
startFocalY: number;
|
|
22
|
+
width: number;
|
|
23
|
+
}) => {
|
|
24
|
+
'worklet';
|
|
25
|
+
|
|
26
|
+
const scaleRatio = nextScale / initialScale;
|
|
27
|
+
const currentCenterX = currentFocalX - width / 2;
|
|
28
|
+
const currentCenterY = currentFocalY - height / 2;
|
|
29
|
+
const startCenterX = startFocalX - width / 2;
|
|
30
|
+
const startCenterY = startFocalY - height / 2;
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
translateX: initialTranslateX * scaleRatio + currentCenterX - startCenterX * scaleRatio,
|
|
34
|
+
translateY: initialTranslateY * scaleRatio + currentCenterY - startCenterY * scaleRatio,
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const shouldAcceptFocalPoint = ({
|
|
39
|
+
focalX,
|
|
40
|
+
focalY,
|
|
41
|
+
hasActiveFocal,
|
|
42
|
+
lastFocalX,
|
|
43
|
+
lastFocalY,
|
|
44
|
+
threshold,
|
|
45
|
+
}: {
|
|
46
|
+
focalX: number;
|
|
47
|
+
focalY: number;
|
|
48
|
+
hasActiveFocal: boolean;
|
|
49
|
+
lastFocalX: number;
|
|
50
|
+
lastFocalY: number;
|
|
51
|
+
threshold: number;
|
|
52
|
+
}) => {
|
|
53
|
+
'worklet';
|
|
54
|
+
|
|
55
|
+
if (!hasActiveFocal) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return Math.abs(focalX - lastFocalX) < threshold && Math.abs(focalY - lastFocalY) < threshold;
|
|
60
|
+
};
|