@momo-kits/foundation 0.161.2-beta.7 → 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.
@@ -1,11 +1,5 @@
1
1
  import React, { FC, useContext } from 'react';
2
- import { Platform, StyleSheet } from 'react-native';
3
- import Animated, {
4
- Extrapolation,
5
- interpolate,
6
- useAnimatedStyle,
7
- type SharedValue,
8
- } from 'react-native-reanimated';
2
+ import { Animated, Platform, StyleSheet } from 'react-native';
9
3
  import { Image } from '../../Image';
10
4
  import { Colors } from '../../Consts';
11
5
  import { ApplicationContext } from '../../Context';
@@ -14,26 +8,16 @@ type BackgroundImageViewProps = {
14
8
  useShadowHeader: boolean;
15
9
  headerBackground?: string;
16
10
  heightHeader: number | `${number}%`;
17
- animatedValue: SharedValue<number>;
11
+ opacityBackground: Animated.AnimatedInterpolation<any>;
18
12
  };
19
13
 
20
14
  const BackgroundImageView: FC<BackgroundImageViewProps> = ({
21
15
  useShadowHeader = true,
22
16
  headerBackground = null,
23
17
  heightHeader,
24
- animatedValue,
18
+ opacityBackground,
25
19
  }) => {
26
20
  const { theme } = useContext(ApplicationContext);
27
-
28
- const opacityStyle = useAnimatedStyle(() => ({
29
- opacity: interpolate(
30
- animatedValue.value,
31
- [0, 52],
32
- [0, 1],
33
- Extrapolation.CLAMP,
34
- ),
35
- }));
36
-
37
21
  if (Platform.OS === 'android') {
38
22
  return (
39
23
  <Animated.View
@@ -43,8 +27,8 @@ const BackgroundImageView: FC<BackgroundImageViewProps> = ({
43
27
  {
44
28
  height: heightHeader,
45
29
  backgroundColor: theme.colors.background.surface,
30
+ opacity: opacityBackground,
46
31
  },
47
- opacityStyle,
48
32
  ]}
49
33
  >
50
34
  {headerBackground && (
@@ -67,8 +51,8 @@ const BackgroundImageView: FC<BackgroundImageViewProps> = ({
67
51
  {
68
52
  height: heightHeader,
69
53
  backgroundColor: theme.colors.background.surface,
54
+ opacity: opacityBackground,
70
55
  },
71
- opacityStyle,
72
56
  ]}
73
57
  />
74
58
  <Animated.View
@@ -76,8 +60,8 @@ const BackgroundImageView: FC<BackgroundImageViewProps> = ({
76
60
  styles.hidden,
77
61
  {
78
62
  height: heightHeader,
63
+ opacity: opacityBackground,
79
64
  },
80
- opacityStyle,
81
65
  ]}
82
66
  >
83
67
  {headerBackground && (
@@ -1,49 +1,46 @@
1
- import { StyleSheet } from 'react-native';
2
- import Animated, {
3
- Extrapolation,
4
- interpolate,
5
- useAnimatedStyle,
6
- } from 'react-native-reanimated';
1
+ import { Animated, StyleSheet } from 'react-native';
7
2
  import { type HeaderAnimatedProps } from '../types';
8
3
  import React from 'react';
9
4
  import { Image } from '../../Image';
10
5
  import { Colors } from '../../Consts';
11
6
 
7
+ /**
8
+ * default header banner for header animated
9
+ */
12
10
  const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
13
11
  animatedValue,
14
12
  image,
15
13
  useScale = true,
16
14
  children,
17
15
  }) => {
18
- const animatedStyle = useAnimatedStyle(() => {
19
- const scale = useScale
20
- ? interpolate(
21
- animatedValue.value,
22
- [-300, 0, 300],
23
- [4, 1, 1],
24
- Extrapolation.CLAMP,
25
- )
26
- : 1;
27
- const opacity = interpolate(
28
- animatedValue.value,
29
- [0, 150, 300],
30
- [1, 0.5, 0],
31
- Extrapolation.CLAMP,
32
- );
33
- const translateY = interpolate(
34
- animatedValue.value,
35
- [-300, 0],
36
- [-80, -2],
37
- Extrapolation.CLAMP,
38
- );
39
- return {
40
- opacity,
41
- transform: [{ scale }, { translateY }],
42
- };
16
+ const scale = useScale
17
+ ? animatedValue.interpolate({
18
+ inputRange: [-300, 0, 300],
19
+ outputRange: [4, 1, 1],
20
+ extrapolate: 'clamp',
21
+ })
22
+ : 1;
23
+ const opacity = animatedValue.interpolate({
24
+ inputRange: [0, 150, 300],
25
+ outputRange: [1, 0.5, 0],
26
+ extrapolate: 'clamp',
27
+ });
28
+ const translateY = animatedValue.interpolate({
29
+ inputRange: [-300, 0],
30
+ outputRange: [-80, -2],
31
+ extrapolate: 'clamp',
43
32
  });
44
33
 
45
34
  return (
46
- <Animated.View style={[styles.container, animatedStyle]}>
35
+ <Animated.View
36
+ style={[
37
+ styles.container,
38
+ {
39
+ opacity: opacity,
40
+ transform: [{ scale }, { translateY }],
41
+ },
42
+ ]}
43
+ >
47
44
  {children ?? (
48
45
  <Image
49
46
  source={{
@@ -1,15 +1,19 @@
1
1
  import LinearGradient from 'react-native-linear-gradient';
2
- import { StyleSheet, View } from 'react-native';
2
+ import { Animated, StyleSheet, View } from 'react-native';
3
3
  import React, { useContext } from 'react';
4
- import Animated, { useSharedValue } from 'react-native-reanimated';
5
4
  import { HeaderBackgroundProps } from '../types';
6
5
  import { ApplicationContext, MiniAppContext } from '../../Context';
7
6
  import { Colors, Styles } from '../../Consts';
8
7
  import { Image } from '../../Image';
9
8
  import BackgroundImageView from './BackgroundImageView';
10
9
 
10
+ const LinearGradientAnimated = Animated.createAnimatedComponent(LinearGradient);
11
+
12
+ /**
13
+ * header background for default
14
+ */
11
15
  const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
12
- animatedValue,
16
+ animatedValue = new Animated.Value(0),
13
17
  useGradient = true,
14
18
  useShadowHeader = true,
15
19
  gradientColor: customGradientColor,
@@ -19,35 +23,37 @@ const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
19
23
  const context = useContext<any>(MiniAppContext);
20
24
  const gradientColor = customGradientColor ?? theme.colors.gradient;
21
25
  const headerImage = headerBackground ?? theme.assets?.headerBackground;
22
- const fallback = useSharedValue(0);
23
- const sv = animatedValue ?? fallback;
24
26
 
25
27
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
26
28
 
29
+ const opacityBackground = animatedValue?.interpolate({
30
+ inputRange: [0, 52],
31
+ outputRange: [0, 1],
32
+ extrapolate: 'clamp',
33
+ });
34
+
27
35
  return (
28
36
  <View style={[Styles.flex, showBaseLineDebug && styles.debugBaseLine]}>
29
37
  <BackgroundImageView
30
38
  useShadowHeader={useShadowHeader}
31
39
  heightHeader={'100%'}
32
- animatedValue={sv}
40
+ opacityBackground={opacityBackground}
33
41
  headerBackground={headerImage}
34
42
  />
35
43
  <View style={styles.gradientContainer}>
36
44
  {useGradient && !!gradientColor && (
37
- <Animated.View style={styles.extendedHeader}>
38
- <LinearGradient
39
- colors={[gradientColor, gradientColor + '00']}
40
- style={StyleSheet.absoluteFill}
41
- >
42
- {!!headerImage && (
43
- <Image
44
- style={styles.headerBackground}
45
- source={{ uri: headerImage }}
46
- loading={false}
47
- />
48
- )}
49
- </LinearGradient>
50
- </Animated.View>
45
+ <LinearGradientAnimated
46
+ colors={[gradientColor, gradientColor + '00']}
47
+ style={styles.extendedHeader}
48
+ >
49
+ {!!headerImage && (
50
+ <Image
51
+ style={styles.headerBackground}
52
+ source={{ uri: headerImage }}
53
+ loading={false}
54
+ />
55
+ )}
56
+ </LinearGradientAnimated>
51
57
  )}
52
58
  </View>
53
59
  </View>
@@ -1,14 +1,7 @@
1
- import React, { Ref, useContext } from 'react';
1
+ import React, { Ref, useContext, useEffect, useRef } from 'react';
2
2
  import LinearGradient from 'react-native-linear-gradient';
3
- import { Dimensions, Platform, StyleSheet, View } from 'react-native';
4
- import Animated, {
5
- Extrapolation,
6
- interpolate,
7
- useAnimatedStyle,
8
- useSharedValue,
9
- type SharedValue,
10
- } from 'react-native-reanimated';
11
3
  import { ApplicationContext, MiniAppContext } from '../../Context';
4
+ import { Animated, Dimensions, Platform, StyleSheet, View } from 'react-native';
12
5
  import { HeaderType } from '../../Layout/types';
13
6
  import { InputRef, InputSearch } from '../../Input';
14
7
  import Navigation from '../Navigation';
@@ -21,10 +14,15 @@ const SCREEN_PADDING = 12;
21
14
  const BACK_WIDTH = 28;
22
15
 
23
16
  const { width: screenWidth } = Dimensions.get('window');
17
+ const LinearGradientAnimated = Animated.createAnimatedComponent(LinearGradient);
24
18
 
19
+ /**
20
+ * Header extended with background image
21
+ * @constructor
22
+ */
25
23
  const HeaderExtendHeader: React.FC<{
26
24
  headerType?: HeaderType;
27
- animatedValue: SharedValue<number>;
25
+ animatedValue: Animated.Value;
28
26
  heightHeader: number;
29
27
  headerRightWidth: number;
30
28
  inputSearchProps?: SearchHeaderProps;
@@ -40,132 +38,118 @@ const HeaderExtendHeader: React.FC<{
40
38
  headerRightWidth = 73,
41
39
  inputSearchProps,
42
40
  inputSearchRef,
43
- useShadowHeader: useShadowHeaderProp = true,
41
+ useShadowHeader = true,
44
42
  gradientColor: customGradientColor,
45
43
  headerBackground: customBackground,
46
44
  }) => {
47
45
  const { theme } = useContext(ApplicationContext);
48
46
  const context = useContext<any>(MiniAppContext);
49
- const fallback = useSharedValue(0);
50
- const sv = animatedValue ?? fallback;
47
+ const animated = useRef(new Animated.Value(0));
51
48
  const gradientColor = customGradientColor ?? theme.colors.gradient;
52
49
  const headerBackground = customBackground ?? theme.assets?.headerBackground;
53
50
  const leftPosition = inputSearchProps?.leftPosition || BACK_WIDTH + 20;
54
51
 
55
- let useShadowHeader = useShadowHeaderProp;
56
- if (inputSearchProps && Platform.OS === 'android') {
57
- useShadowHeader = false;
58
- }
59
-
60
52
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
61
53
 
62
- const heightStyle = useAnimatedStyle(() => ({
63
- height: interpolate(
64
- sv.value,
65
- [0, 100],
66
- [heightHeader + 52, heightHeader],
67
- Extrapolation.CLAMP,
68
- ),
69
- }));
54
+ const opacityBackground = animatedValue?.interpolate({
55
+ inputRange: [0, 52],
56
+ outputRange: [0, 1],
57
+ extrapolate: 'clamp',
58
+ });
59
+ const opacityGradient = animatedValue?.interpolate({
60
+ inputRange: [0, 52],
61
+ outputRange: [1, 0],
62
+ extrapolate: 'clamp',
63
+ });
70
64
 
71
- const gradientOpacityStyle = useAnimatedStyle(() => ({
72
- opacity: interpolate(sv.value, [0, 52], [1, 0], Extrapolation.CLAMP),
73
- }));
65
+ useEffect(() => {
66
+ const listener = animatedValue.addListener(({ value }) => {
67
+ animated.current.setValue(value);
68
+ });
69
+ return () => {
70
+ animatedValue?.removeListener(listener);
71
+ };
72
+ }, [animatedValue]);
74
73
 
75
- const searchTranslateStyle = useAnimatedStyle(() => ({
76
- transform: [
77
- {
78
- translateX: interpolate(
79
- sv.value,
80
- [0, 100],
81
- [SCREEN_PADDING, leftPosition],
82
- Extrapolation.CLAMP,
83
- ),
84
- },
85
- ],
86
- width: interpolate(
87
- sv.value,
88
- [0, 100],
89
- [
90
- screenWidth - SCREEN_PADDING * 2,
91
- screenWidth - leftPosition - 12 - headerRightWidth,
92
- ],
93
- Extrapolation.CLAMP,
94
- ),
95
- }));
74
+ const height = animated.current.interpolate({
75
+ inputRange: [0, 100],
76
+ outputRange: [heightHeader + 52, heightHeader],
77
+ extrapolate: 'clamp',
78
+ });
96
79
 
97
- const searchBackgroundStyle = useAnimatedStyle(() => {
98
- const t = Math.min(Math.max(sv.value / 100, 0), 1);
99
- return { backgroundColor: t === 0
100
- ? theme.colors.background.surface
101
- : theme.colors.background.default };
80
+ const translateX = animated.current.interpolate({
81
+ inputRange: [0, 100],
82
+ outputRange: [SCREEN_PADDING, leftPosition],
83
+ extrapolate: 'clamp',
102
84
  });
103
85
 
104
- const extendedHeightStyle = useAnimatedStyle(() => ({
105
- height: interpolate(
106
- sv.value,
107
- [0, 100],
108
- [heightHeader + 52, heightHeader],
109
- Extrapolation.CLAMP,
110
- ),
111
- }));
86
+ const backgroundColor = animated.current.interpolate({
87
+ inputRange: [0, 100],
88
+ outputRange: [
89
+ theme.colors.background.surface,
90
+ theme.colors.background.default,
91
+ ],
92
+ extrapolate: 'clamp',
93
+ });
94
+
95
+ if (inputSearchProps && Platform.OS === 'android') {
96
+ useShadowHeader = false;
97
+ }
112
98
 
113
99
  if (inputSearchProps) {
114
100
  return (
115
101
  <View style={[{ zIndex: 0 }, showBaseLineDebug && styles.debugBaseLine]}>
116
- <Animated.View style={heightStyle} />
102
+ <Animated.View style={{ height: height }} />
117
103
  <BackgroundImageView
118
104
  useShadowHeader={useShadowHeader}
119
105
  heightHeader={heightHeader}
120
- animatedValue={sv}
106
+ opacityBackground={opacityBackground}
121
107
  headerBackground={headerBackground}
122
108
  />
123
109
  <Animated.View
124
110
  style={[
125
111
  styles.headerBox,
126
- headerType === 'extended'
127
- ? heightStyle
128
- : { height: heightHeader },
112
+ { height: headerType === 'extended' ? height : heightHeader },
129
113
  ]}
130
114
  >
131
115
  {!!gradientColor && (
132
- <Animated.View
133
- style={[styles.extendedHeader, gradientOpacityStyle]}
116
+ <LinearGradientAnimated
117
+ colors={[gradientColor, gradientColor + '00']}
118
+ style={[styles.extendedHeader, { opacity: opacityGradient }]}
134
119
  >
135
- <LinearGradient
136
- colors={[gradientColor, gradientColor + '00']}
137
- style={StyleSheet.absoluteFill}
138
- >
139
- {!!theme.assets?.headerBackground && (
140
- <Image
141
- style={styles.headerBackground}
142
- source={{ uri: theme.assets?.headerBackground }}
143
- loading={false}
144
- />
145
- )}
146
- </LinearGradient>
147
- </Animated.View>
120
+ {!!theme.assets?.headerBackground && (
121
+ <Image
122
+ style={styles.headerBackground}
123
+ source={{ uri: theme.assets?.headerBackground }}
124
+ loading={false}
125
+ />
126
+ )}
127
+ </LinearGradientAnimated>
148
128
  )}
149
129
  </Animated.View>
150
130
  <Animated.View
151
- style={[
152
- {
153
- justifyContent: 'flex-end',
154
- position: 'absolute',
155
- zIndex: 2,
156
- },
157
- heightStyle,
158
- ]}
131
+ style={{
132
+ justifyContent: 'flex-end',
133
+ height,
134
+ position: 'absolute',
135
+ zIndex: 2,
136
+ }}
159
137
  >
160
138
  <Animated.View
161
- style={[{ marginVertical: Spacing.S }, searchTranslateStyle]}
139
+ style={{
140
+ transform: [{ translateX }],
141
+ marginVertical: Spacing.S,
142
+ width: animated.current.interpolate({
143
+ inputRange: [0, 100],
144
+ outputRange: [
145
+ screenWidth - SCREEN_PADDING * 2,
146
+ screenWidth - leftPosition - 12 - headerRightWidth,
147
+ ],
148
+ extrapolate: 'clamp',
149
+ }),
150
+ }}
162
151
  >
163
- <Animated.View
164
- style={[
165
- { borderRadius: Radius.XL },
166
- searchBackgroundStyle,
167
- ]}
168
- >
152
+ <Animated.View style={{ backgroundColor, borderRadius: Radius.XL }}>
169
153
  <InputSearch
170
154
  {...inputSearchProps}
171
155
  ref={inputSearchRef}
@@ -185,26 +169,22 @@ const HeaderExtendHeader: React.FC<{
185
169
  <BackgroundImageView
186
170
  useShadowHeader={useShadowHeader}
187
171
  heightHeader={heightHeader}
188
- animatedValue={sv}
172
+ opacityBackground={opacityBackground}
189
173
  headerBackground={headerBackground}
190
174
  />
191
175
  {!!gradientColor && (
192
- <Animated.View
193
- style={[styles.extendedHeader, gradientOpacityStyle, extendedHeightStyle]}
176
+ <LinearGradientAnimated
177
+ colors={[gradientColor, gradientColor + '00']}
178
+ style={[styles.extendedHeader, { opacity: opacityGradient }]}
194
179
  >
195
- <LinearGradient
196
- colors={[gradientColor, gradientColor + '00']}
197
- style={StyleSheet.absoluteFill}
198
- >
199
- {!!headerBackground && (
200
- <Image
201
- style={styles.headerBackground}
202
- source={{ uri: headerBackground }}
203
- loading={false}
204
- />
205
- )}
206
- </LinearGradient>
207
- </Animated.View>
180
+ {!!headerBackground && (
181
+ <Image
182
+ style={styles.headerBackground}
183
+ source={{ uri: headerBackground }}
184
+ loading={false}
185
+ />
186
+ )}
187
+ </LinearGradientAnimated>
208
188
  )}
209
189
  <View style={{ height: heightHeader }} />
210
190
  </View>
@@ -1,16 +1,11 @@
1
1
  import React, { useContext } from 'react';
2
2
  import {
3
+ Animated,
3
4
  Dimensions,
4
5
  StyleSheet,
5
6
  TouchableOpacity,
6
7
  View,
7
8
  } from 'react-native';
8
- import Animated, {
9
- Extrapolation,
10
- interpolate,
11
- useAnimatedStyle,
12
- type SharedValue,
13
- } from 'react-native-reanimated';
14
9
  import { ApplicationContext, MiniAppContext } from '../../Context';
15
10
  import { exportFontFamily, Text, useScaleSize } from '../../Text';
16
11
  import { Colors, Radius, Spacing, Styles } from '../../Consts';
@@ -23,45 +18,21 @@ import { Image } from '../../Image';
23
18
  import { Icon } from '../../Icon';
24
19
  import { Skeleton } from '../../Skeleton';
25
20
 
26
- type HeaderTitleInterpolate = {
27
- inputRange: number[];
28
- outputRange: number[];
29
- };
30
-
31
- type HeaderTitleExtraProps = {
32
- animatedValue?: SharedValue<number>;
33
- interpolate?: HeaderTitleInterpolate;
34
- tintColor?: string;
35
- children?: React.ReactNode;
36
- };
37
-
38
21
  /**
39
22
  * default header title used for nav
40
23
  */
41
- const HeaderTitle: React.FC<HeaderTitleExtraProps & { [key: string]: any }> = props => {
24
+ const HeaderTitle: React.FC<any> = props => {
42
25
  const context = useContext<any>(MiniAppContext);
43
26
 
44
27
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
45
28
 
46
- const interpolateConfig = props.interpolate ?? {
47
- inputRange: [0, 200],
48
- outputRange: [0, 1],
49
- };
50
- const animatedValue = props.animatedValue;
51
-
52
- const animatedStyle = useAnimatedStyle(() => {
53
- if (!animatedValue) {
54
- return { opacity: 1 };
55
- }
56
- return {
57
- opacity: interpolate(
58
- animatedValue.value,
59
- interpolateConfig.inputRange,
60
- interpolateConfig.outputRange,
61
- Extrapolation.CLAMP,
62
- ),
63
- };
64
- });
29
+ const opacity = props.animatedValue?.interpolate(
30
+ props.interpolate ?? {
31
+ inputRange: [0, 200],
32
+ outputRange: [0, 1],
33
+ extrapolate: 'clamp',
34
+ },
35
+ );
65
36
 
66
37
  return (
67
38
  <View
@@ -86,9 +57,9 @@ const HeaderTitle: React.FC<HeaderTitleExtraProps & { [key: string]: any }> = pr
86
57
  },
87
58
  {
88
59
  fontFamily: exportFontFamily('bold', 'action_xs_bold'),
60
+ opacity,
89
61
  color: props.tintColor,
90
62
  },
91
- animatedStyle,
92
63
  ]}
93
64
  numberOfLines={1}
94
65
  />
@@ -1,16 +1,13 @@
1
1
  import { Colors, Radius, Spacing, Styles } from '../../Consts';
2
2
  import { InputRef, InputSearch } from '../../Input';
3
3
  import {
4
+ Animated,
4
5
  Dimensions,
5
6
  StyleSheet,
6
7
  TouchableOpacity,
7
8
  View,
8
9
  } from 'react-native';
9
- import Animated, {
10
- useAnimatedStyle,
11
- useSharedValue,
12
- } from 'react-native-reanimated';
13
- import React, { useContext } from 'react';
10
+ import React, { useContext, useEffect, useRef } from 'react';
14
11
  import { SearchHeaderProps } from '../types';
15
12
  import { ApplicationContext, MiniAppContext } from '../../Context';
16
13
  import { Text } from '../../Text';
@@ -31,18 +28,28 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
31
28
  const BACK_WIDTH = 28;
32
29
  const { width: screenWidth } = Dimensions.get('window');
33
30
 
34
- const fallback = useSharedValue(0);
35
- const sv = animatedValue ?? fallback;
31
+ const animated = useRef(new Animated.Value(0));
36
32
  const leftPosition = props?.leftPosition ?? BACK_WIDTH + 20;
37
33
  const headerRightWidth = props?.headerRightWidth ?? 73;
38
34
 
39
- const backgroundStyle = useAnimatedStyle(() => {
40
- const t = Math.min(Math.max(sv.value / 100, 0), 1);
41
- return {
42
- backgroundColor: t === 0
43
- ? theme.colors.background.surface
44
- : theme.colors.background.default,
35
+ useEffect(() => {
36
+ const listener = animatedValue?.addListener(({ value }) => {
37
+ animated.current.setValue(value);
38
+ });
39
+ return () => {
40
+ if (listener) {
41
+ animatedValue?.removeListener(listener);
42
+ }
45
43
  };
44
+ }, [animatedValue]);
45
+
46
+ const backgroundColor = animated.current?.interpolate({
47
+ inputRange: [0, 100],
48
+ outputRange: [
49
+ theme.colors.background.surface,
50
+ theme.colors.background.default,
51
+ ],
52
+ extrapolate: 'clamp',
46
53
  });
47
54
 
48
55
  const goBack = () => {
@@ -84,9 +91,7 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
84
91
  },
85
92
  ]}
86
93
  >
87
- <Animated.View
88
- style={[{ borderRadius: Radius.XL }, backgroundStyle]}
89
- >
94
+ <Animated.View style={{ backgroundColor, borderRadius: Radius.XL }}>
90
95
  <InputSearch
91
96
  ref={ref}
92
97
  {...props}
@@ -2,11 +2,11 @@ import { EventArg } from '@react-navigation/core';
2
2
  import { StackNavigationOptions } from '@react-navigation/stack';
3
3
  import React, { ReactNode } from 'react';
4
4
  import {
5
+ Animated,
5
6
  TouchableOpacityProps,
6
7
  ViewProps,
7
8
  ViewStyle,
8
9
  } from 'react-native';
9
- import type { SharedValue } from 'react-native-reanimated';
10
10
  import { PopupNotifyProps } from '../Popup/types';
11
11
  import { InputRef, InputSearchProps } from '../Input';
12
12
  import Navigation from './Navigation';
@@ -258,7 +258,7 @@ export interface HeaderBackProps extends NavigationButtonProps {
258
258
  }
259
259
 
260
260
  export type HeaderBackgroundProps = {
261
- animatedValue?: SharedValue<number>;
261
+ animatedValue?: Animated.Value;
262
262
  useGradient?: boolean;
263
263
  useShadowHeader?: boolean;
264
264
  backgroundColor?: string;
@@ -298,7 +298,7 @@ export type TitleJourneyProps = {
298
298
  };
299
299
 
300
300
  export interface HeaderAnimatedProps extends ViewProps {
301
- animatedValue: SharedValue<number>;
301
+ animatedValue: Animated.Value;
302
302
  image: string;
303
303
  useScale?: boolean;
304
304
  }
@@ -342,7 +342,7 @@ export type AnimatedHeader = {
342
342
 
343
343
  export interface SearchHeaderProps extends InputSearchProps {
344
344
  ref?: React.RefObject<InputRef>;
345
- animatedValue?: SharedValue<number>;
345
+ animatedValue?: Animated.Value;
346
346
  headerRightWidth?: 0 | 74 | 110 | number;
347
347
  leftPosition?: 12 | 48 | number;
348
348
  renderButtons?: () => ReactNode;
@@ -5,8 +5,7 @@ import {
5
5
  } from '@react-navigation/stack';
6
6
  import type { HeaderTitleProps, NavigationOptions } from './types';
7
7
  import { Colors, Spacing } from '../Consts';
8
- import { AppState, Platform } from 'react-native';
9
- import type { SharedValue } from 'react-native-reanimated';
8
+ import { Animated, AppState, Platform } from 'react-native';
10
9
  import {
11
10
  MiniAppContext,
12
11
  ScreenContext,
@@ -61,7 +60,7 @@ const getModalOptions = (): StackNavigationOptions => {
61
60
  */
62
61
  const getOptions = (
63
62
  params: NavigationOptions,
64
- animatedValue?: SharedValue<number>,
63
+ animatedValue?: Animated.Value,
65
64
  ) => {
66
65
  let options: StackNavigationOptions = {};
67
66
 
@@ -132,7 +131,7 @@ const getOptions = (
132
131
 
133
132
  const exportHeaderTitle = (
134
133
  params: NavigationOptions,
135
- animatedValue?: SharedValue<number>,
134
+ animatedValue?: Animated.Value,
136
135
  ): StackNavigationOptions => {
137
136
  if (typeof params.headerTitle === 'object') {
138
137
  return {
@@ -1,18 +1,17 @@
1
- import React, { useContext, useEffect, useState } from 'react';
2
1
  import {
2
+ default as React,
3
+ useContext,
4
+ useEffect,
5
+ useRef,
6
+ useState,
7
+ } from 'react';
8
+ import {
9
+ Animated,
3
10
  LayoutChangeEvent,
4
11
  StyleSheet,
5
12
  TouchableOpacity,
6
13
  View,
7
14
  } from 'react-native';
8
- import Animated, {
9
- useAnimatedReaction,
10
- useAnimatedStyle,
11
- useSharedValue,
12
- withTiming,
13
- runOnJS,
14
- type SharedValue,
15
- } from 'react-native-reanimated';
16
15
  import { ApplicationContext } from '../Context';
17
16
  import { Icon } from '../Icon';
18
17
  import { useScaleSize } from '../Text';
@@ -24,7 +23,7 @@ export interface FloatingButtonProps {
24
23
  icon?: string;
25
24
  iconColor?: string;
26
25
  size?: 'small' | 'large';
27
- animatedValue?: SharedValue<number>;
26
+ animatedValue?: Animated.Value;
28
27
  bottom?: number;
29
28
  renderComponent?: () => React.ReactNode;
30
29
  }
@@ -43,67 +42,68 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
43
42
  const { theme } = useContext(ApplicationContext);
44
43
  const scaledFontSize = useScaleSize(16);
45
44
  const scaledLineHeight = useScaleSize(22);
46
- const maxWidth = useSharedValue(0);
45
+ const maxWidth = useRef(0);
47
46
  const minWidth = size === 'small' ? 36 : 48;
48
- const opacityAnimated = useSharedValue(0);
49
- const widthAnimated = useSharedValue<number | null>(null);
50
- const lastOffset = useSharedValue(0);
51
- const lastDirection = useSharedValue<string | null>(null);
52
- const [showText, setShowText] = useState(true);
47
+ const [opacityAnimated] = useState(new Animated.Value(0)); // Initial opacity set to 0
48
+ const [widthAnimated, setWidthAnimated] = useState<Animated.Value>();
49
+ const lastOffset = useRef(0);
50
+ const lastDirection = useRef<string>(null);
51
+ const [showText, setShowText] = React.useState(true);
53
52
 
54
53
  useEffect(() => {
55
54
  if (!label) return;
56
- opacityAnimated.value = withTiming(1, { duration: 100 });
57
- }, [label, opacityAnimated]);
58
55
 
59
- useAnimatedReaction(
60
- () => animatedValue?.value ?? 0,
61
- (value) => {
62
- 'worklet';
63
- if (!label || !animatedValue) return;
64
- if (value !== lastOffset.value && value > 0) {
65
- const direction = value > lastOffset.value ? 'down' : 'up';
66
- lastOffset.value = value;
67
- if (lastDirection.value !== direction) {
68
- lastDirection.value = direction;
56
+ Animated.timing(opacityAnimated, {
57
+ toValue: 1,
58
+ duration: 100,
59
+ useNativeDriver: true,
60
+ }).start();
61
+
62
+ const listener = animatedValue?.addListener(({ value }) => {
63
+ if (value !== lastOffset.current && value > 0) {
64
+ const direction = value > lastOffset.current ? 'down' : 'up';
65
+ lastOffset.current = value;
66
+ if (lastDirection.current !== direction) {
67
+ lastDirection.current = direction;
69
68
  if (direction === 'down') {
70
- opacityAnimated.value = withTiming(0, { duration: 100 });
71
- widthAnimated.value = withTiming(
72
- minWidth,
73
- { duration: 100 },
74
- (finished) => {
75
- if (finished) runOnJS(setShowText)(false);
76
- },
77
- );
69
+ Animated.timing(opacityAnimated, {
70
+ toValue: 0,
71
+ duration: 100,
72
+ useNativeDriver: true,
73
+ }).start();
74
+ Animated.timing(widthAnimated!, {
75
+ toValue: minWidth,
76
+ duration: 100,
77
+ useNativeDriver: false,
78
+ }).start(() => setShowText(false));
78
79
  } else {
79
- opacityAnimated.value = withTiming(1, { duration: 100 });
80
- widthAnimated.value = withTiming(
81
- maxWidth.value,
82
- { duration: 100 },
83
- (finished) => {
84
- if (finished) runOnJS(setShowText)(true);
85
- },
86
- );
80
+ Animated.timing(opacityAnimated, {
81
+ toValue: 1,
82
+ duration: 100,
83
+ useNativeDriver: true,
84
+ }).start();
85
+ Animated.timing(widthAnimated!, {
86
+ toValue: maxWidth.current,
87
+ duration: 100,
88
+ useNativeDriver: false,
89
+ }).start(() => setShowText(true));
87
90
  }
88
91
  }
89
92
  }
90
- },
91
- [label, animatedValue, minWidth],
92
- );
93
-
94
- const containerStyle = useAnimatedStyle(() => ({
95
- width: widthAnimated.value ?? undefined,
96
- }));
93
+ });
97
94
 
98
- const labelStyle = useAnimatedStyle(() => ({
99
- opacity: opacityAnimated.value,
100
- }));
95
+ return () => {
96
+ if (listener) {
97
+ animatedValue?.removeListener(listener);
98
+ }
99
+ };
100
+ }, [animatedValue, label, minWidth, opacityAnimated, widthAnimated]);
101
101
 
102
102
  const handleLayout = (event: LayoutChangeEvent) => {
103
103
  const layout = event.nativeEvent.layout;
104
- if (widthAnimated.value != null) return;
105
- maxWidth.value = layout.width;
106
- widthAnimated.value = layout.width;
104
+ if (widthAnimated) return;
105
+ maxWidth.current = layout.width;
106
+ setWidthAnimated(new Animated.Value(layout.width));
107
107
  };
108
108
 
109
109
  if (renderComponent) {
@@ -132,11 +132,11 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
132
132
  {
133
133
  right: position === 'right' ? 12 : undefined,
134
134
  alignSelf: position === 'center' ? 'center' : 'flex-end',
135
+ width: widthAnimated,
135
136
  height: size === 'small' ? 36 : 48,
136
137
  backgroundColor: theme.colors.primary,
137
138
  bottom,
138
139
  },
139
- containerStyle,
140
140
  ]}
141
141
  >
142
142
  <TouchableOpacity
@@ -156,9 +156,9 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
156
156
  {
157
157
  fontSize: scaledFontSize,
158
158
  lineHeight: scaledLineHeight,
159
+ opacity: opacityAnimated,
159
160
  color: 'white',
160
161
  },
161
- labelStyle,
162
162
  ]}
163
163
  numberOfLines={1}
164
164
  >
package/Layout/Screen.tsx CHANGED
@@ -12,6 +12,7 @@ import React, {
12
12
  useRef,
13
13
  } from 'react';
14
14
  import {
15
+ Animated,
15
16
  KeyboardAvoidingView,
16
17
  NativeScrollEvent,
17
18
  NativeSyntheticEvent,
@@ -24,13 +25,6 @@ import {
24
25
  View,
25
26
  ViewProps,
26
27
  } from 'react-native';
27
- import Animated, {
28
- runOnJS,
29
- useAnimatedScrollHandler,
30
- useSharedValue,
31
- withTiming,
32
- type SharedValue,
33
- } from 'react-native-reanimated';
34
28
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
35
29
  import { ApplicationContext, ScreenContext } from '../Context';
36
30
  import Navigation from '../Application/Navigation';
@@ -144,7 +138,7 @@ export interface ScreenProps extends ViewProps {
144
138
  /**
145
139
  * Optional. Animated value for header.
146
140
  */
147
- animatedValue?: SharedValue<number>;
141
+ animatedValue?: Animated.Value;
148
142
 
149
143
  /**
150
144
  * Optional. If `true`, use shadow header.
@@ -201,12 +195,13 @@ const Screen = forwardRef(
201
195
  const screen: any = useContext(ScreenContext);
202
196
  const insets = useSafeAreaInsets();
203
197
  const heightHeader = useHeaderHeight();
204
- const internalAnimatedValue = useSharedValue(0);
205
- const animatedValue = customAnimatedValue ?? internalAnimatedValue;
198
+ const animatedValue = useRef<Animated.Value>(
199
+ customAnimatedValue || new Animated.Value(0),
200
+ );
206
201
  const currentTint = useRef<string | undefined>(undefined);
207
202
  const isTab = navigation?.instance?.getState?.()?.type === 'tab';
208
203
 
209
- let handleScroll: any;
204
+ let handleScroll;
210
205
  let Component: any = View;
211
206
 
212
207
  let keyboardOffset = heightHeader - Math.min(insets.bottom, 21);
@@ -253,8 +248,9 @@ const Screen = forwardRef(
253
248
  interpolate={{
254
249
  inputRange: [0, 50],
255
250
  outputRange: [1, 0],
251
+ extrapolate: 'clamp',
256
252
  }}
257
- animatedValue={animatedValue}
253
+ animatedValue={animatedValue.current}
258
254
  />
259
255
  ),
260
256
  };
@@ -269,7 +265,7 @@ const Screen = forwardRef(
269
265
  headerBackground: (props: any) => (
270
266
  <HeaderBackground
271
267
  {...props}
272
- animatedValue={animatedValue}
268
+ animatedValue={animatedValue.current}
273
269
  useShadowHeader={useShadowHeader}
274
270
  headerBackground={headerBackground}
275
271
  gradientColor={gradientColor}
@@ -288,8 +284,9 @@ const Screen = forwardRef(
288
284
  interpolate={{
289
285
  inputRange: [0, 50],
290
286
  outputRange: [1, 0],
287
+ extrapolate: 'clamp',
291
288
  }}
292
- animatedValue={animatedValue}
289
+ animatedValue={animatedValue.current}
293
290
  />
294
291
  ),
295
292
  };
@@ -324,7 +321,7 @@ const Screen = forwardRef(
324
321
  headerBackground: (props: any) => (
325
322
  <HeaderBackground
326
323
  {...props}
327
- animatedValue={animatedValue}
324
+ animatedValue={animatedValue.current}
328
325
  useGradient={false}
329
326
  useShadowHeader={useShadowHeader}
330
327
  headerBackground={headerBackground}
@@ -362,8 +359,9 @@ const Screen = forwardRef(
362
359
  interpolate={{
363
360
  inputRange: [0, 50],
364
361
  outputRange: [1, 0],
362
+ extrapolate: 'clamp',
365
363
  }}
366
- animatedValue={animatedValue}
364
+ animatedValue={animatedValue.current}
367
365
  />
368
366
  ),
369
367
  };
@@ -392,7 +390,7 @@ const Screen = forwardRef(
392
390
  headerLeft: (props: any) =>
393
391
  params?.hiddenBack ? null : <HeaderLeft {...props} />,
394
392
  headerTitle: () => (
395
- <SearchHeader {...params} animatedValue={animatedValue} />
393
+ <SearchHeader {...params} animatedValue={animatedValue.current} />
396
394
  ),
397
395
  };
398
396
 
@@ -443,51 +441,45 @@ const Screen = forwardRef(
443
441
  });
444
442
  });
445
443
 
446
- const onTintColorChange = useCallback(
447
- (offsetY: number) => {
448
- if (!animatedHeader) return;
449
- let color = animatedHeader?.headerTintColor ?? Colors.black_17;
450
- if (offsetY > 50) {
451
- color = Colors.black_17;
452
- }
453
- if (color !== currentTint.current) {
454
- currentTint.current = color;
455
- navigation?.setOptions({
456
- headerTintColor: color,
457
- });
458
- let barStyle: StatusBarStyle = 'dark-content';
459
- if (currentTint.current === Colors.black_01) {
460
- barStyle = 'light-content';
461
- }
462
- StatusBar.setBarStyle(barStyle, true);
463
- }
464
- },
465
- [animatedHeader, navigation],
466
- );
467
-
468
- const emitOnScroll = useCallback(
469
- (offsetY: number) => {
470
- scrollViewProps?.onScroll?.({
471
- nativeEvent: { contentOffset: { x: 0, y: offsetY } },
472
- } as NativeSyntheticEvent<NativeScrollEvent>);
473
- },
474
- [scrollViewProps],
475
- );
476
-
477
- const scrollHandler = useAnimatedScrollHandler({
478
- onScroll: (event) => {
479
- animatedValue.value = event.contentOffset.y;
480
- runOnJS(emitOnScroll)(event.contentOffset.y);
481
- runOnJS(onTintColorChange)(event.contentOffset.y);
482
- },
483
- });
484
-
485
444
  /**
486
445
  * animated when use scroll && animated value
487
446
  */
488
447
  if (scrollable) {
489
448
  Component = Animated.ScrollView;
490
- handleScroll = scrollHandler;
449
+ handleScroll = Animated.event(
450
+ [
451
+ {
452
+ nativeEvent: {
453
+ contentOffset: { y: animatedValue.current as Animated.Value },
454
+ },
455
+ },
456
+ ],
457
+ {
458
+ useNativeDriver: true,
459
+ listener: (e: NativeSyntheticEvent<NativeScrollEvent>) => {
460
+ scrollViewProps?.onScroll?.(e);
461
+ if (animatedHeader) {
462
+ const offsetY = e.nativeEvent.contentOffset.y;
463
+ let color = animatedHeader?.headerTintColor ?? Colors.black_17;
464
+ if (offsetY > 50) {
465
+ color = Colors.black_17;
466
+ }
467
+ if (color !== currentTint.current) {
468
+ currentTint.current = color;
469
+ navigation?.setOptions({
470
+ headerTintColor: color,
471
+ });
472
+
473
+ let barStyle: StatusBarStyle = 'dark-content';
474
+ if (currentTint.current === Colors.black_01) {
475
+ barStyle = 'light-content';
476
+ }
477
+ StatusBar.setBarStyle(barStyle, true);
478
+ }
479
+ }
480
+ },
481
+ },
482
+ );
491
483
  }
492
484
 
493
485
  /**
@@ -497,7 +489,11 @@ const Screen = forwardRef(
497
489
  const handleScrollEnd = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
498
490
  const offsetY = e.nativeEvent.contentOffset.y;
499
491
  if (inputSearchProps && offsetY < 100 && offsetY > 0) {
500
- animatedValue.value = withTiming(0, { duration: 300 });
492
+ Animated.timing(animatedValue.current, {
493
+ toValue: 0,
494
+ useNativeDriver: true,
495
+ duration: 300,
496
+ }).start();
501
497
  ref?.scrollTo?.({ y: 0, animated: true });
502
498
  }
503
499
  scrollViewProps?.onScrollEndDrag?.(e);
@@ -513,7 +509,7 @@ const Screen = forwardRef(
513
509
  style={[styles.screenBanner, { maxHeight: 210 + layoutOffset }]}
514
510
  >
515
511
  {animatedHeader?.component({
516
- animatedValue: animatedValue,
512
+ animatedValue: animatedValue.current,
517
513
  })}
518
514
  </View>
519
515
  );
@@ -580,7 +576,7 @@ const Screen = forwardRef(
580
576
  headerType={headerType}
581
577
  heightHeader={heightHeader}
582
578
  headerRightWidth={headerRightWidth}
583
- animatedValue={animatedValue}
579
+ animatedValue={animatedValue.current}
584
580
  inputSearchProps={inputSearchProps}
585
581
  navigation={navigation}
586
582
  inputSearchRef={inputSearchRef}
@@ -618,7 +614,7 @@ const Screen = forwardRef(
618
614
  <View>
619
615
  <FloatingButton
620
616
  {...floatingButtonProps}
621
- animatedValue={animatedValue}
617
+ animatedValue={animatedValue.current}
622
618
  bottom={
623
619
  Footer || isTab ? 12 : Math.min(insets.bottom, 21) + Spacing.S
624
620
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.161.2-beta.7",
3
+ "version": "0.161.2-reanimated.1",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {},