react-native-gesture-image-viewer 0.1.0 → 0.2.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.
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
 
3
- import { useCallback } from 'react';
4
- import { FlatList, Platform, StyleSheet, useWindowDimensions, View } from 'react-native';
5
- import { GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';
3
+ import { useCallback, useMemo } from 'react';
4
+ import { Platform, StyleSheet, useWindowDimensions, View } from 'react-native';
5
+ import { FlatList, Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';
6
6
  import Animated from 'react-native-reanimated';
7
7
  import { useGestureImageViewer } from "./useGestureImageViewer.js";
8
8
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
@@ -31,8 +31,11 @@ export function GestureImageViewer({
31
31
  } = useWindowDimensions();
32
32
  const width = customWidth || screenWidth;
33
33
  const {
34
+ currentIndex,
34
35
  flatListRef,
35
- panGesture,
36
+ isZoomed,
37
+ dismissGesture,
38
+ zoomGesture,
36
39
  onMomentumScrollEnd,
37
40
  animatedStyle,
38
41
  backdropStyle
@@ -46,31 +49,35 @@ export function GestureImageViewer({
46
49
  item,
47
50
  index
48
51
  }) => {
52
+ const isActive = index === currentIndex;
49
53
  return /*#__PURE__*/_jsx(Animated.View, {
50
- style: {
54
+ style: [{
51
55
  width,
52
56
  height: '100%',
53
57
  justifyContent: 'center',
54
58
  alignItems: 'center'
55
- },
59
+ }, isActive && animatedStyle],
56
60
  children: renderImage(item, index)
57
61
  });
58
- }, [width, renderImage]);
62
+ }, [width, renderImage, animatedStyle, currentIndex]);
59
63
  const getItemLayout = useCallback((_, index) => ({
60
64
  length: width,
61
65
  offset: width * index,
62
66
  index
63
67
  }), [width]);
64
68
  const keyExtractor = useCallback((item, index) => `image-${index}-${item}`, []);
69
+ const gesture = useMemo(() => {
70
+ return Gesture.Race(dismissGesture, zoomGesture);
71
+ }, [zoomGesture, dismissGesture]);
65
72
  const listComponent = /*#__PURE__*/_jsx(GestureHandlerRootView, {
66
73
  children: /*#__PURE__*/_jsx(GestureDetector, {
67
- gesture: panGesture,
74
+ gesture: gesture,
68
75
  children: /*#__PURE__*/_jsxs(View, {
69
76
  style: [styles.container, containerStyle],
70
77
  children: [/*#__PURE__*/_jsx(Animated.View, {
71
78
  style: [styles.background, backdropStyleProps, backdropStyle]
72
- }), /*#__PURE__*/_jsx(Animated.View, {
73
- style: [styles.content, animatedStyle],
79
+ }), /*#__PURE__*/_jsx(View, {
80
+ style: styles.content,
74
81
  children: /*#__PURE__*/_jsx(ListComponent, {
75
82
  ref: flatListRef,
76
83
  data: data,
@@ -78,6 +85,7 @@ export function GestureImageViewer({
78
85
  keyExtractor: keyExtractor,
79
86
  horizontal: true,
80
87
  pagingEnabled: true,
88
+ scrollEnabled: !isZoomed,
81
89
  showsHorizontalScrollIndicator: false,
82
90
  onMomentumScrollEnd: onMomentumScrollEnd,
83
91
  getItemLayout: getItemLayout,
@@ -1 +1 @@
1
- {"version":3,"names":["useCallback","FlatList","Platform","StyleSheet","useWindowDimensions","View","GestureDetector","GestureHandlerRootView","Animated","useGestureImageViewer","jsx","_jsx","jsxs","_jsxs","WebPagingFix","OS","children","GestureImageViewer","data","renderImage","renderContainer","ListComponent","width","customWidth","listProps","backdropStyle","backdropStyleProps","containerStyle","initialIndex","props","screenWidth","flatListRef","panGesture","onMomentumScrollEnd","animatedStyle","renderItem","item","index","style","height","justifyContent","alignItems","getItemLayout","_","length","offset","keyExtractor","listComponent","gesture","styles","container","background","content","ref","horizontal","pagingEnabled","showsHorizontalScrollIndicator","initialScrollIndex","windowSize","maxToRenderPerBatch","removeClippedSubviews","dataSet","create","flex","position","top","left","right","bottom","backgroundColor"],"sourceRoot":"../../src","sources":["GestureImageViewer.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["useCallback","useMemo","Platform","StyleSheet","useWindowDimensions","View","FlatList","Gesture","GestureDetector","GestureHandlerRootView","Animated","useGestureImageViewer","jsx","_jsx","jsxs","_jsxs","WebPagingFix","OS","children","GestureImageViewer","data","renderImage","renderContainer","ListComponent","width","customWidth","listProps","backdropStyle","backdropStyleProps","containerStyle","initialIndex","props","screenWidth","currentIndex","flatListRef","isZoomed","dismissGesture","zoomGesture","onMomentumScrollEnd","animatedStyle","renderItem","item","index","isActive","style","height","justifyContent","alignItems","getItemLayout","_","length","offset","keyExtractor","gesture","Race","listComponent","styles","container","background","content","ref","horizontal","pagingEnabled","scrollEnabled","showsHorizontalScrollIndicator","initialScrollIndex","windowSize","maxToRenderPerBatch","removeClippedSubviews","dataSet","create","flex","position","top","left","right","bottom","backgroundColor"],"sourceRoot":"../../src","sources":["GestureImageViewer.tsx"],"mappings":"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","ignoreList":[]}
@@ -3,7 +3,7 @@
3
3
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
4
4
  import { InteractionManager, useWindowDimensions } from 'react-native';
5
5
  import { Gesture } from 'react-native-gesture-handler';
6
- import { interpolate, runOnJS, useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';
6
+ import { Easing, interpolate, runOnJS, useAnimatedReaction, useAnimatedStyle, useSharedValue, withSpring, withTiming } from 'react-native-reanimated';
7
7
  export const useGestureImageViewer = ({
8
8
  data,
9
9
  initialIndex = 0,
@@ -16,23 +16,40 @@ export const useGestureImageViewer = ({
16
16
  // velocityThreshold = 200,
17
17
  animateBackdrop = true,
18
18
  enableDismissGesture = true,
19
- enableSwipeGesture = true
19
+ enableSwipeGesture = true,
20
+ enableZoomGesture = true,
21
+ enableDoubleTapGesture = true,
22
+ enableZoomPanGesture = true,
23
+ maxZoomScale = 2
20
24
  }) => {
21
25
  const {
22
- width: screenWidth
26
+ width: screenWidth,
27
+ height: screenHeight
23
28
  } = useWindowDimensions();
24
29
  const width = customWidth || screenWidth;
25
30
  const [currentIndex, setCurrentIndex] = useState(initialIndex);
31
+ const [isZoomed, setIsZoomed] = useState(false);
32
+ const initialTranslateY = useSharedValue(0);
33
+ const initialTranslateX = useSharedValue(0);
34
+ const startScale = useSharedValue(1);
26
35
  const translateY = useSharedValue(0);
36
+ const translateX = useSharedValue(0);
37
+ const scale = useSharedValue(1);
27
38
  const backdropOpacity = useSharedValue(1);
28
39
  const flatListRef = useRef(null);
29
40
  const dataLength = data?.length || 0;
41
+ useAnimatedReaction(() => scale.value, currentScale => {
42
+ runOnJS(setIsZoomed)(currentScale > 1);
43
+ });
30
44
 
31
45
  // 초기 위치 설정
32
46
  useEffect(() => {
33
47
  setCurrentIndex(initialIndex);
34
48
  translateY.value = 0;
49
+ translateX.value = 0;
50
+ scale.value = 1;
35
51
  backdropOpacity.value = 1;
52
+ startScale.value = 1;
36
53
  if (initialIndex <= 0 || !flatListRef.current) {
37
54
  return;
38
55
  }
@@ -47,7 +64,7 @@ export const useGestureImageViewer = ({
47
64
  return () => {
48
65
  runAfterInteractions?.cancel();
49
66
  };
50
- }, [initialIndex, translateY, backdropOpacity]);
67
+ }, [initialIndex, translateY, backdropOpacity, translateX, scale, startScale]);
51
68
 
52
69
  // 인덱스 변경 알림
53
70
  useEffect(() => {
@@ -63,8 +80,14 @@ export const useGestureImageViewer = ({
63
80
  const newIndex = Math.round(contentOffset.x / width);
64
81
  if (newIndex !== currentIndex && newIndex >= 0 && newIndex < dataLength) {
65
82
  setCurrentIndex(newIndex);
83
+ translateX.value = withTiming(0);
84
+ translateY.value = withTiming(0);
85
+ initialTranslateX.value = withTiming(0);
86
+ initialTranslateY.value = withTiming(0);
87
+ startScale.value = withTiming(1);
88
+ scale.value = withTiming(1);
66
89
  }
67
- }, [currentIndex, dataLength, width, enableSwipeGesture]);
90
+ }, [currentIndex, dataLength, width, enableSwipeGesture, translateX, translateY, scale, initialTranslateX, initialTranslateY, startScale]);
68
91
  const goToIndex = useCallback(index => {
69
92
  if (index < 0 || index >= dataLength || !enableSwipeGesture) {
70
93
  return;
@@ -85,10 +108,8 @@ export const useGestureImageViewer = ({
85
108
  goToIndex(currentIndex + 1);
86
109
  }
87
110
  }, [currentIndex, dataLength, goToIndex]);
88
- const panGesture = useMemo(() => {
89
- return Gesture.Pan().minDistance(10).activeOffsetY([-10, 10]).failOffsetX([-10, 10]).onUpdate(event => {
90
- 'worklet';
91
-
111
+ const dismissGesture = useMemo(() => {
112
+ return Gesture.Pan().minDistance(10).averageTouches(true).activeOffsetY([-10, 10]).failOffsetX([-10, 10]).enabled(!isZoomed).onUpdate(event => {
92
113
  translateY.value = event.translationY / resistance;
93
114
  }).onEnd(event => {
94
115
  'worklet';
@@ -102,18 +123,78 @@ export const useGestureImageViewer = ({
102
123
  stiffness: 150
103
124
  });
104
125
  });
105
- }, [translateY, dismissThreshold, enableDismissGesture, onDismiss, resistance]);
126
+ }, [translateY, dismissThreshold, enableDismissGesture, onDismiss, resistance, isZoomed]);
127
+ const zoomPinchGesture = useMemo(() => {
128
+ return Gesture.Pinch().enabled(enableZoomGesture).onBegin(() => {
129
+ startScale.value = scale.value;
130
+ }).onUpdate(event => {
131
+ scale.value = startScale.value * event.scale;
132
+ }).onEnd(() => {
133
+ if (scale.value > maxZoomScale) {
134
+ scale.value = withTiming(maxZoomScale, {
135
+ duration: 300,
136
+ easing: Easing.bezier(0.25, 0.1, 0.25, 1.0)
137
+ });
138
+ return;
139
+ }
140
+ if (scale.value < 1) {
141
+ scale.value = withTiming(1, {
142
+ duration: 300,
143
+ easing: Easing.bezier(0.25, 0.1, 0.25, 1.0)
144
+ });
145
+ translateX.value = withTiming(0);
146
+ translateY.value = withTiming(0);
147
+ }
148
+ });
149
+ }, [scale, enableZoomGesture, maxZoomScale, translateX, translateY, startScale]);
150
+ const zoomPanGesture = useMemo(() => {
151
+ return Gesture.Pan().enabled(enableZoomPanGesture && isZoomed).onBegin(() => {
152
+ initialTranslateX.value = translateX.value;
153
+ initialTranslateY.value = translateY.value;
154
+ }).onUpdate(event => {
155
+ 'worklet';
106
156
 
107
- // 애니메이션 스타일
157
+ if (scale.value > 1) {
158
+ const maxTranslateX = (width * scale.value - width) / 2;
159
+ const maxTranslateY = (screenHeight * scale.value - screenHeight) / 2;
160
+ translateX.value = Math.max(-maxTranslateX, Math.min(maxTranslateX, initialTranslateX.value + event.translationX));
161
+ translateY.value = Math.max(-maxTranslateY, Math.min(maxTranslateY, initialTranslateY.value + event.translationY));
162
+ }
163
+ });
164
+ }, [translateX, translateY, enableZoomPanGesture, isZoomed, scale, initialTranslateX, initialTranslateY, width, screenHeight]);
165
+ const doubleTapGesture = useMemo(() => {
166
+ return Gesture.Tap().enabled(enableDoubleTapGesture).numberOfTaps(2).onEnd(() => {
167
+ const nextScale = scale.value > 1 ? 1 : maxZoomScale;
168
+ translateX.value = withTiming(0, {
169
+ duration: 300,
170
+ easing: Easing.bezier(0.25, 0.1, 0.25, 1.0)
171
+ });
172
+ translateY.value = withTiming(0, {
173
+ duration: 300,
174
+ easing: Easing.bezier(0.25, 0.1, 0.25, 1.0)
175
+ });
176
+ scale.value = withTiming(nextScale, {
177
+ duration: 300,
178
+ easing: Easing.bezier(0.25, 0.1, 0.25, 1.0)
179
+ });
180
+ });
181
+ }, [scale, enableDoubleTapGesture, maxZoomScale, translateX, translateY]);
182
+ const zoomGesture = useMemo(() => {
183
+ return Gesture.Simultaneous(zoomPinchGesture, zoomPanGesture, doubleTapGesture);
184
+ }, [zoomPinchGesture, zoomPanGesture, doubleTapGesture]);
108
185
  const animatedStyle = useAnimatedStyle(() => {
109
186
  return {
110
187
  transform: [{
111
188
  translateY: translateY.value
189
+ }, {
190
+ translateX: translateX.value
191
+ }, {
192
+ scale: scale.value
112
193
  }]
113
194
  };
114
- }, [translateY]);
195
+ });
115
196
  const backdropStyle = useAnimatedStyle(() => {
116
- if (!animateBackdrop) {
197
+ if (!animateBackdrop || scale.value > 1) {
117
198
  return {
118
199
  opacity: 1
119
200
  };
@@ -122,19 +203,21 @@ export const useGestureImageViewer = ({
122
203
  return {
123
204
  opacity
124
205
  };
125
- }, [animateBackdrop, translateY]);
206
+ }, [animateBackdrop]);
126
207
  return {
127
208
  // State
128
209
  currentIndex,
129
210
  dataLength,
130
211
  translateY,
131
212
  flatListRef,
213
+ isZoomed,
132
214
  // Actions
133
215
  goToPrevious,
134
216
  goToNext,
135
217
  goToIndex,
136
218
  // Gesture
137
- panGesture,
219
+ dismissGesture,
220
+ zoomGesture,
138
221
  // FlatList handlers
139
222
  onMomentumScrollEnd,
140
223
  // Animated styles
@@ -1 +1 @@
1
- {"version":3,"names":["useCallback","useEffect","useMemo","useRef","useState","InteractionManager","useWindowDimensions","Gesture","interpolate","runOnJS","useAnimatedStyle","useSharedValue","withSpring","useGestureImageViewer","data","initialIndex","onIndexChange","onDismiss","width","customWidth","dismissThreshold","resistance","animateBackdrop","enableDismissGesture","enableSwipeGesture","screenWidth","currentIndex","setCurrentIndex","translateY","backdropOpacity","flatListRef","dataLength","length","value","current","runAfterInteractions","scrollToIndex","index","animated","cancel","onMomentumScrollEnd","event","contentOffset","nativeEvent","newIndex","Math","round","x","goToIndex","goToPrevious","goToNext","panGesture","Pan","minDistance","activeOffsetY","failOffsetX","onUpdate","translationY","onEnd","damping","stiffness","animatedStyle","transform","backdropStyle","opacity"],"sourceRoot":"../../src","sources":["useGestureImageViewer.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["useCallback","useEffect","useMemo","useRef","useState","InteractionManager","useWindowDimensions","Gesture","Easing","interpolate","runOnJS","useAnimatedReaction","useAnimatedStyle","useSharedValue","withSpring","withTiming","useGestureImageViewer","data","initialIndex","onIndexChange","onDismiss","width","customWidth","dismissThreshold","resistance","animateBackdrop","enableDismissGesture","enableSwipeGesture","enableZoomGesture","enableDoubleTapGesture","enableZoomPanGesture","maxZoomScale","screenWidth","height","screenHeight","currentIndex","setCurrentIndex","isZoomed","setIsZoomed","initialTranslateY","initialTranslateX","startScale","translateY","translateX","scale","backdropOpacity","flatListRef","dataLength","length","value","currentScale","current","runAfterInteractions","scrollToIndex","index","animated","cancel","onMomentumScrollEnd","event","contentOffset","nativeEvent","newIndex","Math","round","x","goToIndex","goToPrevious","goToNext","dismissGesture","Pan","minDistance","averageTouches","activeOffsetY","failOffsetX","enabled","onUpdate","translationY","onEnd","damping","stiffness","zoomPinchGesture","Pinch","onBegin","duration","easing","bezier","zoomPanGesture","maxTranslateX","maxTranslateY","max","min","translationX","doubleTapGesture","Tap","numberOfTaps","nextScale","zoomGesture","Simultaneous","animatedStyle","transform","backdropStyle","opacity"],"sourceRoot":"../../src","sources":["useGestureImageViewer.ts"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"GestureImageViewer.d.ts","sourceRoot":"","sources":["../../../src/GestureImageViewer.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAWvD,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,GAAG,EAAE,EAC1C,IAAI,EACJ,WAAW,EACX,eAAe,EACf,aAAwB,EACxB,KAAK,EAAE,WAAW,EAClB,SAAS,EACT,aAAa,EAAE,kBAAkB,EACjC,cAAc,EACd,YAAgB,EAChB,GAAG,KAAK,EACT,EAAE,uBAAuB,CAAC,CAAC,CAAC,2CAyE5B"}
1
+ {"version":3,"file":"GestureImageViewer.d.ts","sourceRoot":"","sources":["../../../src/GestureImageViewer.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAWvD,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,GAAG,EAAE,EAC1C,IAAI,EACJ,WAAW,EACX,eAAe,EACf,aAAwB,EACxB,KAAK,EAAE,WAAW,EAClB,SAAS,EACT,aAAa,EAAE,kBAAkB,EACjC,cAAc,EACd,YAAgB,EAChB,GAAG,KAAK,EACT,EAAE,uBAAuB,CAAC,CAAC,CAAC,2CA4F5B"}
@@ -19,6 +19,10 @@ export interface GestureImageViewerProps<T = any> {
19
19
  backdropStyle?: StyleProp<ViewStyle>;
20
20
  containerStyle?: StyleProp<ViewStyle>;
21
21
  animateBackdrop?: boolean;
22
+ enableZoomPanGesture?: boolean;
23
+ enableZoomGesture?: boolean;
24
+ enableDoubleTapGesture?: boolean;
25
+ maxZoomScale?: number;
22
26
  }
23
27
  export interface GestureState {
24
28
  currentIndex: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D,MAAM,WAAW,uBAAuB,CAAC,CAAC,GAAG,GAAG;IAC9C,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC,YAAY,CAAC;IAC5D,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC,YAAY,KAAK,KAAK,CAAC,YAAY,CAAC;IACvE,aAAa,CAAC,EAAE,GAAG,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,GAAG,CAAC;IAChB,aAAa,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACrC,cAAc,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAChC,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,YAAY,GAAG,UAAU,GAAG,IAAI,CAAC;CACjD"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D,MAAM,WAAW,uBAAuB,CAAC,CAAC,GAAG,GAAG;IAC9C,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC,YAAY,CAAC;IAC5D,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC,YAAY,KAAK,KAAK,CAAC,YAAY,CAAC;IACvE,aAAa,CAAC,EAAE,GAAG,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,GAAG,CAAC;IAChB,aAAa,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACrC,cAAc,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAChC,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,YAAY,GAAG,UAAU,GAAG,IAAI,CAAC;CACjD"}
@@ -1,20 +1,32 @@
1
1
  import { type NativeScrollEvent, type NativeSyntheticEvent } from 'react-native';
2
2
  import type { GestureImageViewerProps } from './types';
3
3
  type UseGestureImageViewerProps<T = any> = Omit<GestureImageViewerProps<T>, 'renderImage' | 'renderContainer' | 'ListComponent' | 'listProps' | 'containerStyle' | 'backdropStyle'>;
4
- export declare const useGestureImageViewer: <T = any>({ data, initialIndex, onIndexChange, onDismiss, width: customWidth, dismissThreshold, resistance, animateBackdrop, enableDismissGesture, enableSwipeGesture, }: UseGestureImageViewerProps<T>) => {
4
+ export declare const useGestureImageViewer: <T = any>({ data, initialIndex, onIndexChange, onDismiss, width: customWidth, dismissThreshold, resistance, animateBackdrop, enableDismissGesture, enableSwipeGesture, enableZoomGesture, enableDoubleTapGesture, enableZoomPanGesture, maxZoomScale, }: UseGestureImageViewerProps<T>) => {
5
5
  currentIndex: number;
6
6
  dataLength: number;
7
7
  translateY: import("react-native-reanimated").SharedValue<number>;
8
8
  flatListRef: import("react").RefObject<any>;
9
+ isZoomed: boolean;
9
10
  goToPrevious: () => void;
10
11
  goToNext: () => void;
11
12
  goToIndex: (index: number) => void;
12
- panGesture: import("react-native-gesture-handler/lib/typescript/handlers/gestures/panGesture").PanGesture;
13
+ dismissGesture: import("react-native-gesture-handler/lib/typescript/handlers/gestures/panGesture").PanGesture;
14
+ zoomGesture: import("react-native-gesture-handler/lib/typescript/handlers/gestures/gestureComposition").SimultaneousGesture;
13
15
  onMomentumScrollEnd: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
14
16
  animatedStyle: {
15
- transform: {
17
+ transform: ({
16
18
  translateY: number;
17
- }[];
19
+ translateX?: undefined;
20
+ scale?: undefined;
21
+ } | {
22
+ translateX: number;
23
+ translateY?: undefined;
24
+ scale?: undefined;
25
+ } | {
26
+ scale: number;
27
+ translateY?: undefined;
28
+ translateX?: undefined;
29
+ })[];
18
30
  };
19
31
  backdropStyle: {
20
32
  opacity: number;
@@ -1 +1 @@
1
- {"version":3,"file":"useGestureImageViewer.d.ts","sourceRoot":"","sources":["../../../src/useGestureImageViewer.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAE1B,MAAM,cAAc,CAAC;AAGtB,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAEvD,KAAK,0BAA0B,CAAC,CAAC,GAAG,GAAG,IAAI,IAAI,CAC7C,uBAAuB,CAAC,CAAC,CAAC,EAC1B,aAAa,GAAG,iBAAiB,GAAG,eAAe,GAAG,WAAW,GAAG,gBAAgB,GAAG,eAAe,CACvG,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,CAAC,GAAG,GAAG,EAAE,gKAa5C,0BAA0B,CAAC,CAAC,CAAC;;;;;;;uBA2DpB,MAAM;;iCAhBN,oBAAoB,CAAC,iBAAiB,CAAC;;;;;;;;;CAyGlD,CAAC"}
1
+ {"version":3,"file":"useGestureImageViewer.d.ts","sourceRoot":"","sources":["../../../src/useGestureImageViewer.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAE1B,MAAM,cAAc,CAAC;AAYtB,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAEvD,KAAK,0BAA0B,CAAC,CAAC,GAAG,GAAG,IAAI,IAAI,CAC7C,uBAAuB,CAAC,CAAC,CAAC,EAC1B,aAAa,GAAG,iBAAiB,GAAG,eAAe,GAAG,WAAW,GAAG,gBAAgB,GAAG,eAAe,CACvG,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,CAAC,GAAG,GAAG,EAAE,+OAiB5C,0BAA0B,CAAC,CAAC,CAAC;;;;;;;;uBA6FpB,MAAM;;;iCAjCN,oBAAoB,CAAC,iBAAiB,CAAC;;;;;;;;;;;;;;;;;;;CAwNlD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-gesture-image-viewer",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "react-native-gesture-image-viewer",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -1,6 +1,6 @@
1
- import { useCallback } from 'react';
2
- import { FlatList, Platform, StyleSheet, useWindowDimensions, View } from 'react-native';
3
- import { GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';
1
+ import { useCallback, useMemo } from 'react';
2
+ import { Platform, StyleSheet, useWindowDimensions, View } from 'react-native';
3
+ import { FlatList, Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';
4
4
  import Animated from 'react-native-reanimated';
5
5
  import type { GestureImageViewerProps } from './types';
6
6
  import { useGestureImageViewer } from './useGestureImageViewer';
@@ -29,7 +29,16 @@ export function GestureImageViewer<T = any>({
29
29
 
30
30
  const width = customWidth || screenWidth;
31
31
 
32
- const { flatListRef, panGesture, onMomentumScrollEnd, animatedStyle, backdropStyle } = useGestureImageViewer({
32
+ const {
33
+ currentIndex,
34
+ flatListRef,
35
+ isZoomed,
36
+ dismissGesture,
37
+ zoomGesture,
38
+ onMomentumScrollEnd,
39
+ animatedStyle,
40
+ backdropStyle,
41
+ } = useGestureImageViewer({
33
42
  data,
34
43
  width,
35
44
  initialIndex,
@@ -38,20 +47,25 @@ export function GestureImageViewer<T = any>({
38
47
 
39
48
  const renderItem = useCallback(
40
49
  ({ item, index }: { item: T; index: number }) => {
50
+ const isActive = index === currentIndex;
51
+
41
52
  return (
42
53
  <Animated.View
43
- style={{
44
- width,
45
- height: '100%',
46
- justifyContent: 'center',
47
- alignItems: 'center',
48
- }}
54
+ style={[
55
+ {
56
+ width,
57
+ height: '100%',
58
+ justifyContent: 'center',
59
+ alignItems: 'center',
60
+ },
61
+ isActive && animatedStyle,
62
+ ]}
49
63
  >
50
64
  {renderImage(item, index)}
51
65
  </Animated.View>
52
66
  );
53
67
  },
54
- [width, renderImage],
68
+ [width, renderImage, animatedStyle, currentIndex],
55
69
  );
56
70
 
57
71
  const getItemLayout = useCallback(
@@ -65,12 +79,16 @@ export function GestureImageViewer<T = any>({
65
79
 
66
80
  const keyExtractor = useCallback((item: T, index: number) => `image-${index}-${item}`, []);
67
81
 
82
+ const gesture = useMemo(() => {
83
+ return Gesture.Race(dismissGesture, zoomGesture);
84
+ }, [zoomGesture, dismissGesture]);
85
+
68
86
  const listComponent = (
69
87
  <GestureHandlerRootView>
70
- <GestureDetector gesture={panGesture}>
88
+ <GestureDetector gesture={gesture}>
71
89
  <View style={[styles.container, containerStyle]}>
72
90
  <Animated.View style={[styles.background, backdropStyleProps, backdropStyle]} />
73
- <Animated.View style={[styles.content, animatedStyle]}>
91
+ <View style={styles.content}>
74
92
  <ListComponent
75
93
  ref={flatListRef}
76
94
  data={data}
@@ -78,6 +96,7 @@ export function GestureImageViewer<T = any>({
78
96
  keyExtractor={keyExtractor}
79
97
  horizontal
80
98
  pagingEnabled
99
+ scrollEnabled={!isZoomed}
81
100
  showsHorizontalScrollIndicator={false}
82
101
  onMomentumScrollEnd={onMomentumScrollEnd}
83
102
  getItemLayout={getItemLayout}
@@ -89,7 +108,7 @@ export function GestureImageViewer<T = any>({
89
108
  {...(Platform.OS === 'web' && { dataSet: { 'paging-enabled-fix': true } })}
90
109
  {...listProps}
91
110
  />
92
- </Animated.View>
111
+ </View>
93
112
  <WebPagingFix />
94
113
  </View>
95
114
  </GestureDetector>
package/src/types.ts CHANGED
@@ -20,6 +20,10 @@ export interface GestureImageViewerProps<T = any> {
20
20
  backdropStyle?: StyleProp<ViewStyle>;
21
21
  containerStyle?: StyleProp<ViewStyle>;
22
22
  animateBackdrop?: boolean;
23
+ enableZoomPanGesture?: boolean;
24
+ enableZoomGesture?: boolean;
25
+ enableDoubleTapGesture?: boolean;
26
+ maxZoomScale?: number;
23
27
  }
24
28
 
25
29
  export interface GestureState {
@@ -6,7 +6,16 @@ import {
6
6
  useWindowDimensions,
7
7
  } from 'react-native';
8
8
  import { Gesture } from 'react-native-gesture-handler';
9
- import { interpolate, runOnJS, useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';
9
+ import {
10
+ Easing,
11
+ interpolate,
12
+ runOnJS,
13
+ useAnimatedReaction,
14
+ useAnimatedStyle,
15
+ useSharedValue,
16
+ withSpring,
17
+ withTiming,
18
+ } from 'react-native-reanimated';
10
19
  import type { GestureImageViewerProps } from './types';
11
20
 
12
21
  type UseGestureImageViewerProps<T = any> = Omit<
@@ -27,24 +36,45 @@ export const useGestureImageViewer = <T = any>({
27
36
  animateBackdrop = true,
28
37
  enableDismissGesture = true,
29
38
  enableSwipeGesture = true,
39
+ enableZoomGesture = true,
40
+ enableDoubleTapGesture = true,
41
+ enableZoomPanGesture = true,
42
+ maxZoomScale = 2,
30
43
  }: UseGestureImageViewerProps<T>) => {
31
- const { width: screenWidth } = useWindowDimensions();
44
+ const { width: screenWidth, height: screenHeight } = useWindowDimensions();
32
45
  const width = customWidth || screenWidth;
33
46
 
34
47
  const [currentIndex, setCurrentIndex] = useState(initialIndex);
48
+ const [isZoomed, setIsZoomed] = useState(false);
49
+
50
+ const initialTranslateY = useSharedValue(0);
51
+ const initialTranslateX = useSharedValue(0);
52
+ const startScale = useSharedValue(1);
35
53
 
36
54
  const translateY = useSharedValue(0);
55
+ const translateX = useSharedValue(0);
56
+ const scale = useSharedValue(1);
37
57
  const backdropOpacity = useSharedValue(1);
38
58
 
39
59
  const flatListRef = useRef<any>(null);
40
60
 
41
61
  const dataLength = data?.length || 0;
42
62
 
63
+ useAnimatedReaction(
64
+ () => scale.value,
65
+ (currentScale) => {
66
+ runOnJS(setIsZoomed)(currentScale > 1);
67
+ },
68
+ );
69
+
43
70
  // 초기 위치 설정
44
71
  useEffect(() => {
45
72
  setCurrentIndex(initialIndex);
46
73
  translateY.value = 0;
74
+ translateX.value = 0;
75
+ scale.value = 1;
47
76
  backdropOpacity.value = 1;
77
+ startScale.value = 1;
48
78
 
49
79
  if (initialIndex <= 0 || !flatListRef.current) {
50
80
  return;
@@ -61,7 +91,7 @@ export const useGestureImageViewer = <T = any>({
61
91
  return () => {
62
92
  runAfterInteractions?.cancel();
63
93
  };
64
- }, [initialIndex, translateY, backdropOpacity]);
94
+ }, [initialIndex, translateY, backdropOpacity, translateX, scale, startScale]);
65
95
 
66
96
  // 인덱스 변경 알림
67
97
  useEffect(() => {
@@ -80,9 +110,26 @@ export const useGestureImageViewer = <T = any>({
80
110
 
81
111
  if (newIndex !== currentIndex && newIndex >= 0 && newIndex < dataLength) {
82
112
  setCurrentIndex(newIndex);
113
+ translateX.value = withTiming(0);
114
+ translateY.value = withTiming(0);
115
+ initialTranslateX.value = withTiming(0);
116
+ initialTranslateY.value = withTiming(0);
117
+ startScale.value = withTiming(1);
118
+ scale.value = withTiming(1);
83
119
  }
84
120
  },
85
- [currentIndex, dataLength, width, enableSwipeGesture],
121
+ [
122
+ currentIndex,
123
+ dataLength,
124
+ width,
125
+ enableSwipeGesture,
126
+ translateX,
127
+ translateY,
128
+ scale,
129
+ initialTranslateX,
130
+ initialTranslateY,
131
+ startScale,
132
+ ],
86
133
  );
87
134
 
88
135
  const goToIndex = useCallback(
@@ -112,18 +159,19 @@ export const useGestureImageViewer = <T = any>({
112
159
  }
113
160
  }, [currentIndex, dataLength, goToIndex]);
114
161
 
115
- const panGesture = useMemo(() => {
162
+ const dismissGesture = useMemo(() => {
116
163
  return Gesture.Pan()
117
164
  .minDistance(10)
165
+ .averageTouches(true)
118
166
  .activeOffsetY([-10, 10])
119
167
  .failOffsetX([-10, 10])
168
+ .enabled(!isZoomed)
120
169
  .onUpdate((event) => {
121
- 'worklet';
122
-
123
170
  translateY.value = event.translationY / resistance;
124
171
  })
125
172
  .onEnd((event) => {
126
173
  'worklet';
174
+
127
175
  if (event.translationY > dismissThreshold && enableDismissGesture && onDismiss) {
128
176
  runOnJS(onDismiss)();
129
177
  return;
@@ -134,24 +182,115 @@ export const useGestureImageViewer = <T = any>({
134
182
  stiffness: 150,
135
183
  });
136
184
  });
137
- }, [translateY, dismissThreshold, enableDismissGesture, onDismiss, resistance]);
185
+ }, [translateY, dismissThreshold, enableDismissGesture, onDismiss, resistance, isZoomed]);
186
+
187
+ const zoomPinchGesture = useMemo(() => {
188
+ return Gesture.Pinch()
189
+ .enabled(enableZoomGesture)
190
+ .onBegin(() => {
191
+ startScale.value = scale.value;
192
+ })
193
+ .onUpdate((event) => {
194
+ scale.value = startScale.value * event.scale;
195
+ })
196
+ .onEnd(() => {
197
+ if (scale.value > maxZoomScale) {
198
+ scale.value = withTiming(maxZoomScale, {
199
+ duration: 300,
200
+ easing: Easing.bezier(0.25, 0.1, 0.25, 1.0),
201
+ });
202
+ return;
203
+ }
204
+
205
+ if (scale.value < 1) {
206
+ scale.value = withTiming(1, {
207
+ duration: 300,
208
+ easing: Easing.bezier(0.25, 0.1, 0.25, 1.0),
209
+ });
210
+ translateX.value = withTiming(0);
211
+ translateY.value = withTiming(0);
212
+ }
213
+ });
214
+ }, [scale, enableZoomGesture, maxZoomScale, translateX, translateY, startScale]);
215
+
216
+ const zoomPanGesture = useMemo(() => {
217
+ return Gesture.Pan()
218
+ .enabled(enableZoomPanGesture && isZoomed)
219
+ .onBegin(() => {
220
+ initialTranslateX.value = translateX.value;
221
+ initialTranslateY.value = translateY.value;
222
+ })
223
+ .onUpdate((event) => {
224
+ 'worklet';
225
+
226
+ if (scale.value > 1) {
227
+ const maxTranslateX = (width * scale.value - width) / 2;
228
+ const maxTranslateY = (screenHeight * scale.value - screenHeight) / 2;
229
+
230
+ translateX.value = Math.max(
231
+ -maxTranslateX,
232
+ Math.min(maxTranslateX, initialTranslateX.value + event.translationX),
233
+ );
234
+ translateY.value = Math.max(
235
+ -maxTranslateY,
236
+ Math.min(maxTranslateY, initialTranslateY.value + event.translationY),
237
+ );
238
+ }
239
+ });
240
+ }, [
241
+ translateX,
242
+ translateY,
243
+ enableZoomPanGesture,
244
+ isZoomed,
245
+ scale,
246
+ initialTranslateX,
247
+ initialTranslateY,
248
+ width,
249
+ screenHeight,
250
+ ]);
251
+
252
+ const doubleTapGesture = useMemo(() => {
253
+ return Gesture.Tap()
254
+ .enabled(enableDoubleTapGesture)
255
+ .numberOfTaps(2)
256
+ .onEnd(() => {
257
+ const nextScale = scale.value > 1 ? 1 : maxZoomScale;
258
+
259
+ translateX.value = withTiming(0, {
260
+ duration: 300,
261
+ easing: Easing.bezier(0.25, 0.1, 0.25, 1.0),
262
+ });
263
+ translateY.value = withTiming(0, {
264
+ duration: 300,
265
+ easing: Easing.bezier(0.25, 0.1, 0.25, 1.0),
266
+ });
267
+
268
+ scale.value = withTiming(nextScale, {
269
+ duration: 300,
270
+ easing: Easing.bezier(0.25, 0.1, 0.25, 1.0),
271
+ });
272
+ });
273
+ }, [scale, enableDoubleTapGesture, maxZoomScale, translateX, translateY]);
274
+
275
+ const zoomGesture = useMemo(() => {
276
+ return Gesture.Simultaneous(zoomPinchGesture, zoomPanGesture, doubleTapGesture);
277
+ }, [zoomPinchGesture, zoomPanGesture, doubleTapGesture]);
138
278
 
139
- // 애니메이션 스타일
140
279
  const animatedStyle = useAnimatedStyle(() => {
141
280
  return {
142
- transform: [{ translateY: translateY.value }],
281
+ transform: [{ translateY: translateY.value }, { translateX: translateX.value }, { scale: scale.value }],
143
282
  };
144
- }, [translateY]);
283
+ });
145
284
 
146
285
  const backdropStyle = useAnimatedStyle(() => {
147
- if (!animateBackdrop) {
286
+ if (!animateBackdrop || scale.value > 1) {
148
287
  return { opacity: 1 };
149
288
  }
150
289
 
151
290
  const opacity = interpolate(translateY.value, [0, 200], [1, 0], 'clamp');
152
291
 
153
292
  return { opacity };
154
- }, [animateBackdrop, translateY]);
293
+ }, [animateBackdrop]);
155
294
 
156
295
  return {
157
296
  // State
@@ -159,6 +298,7 @@ export const useGestureImageViewer = <T = any>({
159
298
  dataLength,
160
299
  translateY,
161
300
  flatListRef,
301
+ isZoomed,
162
302
 
163
303
  // Actions
164
304
  goToPrevious,
@@ -166,7 +306,8 @@ export const useGestureImageViewer = <T = any>({
166
306
  goToIndex,
167
307
 
168
308
  // Gesture
169
- panGesture,
309
+ dismissGesture,
310
+ zoomGesture,
170
311
 
171
312
  // FlatList handlers
172
313
  onMomentumScrollEnd,