react-native-gesture-image-viewer 2.3.3 → 2.5.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 +19 -5
- package/lib/module/GestureViewer.js +39 -15
- package/lib/module/GestureViewer.js.map +1 -1
- package/lib/module/GestureViewerManager.js +43 -14
- package/lib/module/GestureViewerManager.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/itemDimensions.js +86 -0
- package/lib/module/itemDimensions.js.map +1 -0
- package/lib/module/useGestureViewer.js +246 -28
- package/lib/module/useGestureViewer.js.map +1 -1
- package/lib/module/useGestureViewerPaging.js +17 -5
- package/lib/module/useGestureViewerPaging.js.map +1 -1
- package/lib/module/useGestureViewerPaging.web.js +31 -7
- package/lib/module/useGestureViewerPaging.web.js.map +1 -1
- package/lib/module/utils/index.js +43 -10
- package/lib/module/utils/index.js.map +1 -1
- package/lib/module/utils/tapZoom.js +51 -13
- package/lib/module/utils/tapZoom.js.map +1 -1
- package/lib/typescript/src/GestureViewer.d.ts.map +1 -1
- package/lib/typescript/src/GestureViewerManager.d.ts +16 -3
- package/lib/typescript/src/GestureViewerManager.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/itemDimensions.d.ts +26 -0
- package/lib/typescript/src/itemDimensions.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +40 -1
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useGestureViewer.d.ts +4 -2
- package/lib/typescript/src/useGestureViewer.d.ts.map +1 -1
- package/lib/typescript/src/useGestureViewerPaging.d.ts +1 -1
- package/lib/typescript/src/useGestureViewerPaging.d.ts.map +1 -1
- package/lib/typescript/src/useGestureViewerPaging.types.d.ts +3 -0
- package/lib/typescript/src/useGestureViewerPaging.types.d.ts.map +1 -1
- package/lib/typescript/src/useGestureViewerPaging.web.d.ts +1 -1
- package/lib/typescript/src/useGestureViewerPaging.web.d.ts.map +1 -1
- package/lib/typescript/src/utils/index.d.ts +7 -3
- package/lib/typescript/src/utils/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/tapZoom.d.ts +17 -1
- package/lib/typescript/src/utils/tapZoom.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/GestureViewer.tsx +64 -18
- package/src/GestureViewerManager.ts +58 -19
- package/src/index.tsx +5 -0
- package/src/itemDimensions.ts +162 -0
- package/src/types.ts +51 -1
- package/src/useGestureViewer.ts +381 -52
- package/src/useGestureViewerPaging.ts +21 -7
- package/src/useGestureViewerPaging.types.ts +3 -0
- package/src/useGestureViewerPaging.web.ts +46 -7
- package/src/utils/index.ts +86 -27
- package/src/utils/tapZoom.ts +66 -13
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useEffect } from 'react';
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
2
|
import type { NativeScrollEvent, NativeSyntheticEvent } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import type {
|
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
import { getLoopAdjustedIndex } from './utils';
|
|
9
9
|
|
|
10
10
|
export function useGestureViewerPaging({
|
|
11
|
+
adjustedInitialIndex,
|
|
11
12
|
autoPlay,
|
|
12
13
|
autoPlayInterval,
|
|
13
14
|
currentIndex,
|
|
@@ -23,6 +24,12 @@ export function useGestureViewerPaging({
|
|
|
23
24
|
syncPendingIndex,
|
|
24
25
|
width,
|
|
25
26
|
}: UseGestureViewerPagingArgs): UseGestureViewerPagingResult {
|
|
27
|
+
const [activeListIndex, setActiveListIndex] = useState(adjustedInitialIndex);
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
setActiveListIndex(adjustedInitialIndex);
|
|
31
|
+
}, [adjustedInitialIndex, dataLength]);
|
|
32
|
+
|
|
26
33
|
useEffect(() => {
|
|
27
34
|
if (
|
|
28
35
|
!autoPlay ||
|
|
@@ -69,24 +76,30 @@ export function useGestureViewerPaging({
|
|
|
69
76
|
|
|
70
77
|
const { contentOffset } = event.nativeEvent;
|
|
71
78
|
const scrollIndex = Math.round(contentOffset.x / (width + itemSpacing));
|
|
79
|
+
const { realIndex, needsJump, jumpToIndex } = getLoopAdjustedIndex(
|
|
80
|
+
scrollIndex,
|
|
81
|
+
dataLength,
|
|
82
|
+
enableLoop,
|
|
83
|
+
);
|
|
72
84
|
|
|
73
85
|
const isLoopHandled = manager?.handleMomentumScrollEnd(scrollIndex);
|
|
74
86
|
|
|
75
|
-
if (
|
|
87
|
+
if (realIndex < 0 || realIndex >= dataLength) {
|
|
76
88
|
return;
|
|
77
89
|
}
|
|
78
90
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
91
|
+
if (isLoopHandled) {
|
|
92
|
+
syncCurrentIndex(realIndex);
|
|
93
|
+
setActiveListIndex(jumpToIndex ?? scrollIndex);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
84
96
|
|
|
85
97
|
if (needsJump && jumpToIndex !== undefined) {
|
|
86
98
|
scrollTo(jumpToIndex, false);
|
|
87
99
|
}
|
|
88
100
|
|
|
89
101
|
syncCurrentIndex(realIndex);
|
|
102
|
+
setActiveListIndex(jumpToIndex ?? scrollIndex);
|
|
90
103
|
},
|
|
91
104
|
[
|
|
92
105
|
dataLength,
|
|
@@ -120,6 +133,7 @@ export function useGestureViewerPaging({
|
|
|
120
133
|
}, [manager]);
|
|
121
134
|
|
|
122
135
|
return {
|
|
136
|
+
activeListIndex,
|
|
123
137
|
onMomentumScrollEnd,
|
|
124
138
|
onScroll,
|
|
125
139
|
onScrollBeginDrag,
|
|
@@ -8,6 +8,8 @@ export type UseGestureViewerPagingArgs = {
|
|
|
8
8
|
adjustedInitialIndex: number;
|
|
9
9
|
autoPlay: boolean;
|
|
10
10
|
autoPlayInterval: number;
|
|
11
|
+
contentHeight: SharedValue<number>;
|
|
12
|
+
contentWidth: SharedValue<number>;
|
|
11
13
|
currentIndex: number;
|
|
12
14
|
dataLength: number;
|
|
13
15
|
enableDoubleTapZoom: boolean;
|
|
@@ -34,6 +36,7 @@ export type WebClickTarget = {
|
|
|
34
36
|
};
|
|
35
37
|
|
|
36
38
|
export type UseGestureViewerPagingResult = {
|
|
39
|
+
activeListIndex: number;
|
|
37
40
|
onMomentumScrollEnd?: ScrollViewProps['onMomentumScrollEnd'];
|
|
38
41
|
onScroll?: ScrollViewProps['onScroll'];
|
|
39
42
|
onScrollBeginDrag?: ScrollViewProps['onScrollBeginDrag'];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef, type MouseEvent } from 'react';
|
|
1
|
+
import { useCallback, useEffect, useRef, useState, type MouseEvent } from 'react';
|
|
2
2
|
import type { NativeScrollEvent, NativeSyntheticEvent } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import type {
|
|
@@ -30,6 +30,8 @@ export function useGestureViewerPaging({
|
|
|
30
30
|
adjustedInitialIndex,
|
|
31
31
|
autoPlay,
|
|
32
32
|
autoPlayInterval,
|
|
33
|
+
contentHeight,
|
|
34
|
+
contentWidth,
|
|
33
35
|
currentIndex,
|
|
34
36
|
dataLength,
|
|
35
37
|
enableDoubleTapZoom,
|
|
@@ -50,18 +52,26 @@ export function useGestureViewerPaging({
|
|
|
50
52
|
translateY,
|
|
51
53
|
width,
|
|
52
54
|
}: UseGestureViewerPagingArgs): UseGestureViewerPagingResult {
|
|
55
|
+
const [activeListIndex, setActiveListIndex] = useState(adjustedInitialIndex);
|
|
56
|
+
const pageStride = width + itemSpacing;
|
|
53
57
|
const webSingleTapTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
54
58
|
|
|
55
59
|
const webScrollRuntimeRef = useRef<WebScrollRuntime>({
|
|
56
60
|
actor: 'idle',
|
|
57
61
|
isAutoplayPausedByUser: false,
|
|
58
62
|
lastSettledPhysicalIndex: adjustedInitialIndex,
|
|
59
|
-
latestOffsetX: adjustedInitialIndex *
|
|
63
|
+
latestOffsetX: adjustedInitialIndex * pageStride,
|
|
60
64
|
latestRawPhysicalIndex: adjustedInitialIndex,
|
|
61
65
|
lastProgrammaticScrollVersion: 0,
|
|
62
66
|
settleTimer: null,
|
|
63
67
|
resumeAutoplayTimer: null,
|
|
64
68
|
});
|
|
69
|
+
const previousPagingLayoutRef = useRef<{
|
|
70
|
+
adjustedInitialIndex: number;
|
|
71
|
+
dataLength: number;
|
|
72
|
+
manager: typeof manager;
|
|
73
|
+
pageStride: number;
|
|
74
|
+
} | null>(null);
|
|
65
75
|
|
|
66
76
|
const clearWebSingleTapTimer = useCallback(() => {
|
|
67
77
|
if (webSingleTapTimerRef.current) {
|
|
@@ -187,6 +197,7 @@ export function useGestureViewerPaging({
|
|
|
187
197
|
runtime.latestOffsetX = settledPhysicalIndex * (width + itemSpacing);
|
|
188
198
|
runtime.actor = 'idle';
|
|
189
199
|
manager?.cancelPendingLoopTransition();
|
|
200
|
+
setActiveListIndex(settledPhysicalIndex);
|
|
190
201
|
syncCurrentIndex(logicalIndex);
|
|
191
202
|
|
|
192
203
|
if (settledByActor === 'user') {
|
|
@@ -223,12 +234,35 @@ export function useGestureViewerPaging({
|
|
|
223
234
|
|
|
224
235
|
useEffect(() => {
|
|
225
236
|
const runtime = webScrollRuntimeRef.current;
|
|
237
|
+
const previousLayout = previousPagingLayoutRef.current;
|
|
238
|
+
const shouldResetToInitialIndex =
|
|
239
|
+
previousLayout === null ||
|
|
240
|
+
previousLayout.adjustedInitialIndex !== adjustedInitialIndex ||
|
|
241
|
+
previousLayout.dataLength !== dataLength ||
|
|
242
|
+
previousLayout.manager !== manager;
|
|
243
|
+
const didPageStrideChange = previousLayout !== null && previousLayout.pageStride !== pageStride;
|
|
244
|
+
|
|
245
|
+
previousPagingLayoutRef.current = {
|
|
246
|
+
adjustedInitialIndex,
|
|
247
|
+
dataLength,
|
|
248
|
+
manager,
|
|
249
|
+
pageStride,
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
if (!shouldResetToInitialIndex && !didPageStrideChange) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
226
255
|
|
|
227
256
|
runtime.actor = 'idle';
|
|
228
257
|
runtime.isAutoplayPausedByUser = false;
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
258
|
+
|
|
259
|
+
if (shouldResetToInitialIndex) {
|
|
260
|
+
runtime.lastSettledPhysicalIndex = adjustedInitialIndex;
|
|
261
|
+
setActiveListIndex(adjustedInitialIndex);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
runtime.latestOffsetX = runtime.lastSettledPhysicalIndex * pageStride;
|
|
265
|
+
runtime.latestRawPhysicalIndex = runtime.lastSettledPhysicalIndex;
|
|
232
266
|
runtime.lastProgrammaticScrollVersion = manager?.getProgrammaticScrollVersion() ?? 0;
|
|
233
267
|
clearWebSettleTimer();
|
|
234
268
|
clearWebAutoplayResumeTimer();
|
|
@@ -236,9 +270,9 @@ export function useGestureViewerPaging({
|
|
|
236
270
|
adjustedInitialIndex,
|
|
237
271
|
clearWebAutoplayResumeTimer,
|
|
238
272
|
clearWebSettleTimer,
|
|
239
|
-
|
|
273
|
+
dataLength,
|
|
240
274
|
manager,
|
|
241
|
-
|
|
275
|
+
pageStride,
|
|
242
276
|
]);
|
|
243
277
|
|
|
244
278
|
useEffect(() => {
|
|
@@ -387,6 +421,8 @@ export function useGestureViewerPaging({
|
|
|
387
421
|
scheduleWebAutoplayResume();
|
|
388
422
|
|
|
389
423
|
applyTapZoomAtPoint({
|
|
424
|
+
contentHeight: contentHeight.get(),
|
|
425
|
+
contentWidth: contentWidth.get(),
|
|
390
426
|
x: resolvedX,
|
|
391
427
|
y: resolvedY,
|
|
392
428
|
width,
|
|
@@ -412,6 +448,8 @@ export function useGestureViewerPaging({
|
|
|
412
448
|
},
|
|
413
449
|
[
|
|
414
450
|
clearWebSingleTapTimer,
|
|
451
|
+
contentHeight,
|
|
452
|
+
contentWidth,
|
|
415
453
|
enableDoubleTapZoom,
|
|
416
454
|
height,
|
|
417
455
|
maxZoomScale,
|
|
@@ -426,6 +464,7 @@ export function useGestureViewerPaging({
|
|
|
426
464
|
);
|
|
427
465
|
|
|
428
466
|
return {
|
|
467
|
+
activeListIndex,
|
|
429
468
|
onMomentumScrollEnd,
|
|
430
469
|
onScroll,
|
|
431
470
|
onScrollBeginDrag,
|
package/src/utils/index.ts
CHANGED
|
@@ -47,39 +47,92 @@ export const shouldUseNativeScrollGesture = (
|
|
|
47
47
|
return platformOS === 'ios' && component !== GestureScrollView && component !== GestureFlatList;
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
-
export const
|
|
51
|
-
(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
50
|
+
export const clampIndex = (index: number | undefined, dataLength: number): number => {
|
|
51
|
+
if (dataLength <= 0) {
|
|
52
|
+
return 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const candidateIndex = index ?? 0;
|
|
56
|
+
|
|
57
|
+
if (!Number.isFinite(candidateIndex)) {
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return Math.min(Math.max(Math.trunc(candidateIndex), 0), dataLength - 1);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const resolveGeometrySyncTranslationMode = (
|
|
65
|
+
previousIndex: number | null,
|
|
66
|
+
nextIndex: number,
|
|
67
|
+
scale: number,
|
|
68
|
+
): 'constrain' | 'none' | 'reset' => {
|
|
69
|
+
if (previousIndex !== null && previousIndex !== nextIndex) {
|
|
70
|
+
return 'reset';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return scale > 1 ? 'constrain' : 'none';
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const isValidDimension = (value: number): boolean => {
|
|
77
|
+
'worklet';
|
|
78
|
+
|
|
79
|
+
return Number.isFinite(value) && value > 0;
|
|
80
|
+
};
|
|
69
81
|
|
|
70
|
-
|
|
71
|
-
|
|
82
|
+
const clampTranslation = (value: number, max: number): number => {
|
|
83
|
+
'worklet';
|
|
72
84
|
|
|
85
|
+
if (max <= 0) {
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const clamped = Math.max(-max, Math.min(max, value));
|
|
90
|
+
|
|
91
|
+
return clamped === 0 ? 0 : clamped;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const clampTranslationToBounds = ({
|
|
95
|
+
width,
|
|
96
|
+
height,
|
|
97
|
+
contentWidth,
|
|
98
|
+
contentHeight,
|
|
99
|
+
scale,
|
|
100
|
+
translateX,
|
|
101
|
+
translateY,
|
|
102
|
+
}: {
|
|
103
|
+
width: number;
|
|
104
|
+
height: number;
|
|
105
|
+
contentWidth?: number;
|
|
106
|
+
contentHeight?: number;
|
|
107
|
+
translateX: number;
|
|
108
|
+
translateY: number;
|
|
109
|
+
scale: number;
|
|
110
|
+
}) => {
|
|
111
|
+
'worklet';
|
|
112
|
+
|
|
113
|
+
if (scale <= 1) {
|
|
73
114
|
return {
|
|
74
|
-
translateX
|
|
75
|
-
translateY
|
|
115
|
+
translateX,
|
|
116
|
+
translateY,
|
|
76
117
|
};
|
|
77
|
-
}
|
|
118
|
+
}
|
|
78
119
|
|
|
79
|
-
|
|
80
|
-
|
|
120
|
+
const baseContentWidth =
|
|
121
|
+
contentWidth !== undefined && isValidDimension(contentWidth) ? contentWidth : width;
|
|
122
|
+
const baseContentHeight =
|
|
123
|
+
contentHeight !== undefined && isValidDimension(contentHeight) ? contentHeight : height;
|
|
81
124
|
|
|
82
|
-
|
|
125
|
+
const maxTranslateX = Math.max(0, (baseContentWidth * scale - width) / 2);
|
|
126
|
+
const maxTranslateY = Math.max(0, (baseContentHeight * scale - height) / 2);
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
translateX: clampTranslation(translateX, maxTranslateX),
|
|
130
|
+
translateY: clampTranslation(translateY, maxTranslateY),
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const createLoopData = <T>(data: T[], enableLoop: boolean): T[] => {
|
|
135
|
+
if (!enableLoop || data.length <= 1) {
|
|
83
136
|
return data;
|
|
84
137
|
}
|
|
85
138
|
|
|
@@ -93,6 +146,12 @@ export const createLoopData = <T>(dataRef: React.RefObject<T[]>, enableLoop: boo
|
|
|
93
146
|
return [lastItem, ...data, firstItem];
|
|
94
147
|
};
|
|
95
148
|
|
|
149
|
+
export const getLoopPhysicalIndex = (
|
|
150
|
+
logicalIndex: number,
|
|
151
|
+
dataLength: number,
|
|
152
|
+
enableLoop: boolean,
|
|
153
|
+
): number => (enableLoop && dataLength > 1 ? logicalIndex + 1 : logicalIndex);
|
|
154
|
+
|
|
96
155
|
export const getLoopAdjustedIndex = (
|
|
97
156
|
scrollIndex: number,
|
|
98
157
|
originalDataLength: number,
|
package/src/utils/tapZoom.ts
CHANGED
|
@@ -1,11 +1,62 @@
|
|
|
1
1
|
import type { SharedValue } from 'react-native-reanimated';
|
|
2
2
|
import { Easing, withTiming } from 'react-native-reanimated';
|
|
3
3
|
|
|
4
|
+
import { clampTranslationToBounds } from '.';
|
|
5
|
+
|
|
6
|
+
export const getTapZoomTarget = ({
|
|
7
|
+
x,
|
|
8
|
+
y,
|
|
9
|
+
width,
|
|
10
|
+
height,
|
|
11
|
+
contentWidth,
|
|
12
|
+
contentHeight,
|
|
13
|
+
maxZoomScale,
|
|
14
|
+
scale,
|
|
15
|
+
}: {
|
|
16
|
+
x: number;
|
|
17
|
+
y: number;
|
|
18
|
+
width: number;
|
|
19
|
+
height: number;
|
|
20
|
+
contentWidth?: number;
|
|
21
|
+
contentHeight?: number;
|
|
22
|
+
maxZoomScale: number;
|
|
23
|
+
scale: number;
|
|
24
|
+
}) => {
|
|
25
|
+
'worklet';
|
|
26
|
+
|
|
27
|
+
const nextScale = scale > 1 ? 1 : maxZoomScale;
|
|
28
|
+
|
|
29
|
+
if (nextScale <= 1) {
|
|
30
|
+
return {
|
|
31
|
+
scale: nextScale,
|
|
32
|
+
translateX: 0,
|
|
33
|
+
translateY: 0,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const centerX = x - width / 2;
|
|
38
|
+
const centerY = y - height / 2;
|
|
39
|
+
return {
|
|
40
|
+
scale: nextScale,
|
|
41
|
+
...clampTranslationToBounds({
|
|
42
|
+
contentHeight,
|
|
43
|
+
contentWidth,
|
|
44
|
+
height,
|
|
45
|
+
scale: nextScale,
|
|
46
|
+
translateX: -centerX * (nextScale - 1),
|
|
47
|
+
translateY: -centerY * (nextScale - 1),
|
|
48
|
+
width,
|
|
49
|
+
}),
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
|
|
4
53
|
export const applyTapZoomAtPoint = ({
|
|
5
54
|
x,
|
|
6
55
|
y,
|
|
7
56
|
width,
|
|
8
57
|
height,
|
|
58
|
+
contentWidth,
|
|
59
|
+
contentHeight,
|
|
9
60
|
maxZoomScale,
|
|
10
61
|
scale,
|
|
11
62
|
translateX,
|
|
@@ -15,6 +66,8 @@ export const applyTapZoomAtPoint = ({
|
|
|
15
66
|
y: number;
|
|
16
67
|
width: number;
|
|
17
68
|
height: number;
|
|
69
|
+
contentWidth?: number;
|
|
70
|
+
contentHeight?: number;
|
|
18
71
|
maxZoomScale: number;
|
|
19
72
|
scale: SharedValue<number>;
|
|
20
73
|
translateX: SharedValue<number>;
|
|
@@ -22,23 +75,23 @@ export const applyTapZoomAtPoint = ({
|
|
|
22
75
|
}) => {
|
|
23
76
|
'worklet';
|
|
24
77
|
|
|
25
|
-
const
|
|
78
|
+
const target = getTapZoomTarget({
|
|
79
|
+
contentHeight,
|
|
80
|
+
contentWidth,
|
|
81
|
+
height,
|
|
82
|
+
maxZoomScale,
|
|
83
|
+
scale: scale.get(),
|
|
84
|
+
width,
|
|
85
|
+
x,
|
|
86
|
+
y,
|
|
87
|
+
});
|
|
26
88
|
const timingConfig = {
|
|
27
89
|
duration: 300,
|
|
28
90
|
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
|
|
29
91
|
};
|
|
30
92
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const centerY = y - height / 2;
|
|
34
|
-
|
|
35
|
-
// NOTE 확대로 밀려난 거리만큼 반대로 이동해서 탭 지점을 제자리에 유지
|
|
36
|
-
translateX.set(withTiming(-centerX * (nextScale - 1), timingConfig));
|
|
37
|
-
translateY.set(withTiming(-centerY * (nextScale - 1), timingConfig));
|
|
38
|
-
} else {
|
|
39
|
-
translateX.set(withTiming(0, timingConfig));
|
|
40
|
-
translateY.set(withTiming(0, timingConfig));
|
|
41
|
-
}
|
|
93
|
+
translateX.set(withTiming(target.translateX, timingConfig));
|
|
94
|
+
translateY.set(withTiming(target.translateY, timingConfig));
|
|
42
95
|
|
|
43
|
-
scale.set(withTiming(
|
|
96
|
+
scale.set(withTiming(target.scale, timingConfig));
|
|
44
97
|
};
|