@momo-kits/foundation 0.161.2-beta.8 → 0.161.2-reanimated.1

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.
@@ -20,7 +20,7 @@ import { Icon } from '../Icon';
20
20
  import { useHeaderHeight } from '@react-navigation/elements';
21
21
  import Animated, {
22
22
  Easing,
23
- Extrapolate,
23
+ Extrapolation,
24
24
  interpolate,
25
25
  runOnJS,
26
26
  useAnimatedStyle,
@@ -242,7 +242,7 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
242
242
  translateY.value,
243
243
  [0, heightDevice],
244
244
  [1, 0],
245
- Extrapolate.CLAMP,
245
+ Extrapolation.CLAMP,
246
246
  ),
247
247
  };
248
248
  });
@@ -2,13 +2,17 @@ import { BottomTabBarProps } from '@react-navigation/bottom-tabs';
2
2
  import { CommonActions, Route } from '@react-navigation/native';
3
3
  import React, { useCallback, useContext } from 'react';
4
4
  import {
5
- Animated,
6
5
  Platform,
7
6
  StyleSheet,
8
7
  TouchableOpacity,
9
8
  useWindowDimensions,
10
9
  View,
11
10
  } from 'react-native';
11
+ import Animated, {
12
+ useAnimatedStyle,
13
+ useSharedValue,
14
+ withTiming,
15
+ } from 'react-native-reanimated';
12
16
  import { EdgeInsets, useSafeAreaInsets } from 'react-native-safe-area-context';
13
17
  import { Colors, Radius, Styles } from '../../Consts';
14
18
  import { Icon } from '../../Icon';
@@ -57,8 +61,13 @@ export default function BottomTabBar({
57
61
  const useFloating = floatingButton && state.routes.length % 2 === 0;
58
62
  const itemWidth = widthDevice / (state.routes.length + (useFloating ? 1 : 0));
59
63
  // const buildLink = useLinkBuilder();
60
- const indicatorAnimated = React.useRef(new Animated.Value(0)).current;
64
+ const indicatorAnimated = useSharedValue(0);
61
65
  const insets = useSafeAreaInsets();
66
+
67
+ const indicatorStyle = useAnimatedStyle(() => ({
68
+ width: itemWidth,
69
+ transform: [{ translateX: indicatorAnimated.value }],
70
+ }));
62
71
  const paddingBottom = getPaddingBottom(insets);
63
72
  const focusedTab = state?.routes[state.index]?.name || routes[0]?.params;
64
73
 
@@ -199,11 +208,7 @@ export default function BottomTabBar({
199
208
  if (useFloating && index > (state.routes.length - 1) / 2) {
200
209
  index += 1;
201
210
  }
202
- Animated.timing(indicatorAnimated, {
203
- toValue: itemWidth * index,
204
- useNativeDriver: true,
205
- duration: 200,
206
- }).start();
211
+ indicatorAnimated.value = withTiming(itemWidth * index, { duration: 200 });
207
212
  }, [
208
213
  state.index,
209
214
  itemWidth,
@@ -232,7 +237,7 @@ export default function BottomTabBar({
232
237
  };
233
238
 
234
239
  return (
235
- <Animated.View
240
+ <View
236
241
  style={[
237
242
  floatingButton ? styles.tabBarFloatingButton : styles.tabBar,
238
243
  {
@@ -250,12 +255,7 @@ export default function BottomTabBar({
250
255
  <View style={styles.content} accessibilityLabel={'bottom_tab_bar'}>
251
256
  {useFloating && floatingButton?.container}
252
257
  <Animated.View
253
- style={{
254
- width: itemWidth,
255
- position: 'absolute',
256
- top: 0,
257
- transform: [{ translateX: indicatorAnimated }],
258
- }}
258
+ style={[{ position: 'absolute', top: 0 }, indicatorStyle]}
259
259
  >
260
260
  <View
261
261
  style={[
@@ -266,7 +266,7 @@ export default function BottomTabBar({
266
266
  </Animated.View>
267
267
  {renderTabBar()}
268
268
  </View>
269
- </Animated.View>
269
+ </View>
270
270
  );
271
271
  }
272
272
 
@@ -5,16 +5,20 @@ import {
5
5
  useFocusEffect,
6
6
  } from '@react-navigation/native';
7
7
  import { createStackNavigator } from '@react-navigation/stack';
8
- import React, { useContext, useEffect, useLayoutEffect, useRef } from 'react';
8
+ import React, { useContext, useEffect, useLayoutEffect } from 'react';
9
9
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
10
10
  import {
11
- Animated,
12
11
  Platform,
13
12
  StatusBar,
14
13
  StyleSheet,
15
14
  useWindowDimensions,
16
15
  View,
17
16
  } from 'react-native';
17
+ import Animated, {
18
+ useAnimatedStyle,
19
+ useSharedValue,
20
+ withTiming,
21
+ } from 'react-native-reanimated';
18
22
  import { Colors } from '../../Consts';
19
23
  import { Icon } from '../../Icon';
20
24
  import { exportFontFamily, Text } from '../../Text';
@@ -35,11 +39,16 @@ const TabScreen: React.FC<any> = ({ route, navigation }) => {
35
39
 
36
40
  const opacityValue = 0.3;
37
41
  const scaleValue = 0.97;
38
- const opacity = useRef(new Animated.Value(opacityValue)).current;
39
- const scale = useRef(new Animated.Value(scaleValue)).current;
42
+ const opacity = useSharedValue(opacityValue);
43
+ const scale = useSharedValue(scaleValue);
40
44
  const screenName = screen?.name || screen?.type?.name || 'Invalid';
41
45
  const { isBackgroundToForeground } = useAppState();
42
46
 
47
+ const tabAnimatedStyle = useAnimatedStyle(() => ({
48
+ opacity: opacity.value,
49
+ transform: [{ scale: scale.value }],
50
+ }));
51
+
43
52
  const onScreenNavigated = (pre: string, current: string) => {
44
53
  if (!isBackgroundToForeground) {
45
54
  const data: any = {
@@ -69,18 +78,8 @@ const TabScreen: React.FC<any> = ({ route, navigation }) => {
69
78
  useFocusEffect(
70
79
  React.useCallback(
71
80
  () => {
72
- Animated.parallel([
73
- Animated.timing(opacity, {
74
- toValue: 1,
75
- duration: 200,
76
- useNativeDriver: true,
77
- }),
78
- Animated.timing(scale, {
79
- toValue: 1,
80
- duration: 200,
81
- useNativeDriver: true,
82
- }),
83
- ]).start();
81
+ opacity.value = withTiming(1, { duration: 200 });
82
+ scale.value = withTiming(1, { duration: 200 });
84
83
 
85
84
  setTimeout(() => {
86
85
  navigator?.maxApi?.getDataObserver?.(
@@ -96,18 +95,8 @@ const TabScreen: React.FC<any> = ({ route, navigation }) => {
96
95
 
97
96
  return () => {
98
97
  if (navigation.getState().index !== route?.params.index) {
99
- Animated.parallel([
100
- Animated.timing(opacity, {
101
- toValue: opacityValue,
102
- duration: 200,
103
- useNativeDriver: true,
104
- }),
105
- Animated.timing(scale, {
106
- toValue: scaleValue,
107
- duration: 200,
108
- useNativeDriver: true,
109
- }),
110
- ]).start();
98
+ opacity.value = withTiming(opacityValue, { duration: 200 });
99
+ scale.value = withTiming(scaleValue, { duration: 200 });
111
100
  }
112
101
  };
113
102
  },
@@ -147,15 +136,7 @@ const TabScreen: React.FC<any> = ({ route, navigation }) => {
147
136
 
148
137
  if (nested) {
149
138
  return (
150
- <Animated.View
151
- style={[
152
- styles.container,
153
- {
154
- opacity,
155
- transform: [{ scale }],
156
- },
157
- ]}
158
- >
139
+ <Animated.View style={[styles.container, tabAnimatedStyle]}>
159
140
  <Stack.Navigator
160
141
  screenOptions={{
161
142
  headerStyle: {
@@ -228,10 +209,15 @@ const BottomTab: React.FC<BottomTabProps> = ({
228
209
  const dimensions = useWindowDimensions();
229
210
  const insets = useSafeAreaInsets();
230
211
  const initialIndex = tabs.findIndex(i => i.name === initialRouteName);
231
- const indicatorAnimated = useRef(new Animated.Value(0));
232
- const activeIndex = useRef(initialIndex > 0 ? initialIndex : 0);
212
+ const indicatorAnimated = useSharedValue(0);
213
+ const activeIndex = React.useRef(initialIndex > 0 ? initialIndex : 0);
233
214
  const itemWidth = dimensions.width / tabs.length;
234
215
 
216
+ const indicatorStyle = useAnimatedStyle(() => ({
217
+ width: itemWidth,
218
+ transform: [{ translateX: indicatorAnimated.value }],
219
+ }));
220
+
235
221
  useLayoutEffect(() => {
236
222
  navigation?.setOptions({
237
223
  headerShown: false,
@@ -241,20 +227,16 @@ const BottomTab: React.FC<BottomTabProps> = ({
241
227
  }, [navigation]);
242
228
 
243
229
  useEffect(() => {
244
- Animated.timing(indicatorAnimated.current, {
245
- toValue: itemWidth * activeIndex.current,
246
- useNativeDriver: true,
230
+ indicatorAnimated.value = withTiming(itemWidth * activeIndex.current, {
247
231
  duration: 200,
248
- }).start();
249
- }, [itemWidth]);
232
+ });
233
+ }, [itemWidth, indicatorAnimated]);
250
234
 
251
235
  const onFocus = (e: any) => {
252
236
  activeIndex.current = tabs.findIndex(i => e.target.includes(i.name));
253
- Animated.timing(indicatorAnimated.current, {
254
- toValue: itemWidth * activeIndex.current,
255
- useNativeDriver: true,
237
+ indicatorAnimated.value = withTiming(itemWidth * activeIndex.current, {
256
238
  duration: 200,
257
- }).start();
239
+ });
258
240
  };
259
241
 
260
242
  const handler: {
@@ -294,13 +276,7 @@ const BottomTab: React.FC<BottomTabProps> = ({
294
276
  ]}
295
277
  >
296
278
  <Animated.View
297
- style={[
298
- styles.indicatorContainer,
299
- {
300
- width: itemWidth,
301
- transform: [{ translateX: indicatorAnimated.current }],
302
- },
303
- ]}
279
+ style={[styles.indicatorContainer, indicatorStyle]}
304
280
  >
305
281
  <View
306
282
  style={[
@@ -1,93 +1,75 @@
1
- import React, { useEffect, useRef } from 'react';
2
- import { Animated, View } from 'react-native';
1
+ import React, { useEffect } from 'react';
2
+ import { View } from 'react-native';
3
+ import Animated, {
4
+ cancelAnimation,
5
+ useAnimatedStyle,
6
+ useSharedValue,
7
+ withRepeat,
8
+ withSequence,
9
+ withSpring,
10
+ withTiming,
11
+ } from 'react-native-reanimated';
3
12
  import { BadgeDotProps } from './types';
4
13
  import styles from './styles';
5
14
 
6
15
  const DURATION = 500;
7
16
 
8
17
  const BadgeDotAnimation = ({ size, style }: BadgeDotProps) => {
9
- // Refs for animated values
10
- const scaleAnim = useRef(new Animated.Value(1)).current;
11
- const waveScaleAnim = useRef(new Animated.Value(1)).current;
12
- const waveOpacityAnim = useRef(new Animated.Value(0)).current;
18
+ const scaleAnim = useSharedValue(1);
19
+ const waveScaleAnim = useSharedValue(1);
20
+ const waveOpacityAnim = useSharedValue(0);
13
21
 
14
22
  const dotStyle =
15
23
  size === 'small' ? styles.dotAnimationSmall : styles.dotAnimation;
16
24
  const waveStyle = size === 'small' ? styles.waveSmall : styles.wave;
17
25
 
18
26
  useEffect(() => {
19
- // Infinite loop animation for the scale and wave effect
20
- const animation = Animated.loop(
21
- Animated.parallel([
22
- // Dot pulse animation
23
- Animated.sequence([
24
- Animated.spring(scaleAnim, {
25
- toValue: 1, // Scale up slightly
26
- friction: 5, // Controls the "bounciness" of the spring
27
- tension: 30, // Controls the "stiffness" of the spring
28
- useNativeDriver: true,
29
- }),
30
- Animated.spring(scaleAnim, {
31
- toValue: 1.1,
32
- friction: 5,
33
- tension: 30,
34
- useNativeDriver: true,
35
- }),
36
- ]), // Wave animation
37
- Animated.sequence([
38
- Animated.timing(waveScaleAnim, {
39
- toValue: 2.5,
40
- duration: DURATION * 3,
41
- useNativeDriver: true,
42
- }),
43
- Animated.timing(waveScaleAnim, {
44
- toValue: 1, // Reset wave size
45
- duration: 0,
46
- useNativeDriver: true,
47
- }),
48
- ]), // Wave opacity animation
49
- Animated.sequence([
50
- Animated.timing(waveOpacityAnim, {
51
- toValue: 0.3, // Wave becomes visible
52
- duration: DURATION * 2,
53
- useNativeDriver: true,
54
- }),
55
- Animated.timing(waveOpacityAnim, {
56
- toValue: 0, // Wave fades out
57
- duration: DURATION,
58
- useNativeDriver: true,
59
- }),
60
- ]),
61
- ]),
62
- );
63
- animation.start();
27
+ const springCfg = { damping: 5, stiffness: 30, mass: 1 };
64
28
 
29
+ scaleAnim.value = withRepeat(
30
+ withSequence(
31
+ withSpring(1, springCfg),
32
+ withSpring(1.1, springCfg),
33
+ ),
34
+ -1,
35
+ false,
36
+ );
37
+ waveScaleAnim.value = withRepeat(
38
+ withSequence(
39
+ withTiming(2.5, { duration: DURATION * 3 }),
40
+ withTiming(1, { duration: 0 }),
41
+ ),
42
+ -1,
43
+ false,
44
+ );
45
+ waveOpacityAnim.value = withRepeat(
46
+ withSequence(
47
+ withTiming(0.3, { duration: DURATION * 2 }),
48
+ withTiming(0, { duration: DURATION }),
49
+ ),
50
+ -1,
51
+ false,
52
+ );
65
53
  return () => {
66
- animation.stop();
54
+ cancelAnimation(scaleAnim);
55
+ cancelAnimation(waveScaleAnim);
56
+ cancelAnimation(waveOpacityAnim);
67
57
  };
68
58
  }, [scaleAnim, waveOpacityAnim, waveScaleAnim]);
69
59
 
60
+ const waveAnimatedStyle = useAnimatedStyle(() => ({
61
+ transform: [{ scale: waveScaleAnim.value }],
62
+ opacity: waveOpacityAnim.value,
63
+ }));
64
+
65
+ const dotAnimatedStyle = useAnimatedStyle(() => ({
66
+ transform: [{ scale: scaleAnim.value }],
67
+ }));
68
+
70
69
  return (
71
70
  <View style={[styles.dotAnimationContainer, style]}>
72
- {/* Wave Animation */}
73
- <Animated.View
74
- style={[
75
- waveStyle,
76
- {
77
- transform: [{ scale: waveScaleAnim }],
78
- opacity: waveOpacityAnim,
79
- },
80
- ]}
81
- />
82
- {/* Dot Animation */}
83
- <Animated.View
84
- style={[
85
- dotStyle,
86
- {
87
- transform: [{ scale: scaleAnim }],
88
- },
89
- ]}
90
- />
71
+ <Animated.View style={[waveStyle, waveAnimatedStyle]} />
72
+ <Animated.View style={[dotStyle, dotAnimatedStyle]} />
91
73
  </View>
92
74
  );
93
75
  };
package/Button/index.tsx CHANGED
@@ -1,6 +1,5 @@
1
1
  import React, { FC, useContext, useRef } from 'react';
2
2
  import {
3
- Animated,
4
3
  StyleSheet,
5
4
  TouchableOpacity,
6
5
  TouchableOpacityProps,
@@ -29,8 +28,6 @@ import Reanimated, {
29
28
  withTiming,
30
29
  } from 'react-native-reanimated';
31
30
 
32
- const AnimationLinear = Animated.createAnimatedComponent(LinearGradient);
33
-
34
31
  export interface ButtonProps extends TouchableOpacityProps {
35
32
  /**
36
33
  * Defines the visual style of the button.
@@ -359,7 +356,7 @@ const Button: FC<ButtonProps> = ({
359
356
  {renderTitle()}
360
357
  {renderIcon('right')}
361
358
  {gradientPros && (
362
- <AnimationLinear {...gradientPros} style={styles.gradientView} />
359
+ <LinearGradient {...gradientPros} style={styles.gradientView} />
363
360
  )}
364
361
  {gradientPros && <View style={styles.strokeView} />}
365
362
  </Reanimated.View>
@@ -8,13 +8,22 @@ import React, {
8
8
  useState,
9
9
  } from 'react';
10
10
  import {
11
- Animated,
12
11
  PixelRatio,
13
12
  TextInput,
14
13
  TextInputFocusEvent,
15
14
  TouchableOpacity,
16
15
  View,
17
16
  } from 'react-native';
17
+ import Animated, {
18
+ cancelAnimation,
19
+ Easing,
20
+ useAnimatedStyle,
21
+ useSharedValue,
22
+ withDelay,
23
+ withRepeat,
24
+ withSequence,
25
+ withTiming,
26
+ } from 'react-native-reanimated';
18
27
  import { useComponentId } from '../Application';
19
28
  import { Spacing, Styles } from '../Consts';
20
29
  import { useScaleSize, Text } from '../Text';
@@ -32,37 +41,43 @@ import {
32
41
  const OTPCaret: FC<CaretProps> = ({ index, length }) => {
33
42
  const DURATION = 300;
34
43
  const { theme } = useContext(ApplicationContext);
35
- const opacity = useRef(new Animated.Value(0)).current;
44
+ const opacity = useSharedValue(0);
36
45
 
37
46
  useEffect(() => {
38
- Animated.loop(
39
- Animated.sequence([
40
- Animated.timing(opacity, {
41
- toValue: 1,
42
- duration: DURATION,
43
- useNativeDriver: true,
44
- }),
45
- Animated.delay(DURATION * 2),
46
- Animated.timing(opacity, {
47
- toValue: 0,
48
- duration: DURATION,
49
- useNativeDriver: true,
50
- }),
51
- ]),
52
- ).start();
47
+ opacity.value = withRepeat(
48
+ withSequence(
49
+ withTiming(1, { duration: DURATION, easing: Easing.linear }),
50
+ withDelay(
51
+ DURATION * 2,
52
+ withTiming(0, { duration: DURATION, easing: Easing.linear }),
53
+ ),
54
+ ),
55
+ -1,
56
+ false,
57
+ );
58
+ return () => {
59
+ cancelAnimation(opacity);
60
+ };
53
61
  }, [opacity]);
62
+
63
+ const animatedStyle = useAnimatedStyle(() => ({
64
+ opacity: opacity.value,
65
+ }));
66
+
54
67
  const spacingStyle = !isNaN(Number(length)) &&
55
68
  index !== Number(length) - 1 && { marginRight: Spacing.L };
56
69
 
57
70
  return (
58
71
  <View style={[Styles.rowCenter, spacingStyle]}>
59
72
  <Animated.View
60
- style={{
61
- height: useScaleSize(12),
62
- width: 1,
63
- backgroundColor: theme.colors.primary,
64
- opacity,
65
- }}
73
+ style={[
74
+ {
75
+ height: useScaleSize(12),
76
+ width: 1,
77
+ backgroundColor: theme.colors.primary,
78
+ },
79
+ animatedStyle,
80
+ ]}
66
81
  />
67
82
  <Text color={theme.colors.text.hint} typography={'body_default_regular'}>
68
83
  -
@@ -1,5 +1,10 @@
1
- import React, { FC, useContext, useEffect, useRef } from 'react';
2
- import { Animated, View } from 'react-native';
1
+ import React, { FC, useContext, useEffect } from 'react';
2
+ import { View } from 'react-native';
3
+ import Animated, {
4
+ useAnimatedStyle,
5
+ useSharedValue,
6
+ withTiming,
7
+ } from 'react-native-reanimated';
3
8
  import styles from './styles';
4
9
  import { ProgressBarProps } from './types';
5
10
  import { ApplicationContext } from '../Context';
@@ -7,20 +12,15 @@ import { Radius } from '../Consts';
7
12
 
8
13
  const ProgressBar: FC<ProgressBarProps> = ({ percent = 0, style }) => {
9
14
  const { theme } = useContext(ApplicationContext);
10
- const animation = useRef(new Animated.Value(0)).current;
15
+ const animation = useSharedValue(0);
11
16
 
12
17
  useEffect(() => {
13
- Animated.timing(animation, {
14
- toValue: percent,
15
- duration: 200,
16
- useNativeDriver: false,
17
- }).start();
18
+ animation.value = withTiming(percent, { duration: 200 });
18
19
  }, [percent, animation]);
19
20
 
20
- const width = animation.interpolate({
21
- inputRange: [0, 100],
22
- outputRange: ['0%', '100%'],
23
- });
21
+ const animatedStyle = useAnimatedStyle(() => ({
22
+ width: `${Math.min(Math.max(animation.value, 0), 100)}%`,
23
+ }));
24
24
 
25
25
  return (
26
26
  <View
@@ -31,12 +31,14 @@ const ProgressBar: FC<ProgressBarProps> = ({ percent = 0, style }) => {
31
31
  ]}
32
32
  >
33
33
  <Animated.View
34
- style={{
35
- height: 4,
36
- borderRadius: Radius.XXS,
37
- width,
38
- backgroundColor: theme.colors.primary,
39
- }}
34
+ style={[
35
+ {
36
+ height: 4,
37
+ borderRadius: Radius.XXS,
38
+ backgroundColor: theme.colors.primary,
39
+ },
40
+ animatedStyle,
41
+ ]}
40
42
  />
41
43
  </View>
42
44
  );
@@ -1,5 +1,5 @@
1
1
  import React, { FC, useContext } from 'react';
2
- import { Animated } from 'react-native';
2
+ import { View } from 'react-native';
3
3
  import styles from './styles';
4
4
  import { DotProps } from './types';
5
5
  import { ApplicationContext } from '../Context';
@@ -13,7 +13,7 @@ const Dot: FC<DotProps> = ({ active, style }) => {
13
13
  { backgroundColor: theme.colors.background.pressed },
14
14
  ];
15
15
 
16
- return <Animated.View style={[style, dotStyle]} />;
16
+ return <View style={[style, dotStyle]} />;
17
17
  };
18
18
 
19
19
  export default Dot;
@@ -1,5 +1,12 @@
1
- import React, { FC, useContext, useRef, useState } from 'react';
2
- import { Animated, View } from 'react-native';
1
+ import React, { FC, useContext, useState } from 'react';
2
+ import { View } from 'react-native';
3
+ import Animated, {
4
+ Extrapolation,
5
+ interpolate,
6
+ useAnimatedScrollHandler,
7
+ useAnimatedStyle,
8
+ useSharedValue,
9
+ } from 'react-native-reanimated';
3
10
  import { ScrollIndicatorProps } from './types';
4
11
  import styles from './styles';
5
12
  import { ApplicationContext, MiniAppContext } from '../Context';
@@ -13,40 +20,37 @@ const PaginationScroll: FC<ScrollIndicatorProps> = ({
13
20
  }) => {
14
21
  const { theme } = useContext(ApplicationContext);
15
22
  const context = useContext<any>(MiniAppContext);
16
- const left = useRef(new Animated.Value(0)).current;
23
+ const left = useSharedValue(0);
17
24
  const [scrollViewWidth, setScrollViewWidth] = useState(0);
18
25
  const [scrollContentWidth, setScrollContentWidth] = useState(0);
19
26
 
20
27
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
21
28
 
22
- const translateX = () => {
23
- if (scrollViewWidth && scrollContentWidth) {
24
- const value = left.interpolate({
25
- inputRange: [0, scrollContentWidth - scrollViewWidth],
26
- outputRange: [0, INDICATOR_CONTAINER_WIDTH - INDICATOR_WIDTH],
27
- extrapolate: 'clamp',
28
- });
29
- return { transform: [{ translateX: value }] };
29
+ const onScroll = useAnimatedScrollHandler({
30
+ onScroll: (event) => {
31
+ left.value = event.contentOffset.x;
32
+ },
33
+ });
34
+
35
+ const indicatorStyle = useAnimatedStyle(() => {
36
+ if (!scrollViewWidth || !scrollContentWidth) {
37
+ return {};
30
38
  }
31
- return {};
32
- };
39
+ const value = interpolate(
40
+ left.value,
41
+ [0, scrollContentWidth - scrollViewWidth],
42
+ [0, INDICATOR_CONTAINER_WIDTH - INDICATOR_WIDTH],
43
+ Extrapolation.CLAMP,
44
+ );
45
+ return { transform: [{ translateX: value }] };
46
+ });
33
47
 
34
48
  const renderScrollView = () => {
35
49
  return (
36
50
  <Animated.ScrollView
37
- ref={scrollViewRef}
38
- onScroll={Animated.event(
39
- [
40
- {
41
- nativeEvent: {
42
- contentOffset: {
43
- x: left,
44
- },
45
- },
46
- },
47
- ],
48
- { useNativeDriver: true },
49
- )}
51
+ ref={scrollViewRef as any}
52
+ onScroll={onScroll}
53
+ scrollEventThrottle={16}
50
54
  alwaysBounceHorizontal={false}
51
55
  showsHorizontalScrollIndicator={false}
52
56
  horizontal
@@ -76,7 +80,7 @@ const PaginationScroll: FC<ScrollIndicatorProps> = ({
76
80
  {
77
81
  backgroundColor: theme.colors.primary,
78
82
  },
79
- translateX(),
83
+ indicatorStyle,
80
84
  ]}
81
85
  />
82
86
  </View>
@@ -1,13 +1,20 @@
1
- import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
1
+ import React, { useContext, useEffect, useState } from 'react';
2
2
  import {
3
- Animated,
4
- Easing,
5
3
  LayoutAnimation,
6
4
  Platform,
7
5
  StyleSheet,
8
6
  UIManager,
9
7
  View,
10
8
  } from 'react-native';
9
+ import Animated, {
10
+ cancelAnimation,
11
+ Easing,
12
+ interpolate,
13
+ useAnimatedStyle,
14
+ useSharedValue,
15
+ withRepeat,
16
+ withTiming,
17
+ } from 'react-native-reanimated';
11
18
  import LinearGradient from 'react-native-linear-gradient';
12
19
  import { SkeletonTypes } from './types';
13
20
  import { Colors, Styles } from '../Consts';
@@ -20,32 +27,31 @@ const Skeleton: React.FC<SkeletonTypes> = ({ style }) => {
20
27
  const PRIMARY_COLOR = Colors.black_05;
21
28
  const HIGHLIGHT_COLOR1 = Colors.black_05;
22
29
  const HIGHLIGHT_COLOR2 = Colors.black_03;
23
- const beginShimmerPosition = useRef(new Animated.Value(0)).current;
30
+ const progress = useSharedValue(0);
24
31
 
25
32
  const shimmerColors = [HIGHLIGHT_COLOR1, HIGHLIGHT_COLOR2, HIGHLIGHT_COLOR1];
26
- const linearTranslate = beginShimmerPosition.interpolate({
27
- inputRange: [0, 1],
28
- outputRange: [-width, width],
29
- });
30
- const animatedValue = useMemo(() => {
31
- return Animated.loop(
32
- Animated.timing(beginShimmerPosition, {
33
- toValue: 1,
33
+
34
+ useEffect(() => {
35
+ progress.value = 0;
36
+ progress.value = withRepeat(
37
+ withTiming(1, {
34
38
  duration: 1000,
35
39
  easing: Easing.linear,
36
- useNativeDriver: Platform.OS !== 'web',
37
40
  }),
41
+ -1,
42
+ false,
38
43
  );
39
- }, [beginShimmerPosition]);
40
-
41
- useEffect(() => {
42
- animatedValue.start();
43
44
  screen?.onLoading?.(true);
44
45
  return () => {
45
- animatedValue.stop();
46
+ cancelAnimation(progress);
46
47
  screen?.onLoading?.(false);
47
48
  };
48
- }, [animatedValue, screen]);
49
+ }, [progress, screen]);
50
+
51
+ const shimmerStyle = useAnimatedStyle(() => {
52
+ const translateX = interpolate(progress.value, [0, 1], [-width, width]);
53
+ return { transform: [{ translateX }] };
54
+ });
49
55
 
50
56
  const onLayout = (newWidth: number) => {
51
57
  if (newWidth !== width) {
@@ -60,11 +66,13 @@ const Skeleton: React.FC<SkeletonTypes> = ({ style }) => {
60
66
  style={[Styles.flex, { backgroundColor: PRIMARY_COLOR }]}
61
67
  >
62
68
  <Animated.View
63
- style={{
64
- transform: [{ translateX: linearTranslate }],
65
- width: '60%',
66
- height: '100%',
67
- }}
69
+ style={[
70
+ {
71
+ width: '60%',
72
+ height: '100%',
73
+ },
74
+ shimmerStyle,
75
+ ]}
68
76
  >
69
77
  <LinearGradient
70
78
  style={[StyleSheet.absoluteFill]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.161.2-beta.8",
3
+ "version": "0.161.2-reanimated.1",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {},