@chem-po/react-native 0.0.24 → 0.0.25

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,9 @@
1
1
  import { useScreen } from '@chem-po/react'
2
2
  import { Ionicons } from '@expo/vector-icons'
3
- import React, { useCallback, useMemo, useRef, useState } from 'react'
4
- import { Animated, Image, Modal, StyleSheet, TouchableOpacity, View } from 'react-native'
3
+ import React, { useCallback, useEffect, useMemo, useState } from 'react'
4
+ import { Image, Modal, StyleSheet, TouchableOpacity, View } from 'react-native'
5
5
  import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler'
6
+ import Animated, { useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated'
6
7
  import { LoadingLogo } from '../loading/Loading'
7
8
 
8
9
  interface ImageViewModalProps {
@@ -17,18 +18,15 @@ export const ImageViewModal: React.FC<ImageViewModalProps> = ({ isOpen, onClose,
17
18
  const screenHeight = useScreen(s => s.height)
18
19
  const [imageSize, setImageSize] = useState({ width: screenWidth / 2, height: screenHeight / 2 })
19
20
 
20
- // Animated values for zoom and pan
21
- const scale = useRef(new Animated.Value(1)).current
22
- const translateX = useRef(new Animated.Value(0)).current
23
- const translateY = useRef(new Animated.Value(0)).current
21
+ // Shared values for zoom and pan (reanimated)
22
+ const scale = useSharedValue(1)
23
+ const translateX = useSharedValue(0)
24
+ const translateY = useSharedValue(0)
24
25
 
25
- // Gesture state
26
- const savedScale = useRef(1)
27
- const savedTranslateX = useRef(0)
28
- const savedTranslateY = useRef(0)
29
- const currentScale = useRef(1)
30
- const currentTranslateX = useRef(0)
31
- const currentTranslateY = useRef(0)
26
+ // Saved state for gesture handling
27
+ const savedScale = useSharedValue(1)
28
+ const savedTranslateX = useSharedValue(0)
29
+ const savedTranslateY = useSharedValue(0)
32
30
 
33
31
  const { height, width } = useMemo(() => {
34
32
  if (loading) return imageSize
@@ -51,121 +49,116 @@ export const ImageViewModal: React.FC<ImageViewModalProps> = ({ isOpen, onClose,
51
49
 
52
50
  // Reset zoom and pan when modal opens/closes
53
51
  const resetTransform = useCallback(() => {
54
- scale.setValue(1)
55
- translateX.setValue(0)
56
- translateY.setValue(0)
57
- savedScale.current = 1
58
- savedTranslateX.current = 0
59
- savedTranslateY.current = 0
60
- currentScale.current = 1
61
- currentTranslateX.current = 0
62
- currentTranslateY.current = 0
63
- }, [scale, translateX, translateY])
52
+ scale.value = 1
53
+ translateX.value = 0
54
+ translateY.value = 0
55
+ savedScale.value = 1
56
+ savedTranslateX.value = 0
57
+ savedTranslateY.value = 0
58
+ }, [scale, translateX, translateY, savedScale, savedTranslateX, savedTranslateY])
64
59
 
65
60
  // Reset when modal closes or src changes
66
- React.useEffect(() => {
61
+ useEffect(() => {
67
62
  if (!isOpen || !src) {
68
63
  resetTransform()
69
64
  }
70
65
  }, [isOpen, src, resetTransform])
71
66
 
72
- // Pan gesture
67
+ // Pan gesture with worklet
73
68
  const panGesture = Gesture.Pan()
74
69
  .onUpdate(event => {
70
+ 'worklet'
75
71
  // Only allow panning if zoomed in
76
- if (savedScale.current > 1) {
77
- const newTranslateX = savedTranslateX.current + event.translationX
78
- const newTranslateY = savedTranslateY.current + event.translationY
72
+ if (savedScale.value > 1) {
73
+ const newTranslateX = savedTranslateX.value + event.translationX
74
+ const newTranslateY = savedTranslateY.value + event.translationY
79
75
 
80
76
  // Calculate bounds to prevent panning too far
81
- const maxTranslateX = (width * savedScale.current - width) / 2
82
- const maxTranslateY = (height * savedScale.current - height) / 2
77
+ const maxTranslateX = (width * savedScale.value - width) / 2
78
+ const maxTranslateY = (height * savedScale.value - height) / 2
83
79
 
84
80
  const boundedTranslateX = Math.max(-maxTranslateX, Math.min(maxTranslateX, newTranslateX))
85
81
  const boundedTranslateY = Math.max(-maxTranslateY, Math.min(maxTranslateY, newTranslateY))
86
82
 
87
- translateX.setValue(boundedTranslateX)
88
- translateY.setValue(boundedTranslateY)
89
- currentTranslateX.current = boundedTranslateX
90
- currentTranslateY.current = boundedTranslateY
83
+ translateX.value = boundedTranslateX
84
+ translateY.value = boundedTranslateY
91
85
  }
92
86
  })
93
87
  .onEnd(() => {
94
- savedTranslateX.current = currentTranslateX.current
95
- savedTranslateY.current = currentTranslateY.current
88
+ 'worklet'
89
+ savedTranslateX.value = translateX.value
90
+ savedTranslateY.value = translateY.value
96
91
  })
97
92
 
98
- // Pinch gesture
93
+ // Pinch gesture with worklet
99
94
  const pinchGesture = Gesture.Pinch()
100
95
  .onUpdate(event => {
101
- const newScale = savedScale.current * event.scale
96
+ 'worklet'
97
+ const newScale = savedScale.value * event.scale
102
98
  // Limit zoom between 1x and 5x
103
99
  const boundedScale = Math.max(1, Math.min(5, newScale))
104
- scale.setValue(boundedScale)
105
- currentScale.current = boundedScale
100
+ scale.value = boundedScale
106
101
 
107
102
  // If zooming out to 1x, reset position
108
103
  if (boundedScale <= 1) {
109
- translateX.setValue(0)
110
- translateY.setValue(0)
111
- currentTranslateX.current = 0
112
- currentTranslateY.current = 0
104
+ translateX.value = 0
105
+ translateY.value = 0
113
106
  }
114
107
  })
115
108
  .onEnd(() => {
116
- savedScale.current = currentScale.current
109
+ 'worklet'
110
+ savedScale.value = scale.value
117
111
  // If scale is close to 1, snap back to 1
118
- if (savedScale.current < 1.1) {
119
- Animated.parallel([
120
- Animated.spring(scale, { toValue: 1, useNativeDriver: true }),
121
- Animated.spring(translateX, { toValue: 0, useNativeDriver: true }),
122
- Animated.spring(translateY, { toValue: 0, useNativeDriver: true }),
123
- ]).start()
124
- savedScale.current = 1
125
- savedTranslateX.current = 0
126
- savedTranslateY.current = 0
127
- currentScale.current = 1
128
- currentTranslateX.current = 0
129
- currentTranslateY.current = 0
112
+ if (savedScale.value < 1.1) {
113
+ scale.value = withSpring(1)
114
+ translateX.value = withSpring(0)
115
+ translateY.value = withSpring(0)
116
+ savedScale.value = 1
117
+ savedTranslateX.value = 0
118
+ savedTranslateY.value = 0
130
119
  } else {
131
- savedTranslateX.current = currentTranslateX.current
132
- savedTranslateY.current = currentTranslateY.current
120
+ savedTranslateX.value = translateX.value
121
+ savedTranslateY.value = translateY.value
133
122
  }
134
123
  })
135
124
 
136
- // Double tap to zoom
125
+ // Double tap to zoom with worklet
137
126
  const doubleTapGesture = Gesture.Tap()
138
127
  .numberOfTaps(2)
139
128
  .onEnd(() => {
140
- if (savedScale.current > 1) {
129
+ 'worklet'
130
+ if (savedScale.value > 1) {
141
131
  // Zoom out
142
- Animated.parallel([
143
- Animated.spring(scale, { toValue: 1, useNativeDriver: true }),
144
- Animated.spring(translateX, { toValue: 0, useNativeDriver: true }),
145
- Animated.spring(translateY, { toValue: 0, useNativeDriver: true }),
146
- ]).start()
147
- savedScale.current = 1
148
- savedTranslateX.current = 0
149
- savedTranslateY.current = 0
150
- currentScale.current = 1
151
- currentTranslateX.current = 0
152
- currentTranslateY.current = 0
132
+ scale.value = withSpring(1)
133
+ translateX.value = withSpring(0)
134
+ translateY.value = withSpring(0)
135
+ savedScale.value = 1
136
+ savedTranslateX.value = 0
137
+ savedTranslateY.value = 0
153
138
  } else {
154
139
  // Zoom in to 2x
155
- Animated.spring(scale, { toValue: 2, useNativeDriver: true }).start()
156
- savedScale.current = 2
157
- currentScale.current = 2
140
+ scale.value = withSpring(2)
141
+ savedScale.value = 2
158
142
  }
159
143
  })
160
144
 
161
- // Test simple single tap gesture
162
-
163
- // Combine gestures - simplified approach
145
+ // Combine gestures
164
146
  const combinedGestures = Gesture.Race(
165
147
  doubleTapGesture,
166
148
  Gesture.Simultaneous(panGesture, pinchGesture),
167
149
  )
168
150
 
151
+ // Animated style using reanimated
152
+ const animatedStyle = useAnimatedStyle(() => {
153
+ return {
154
+ transform: [
155
+ { scale: scale.value },
156
+ { translateX: translateX.value },
157
+ { translateY: translateY.value },
158
+ ],
159
+ }
160
+ })
161
+
169
162
  if (!isOpen) {
170
163
  return null
171
164
  }
@@ -188,8 +181,8 @@ export const ImageViewModal: React.FC<ImageViewModalProps> = ({ isOpen, onClose,
188
181
  width,
189
182
  height,
190
183
  opacity: loading ? 0 : 1,
191
- transform: [{ scale }, { translateX }, { translateY }],
192
184
  },
185
+ animatedStyle,
193
186
  ]}>
194
187
  <Image
195
188
  source={src ? { uri: src } : undefined}