├── .gitignore
├── Examples
├── Example1
│ ├── Example1.tsx
│ └── style.ts
├── Example2
│ ├── Example2.tsx
│ └── style.ts
├── Example3
│ ├── Example3.tsx
│ └── style.ts
├── Example4
│ ├── Example4.tsx
│ └── style.ts
├── assets
│ └── img
│ │ ├── 150.png
│ │ ├── Example1.gif
│ │ ├── Example2.gif
│ │ ├── Example3.gif
│ │ ├── Example4.gif
│ │ └── header-img.jpg
└── index.ts
├── LICENSE
├── README.md
├── package.json
├── src
├── AnimatedScreenCollapsibleElement
│ ├── AnimatedScreenCollapsibleElement.tsx
│ └── index.ts
├── AnimatedScreenContext.tsx
├── AnimatedScreenElement
│ ├── AnimatedScreenElement.tsx
│ └── index.ts
├── AnimatedScreenFlatList
│ ├── AnimatedScreenFlatList.tsx
│ ├── index.ts
│ └── styles.ts
├── AnimatedScreenHeader
│ ├── AnimatedScreenHeader.tsx
│ ├── index.ts
│ └── style.ts
├── AnimatedScreenScrollView
│ ├── AnimatedScreenScrollView.tsx
│ └── index.ts
├── AnimatedScreenSectionList
│ ├── AnimatedScreenSectionList.tsx
│ └── index.ts
├── AnimatedScreenWrapper
│ ├── AnimatedScreenWrapper.tsx
│ └── index.ts
└── index.ts
├── tsconfig.json
└── tslint.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # build
6 | #
7 | lib/
8 |
9 | # node.js
10 | #
11 | node_modules/
12 | npm-debug.log
13 | yarn-error.log
14 | package-lock.json
15 |
16 | .npmrc
17 |
--------------------------------------------------------------------------------
/Examples/Example1/Example1.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Image, Text, View } from 'react-native';
3 | import img1 from '../assets/img/150.png';
4 | import AnimatedScreen from '../../src';
5 | import style from './style';
6 |
7 | const Example1: React.FC = () => {
8 | return (
9 |
10 |
11 |
12 | Example 1
13 |
14 | Simple header with ScrollView
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | Fusce fermentum. Pellentesque egestas, neque sit amet convallis
23 | pulvinar, justo nulla eleifend augue, ac auctor orci leo non est.
24 | In turpis. Cras ultricies mi eu turpis hendrerit fringilla.
25 | Maecenas nec odio et ante tincidunt tempus. Aliquam lobortis.
26 | Etiam vitae tortor. Suspendisse pulvinar, augue ac venenatis
27 | condimentum, sem libero volutpat nibh, nec pellentesque velit pede
28 | quis nunc. Etiam vitae tortor. Cras ultricies mi eu turpis
29 | hendrerit fringilla. Morbi mattis ullamcorper velit. Proin
30 | viverra, ligula sit amet ultrices semper, ligula arcu tristique
31 | sapien, a accumsan nisi mauris ac eros. Vivamus elementum semper
32 | nisi. Praesent metus tellus, elementum eu, semper a, adipiscing
33 | nec, purus. Etiam vitae tortor.
34 |
35 |
36 |
37 |
38 |
39 | Fusce fermentum. Pellentesque egestas, neque sit amet convallis
40 | pulvinar, justo nulla eleifend augue, ac auctor orci leo non est.
41 | In turpis. Cras ultricies mi eu turpis hendrerit fringilla.
42 | Maecenas nec odio et ante tincidunt tempus. Aliquam lobortis.
43 | Etiam vitae tortor. Suspendisse pulvinar, augue ac venenatis
44 | condimentum, sem libero volutpat nibh, nec pellentesque velit pede
45 | quis nunc. Etiam vitae tortor. Cras ultricies mi eu turpis
46 | hendrerit fringilla. Morbi mattis ullamcorper velit. Proin
47 | viverra, ligula sit amet ultrices semper, ligula arcu tristique
48 | sapien, a accumsan nisi mauris ac eros. Vivamus elementum semper
49 | nisi. Praesent metus tellus, elementum eu, semper a, adipiscing
50 | nec, purus. Etiam vitae tortor.
51 |
52 |
53 |
54 |
55 |
56 | );
57 | };
58 |
59 | export default React.memo(Example1);
60 |
--------------------------------------------------------------------------------
/Examples/Example1/style.ts:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 |
3 | export default StyleSheet.create({
4 | container: {
5 | paddingHorizontal: 10,
6 | },
7 | title: {
8 | fontSize: 30,
9 | fontWeight: '900',
10 | },
11 | subtitle: {
12 | fontSize: 20,
13 | fontWeight: '400',
14 | },
15 | body: {
16 | paddingVertical: 10,
17 | },
18 | imgWrapper: {
19 | padding: 10,
20 | justifyContent: 'center',
21 | alignItems: 'center',
22 | },
23 | });
24 |
--------------------------------------------------------------------------------
/Examples/Example2/Example2.tsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback } from 'react';
2 | import { Image, Text, View } from 'react-native';
3 | import img1 from '../assets/img/150.png';
4 | import AnimatedScreen from '../../src';
5 | import style from './style';
6 |
7 | const Example2: React.FC = () => {
8 | const renderItem = useCallback(({ index }) => {
9 | return (
10 |
11 |
12 | FlatList item {index}
13 |
14 | );
15 | }, []);
16 | return (
17 |
18 |
19 |
20 | Example 2
21 |
22 | Simple header with FlatList
23 |
24 |
25 |
26 |
31 |
32 | );
33 | };
34 |
35 | export default React.memo(Example2);
36 |
--------------------------------------------------------------------------------
/Examples/Example2/style.ts:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 |
3 | export default StyleSheet.create({
4 | container: {
5 | paddingHorizontal: 10,
6 | },
7 | title: {
8 | fontSize: 30,
9 | fontWeight: '900',
10 | },
11 | subtitle: {
12 | fontSize: 20,
13 | fontWeight: '400',
14 | },
15 | body: {
16 | paddingVertical: 10,
17 | },
18 | item: {
19 | height: 60,
20 | borderWidth: 1,
21 | borderRadius: 5,
22 | margin: 10,
23 | marginTop: 0,
24 | padding: 10,
25 | flexDirection: 'row',
26 | alignItems: 'center',
27 | },
28 | text: {
29 | flex: 1,
30 | textAlign: 'center',
31 | },
32 | image: {
33 | width: 40,
34 | height: 40,
35 | },
36 | });
37 |
--------------------------------------------------------------------------------
/Examples/Example3/Example3.tsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback } from 'react';
2 | import { Image, Text, View } from 'react-native';
3 | import img1 from '../assets/img/150.png';
4 | import AnimatedScreen from '../../src';
5 | import style from './style';
6 |
7 | const sections = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L'];
8 |
9 | const Example3: React.FC = () => {
10 | const renderItem = useCallback(({ item }) => {
11 | return (
12 |
13 |
14 | {item}
15 |
16 | );
17 | }, []);
18 | const renderSectionHeader = useCallback(({ section }) => {
19 | return (
20 |
21 | {section.title}
22 |
23 | );
24 | }, []);
25 | return (
26 |
27 |
28 |
29 | Example 3
30 |
31 | Simple header with SectionList
32 |
33 |
34 |
35 | ({
37 | title: section,
38 | data: new Array(Math.floor(Math.random() * 5) + 1).fill(
39 | `Element in ${section} section`,
40 | ),
41 | }))}
42 | renderSectionHeader={renderSectionHeader}
43 | renderItem={renderItem}
44 | />
45 |
46 | );
47 | };
48 |
49 | export default React.memo(Example3);
50 |
--------------------------------------------------------------------------------
/Examples/Example3/style.ts:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 |
3 | export default StyleSheet.create({
4 | container: {
5 | paddingHorizontal: 10,
6 | },
7 | title: {
8 | fontSize: 30,
9 | fontWeight: '900',
10 | },
11 | subtitle: {
12 | fontSize: 20,
13 | fontWeight: '400',
14 | },
15 | section: {
16 | backgroundColor: '#e0e0e0',
17 | padding: 10,
18 | },
19 | item: {
20 | height: 60,
21 | borderWidth: 1,
22 | borderRadius: 5,
23 | marginVertical: 5,
24 | marginHorizontal: 10,
25 | padding: 10,
26 | flexDirection: 'row',
27 | alignItems: 'center',
28 | },
29 | text: {
30 | flex: 1,
31 | textAlign: 'center',
32 | },
33 | image: {
34 | width: 40,
35 | height: 40,
36 | },
37 | });
38 |
--------------------------------------------------------------------------------
/Examples/Example4/Example4.tsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback } from 'react';
2 | import { Animated, Image, Text, TextInput, View } from 'react-native';
3 | import { widthPercentageToDP } from 'react-native-responsive-screen';
4 | import img1 from '../assets/img/150.png';
5 | import AnimatedScreen from '../../src';
6 | import style from './style';
7 |
8 | const sections = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L'];
9 |
10 | const Example4: React.FC = () => {
11 | const renderItem = useCallback(({ item }) => {
12 | return (
13 |
14 |
15 | {item}
16 |
17 | );
18 | }, []);
19 | const renderSectionHeader = useCallback(({ section }) => {
20 | return (
21 |
22 | {section.title}
23 |
24 | );
25 | }, []);
26 | const getBackgroundOpacity = useCallback(
27 | (scrollY: Animated.Value) => ({
28 | backgroundColor: scrollY.interpolate({
29 | inputRange: [0, 200],
30 | outputRange: ['#236979', '#6c2379'],
31 | extrapolate: 'clamp',
32 | }),
33 | }),
34 | [],
35 | );
36 | const renderBackground = useCallback(
37 | (scrollY: Animated.Value) => (
38 |
41 | ),
42 | [getBackgroundOpacity],
43 | );
44 | const headerAnimatedStyle = useCallback(
45 | scrollY => ({
46 | transform: [
47 | {
48 | scale: scrollY.interpolate({
49 | inputRange: [0, 200],
50 | outputRange: [0, 1],
51 | extrapolate: 'clamp',
52 | }),
53 | },
54 | ],
55 | }),
56 | [],
57 | );
58 | return (
59 |
64 |
69 |
70 |
71 |
75 |
76 |
77 |
81 | Example 4
82 |
83 |
84 |
85 | Example 4
86 | OnScroll Animated Header
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | ({
98 | title: section,
99 | data: new Array(Math.floor(Math.random() * 5) + 1).fill(
100 | `Element in ${section} section`,
101 | ),
102 | }))}
103 | renderSectionHeader={renderSectionHeader}
104 | renderItem={renderItem}
105 | />
106 |
107 | );
108 | };
109 |
110 | export default React.memo(Example4);
111 |
--------------------------------------------------------------------------------
/Examples/Example4/style.ts:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 |
3 | export default StyleSheet.create({
4 | container: {
5 | paddingHorizontal: 10,
6 | },
7 | title: {
8 | fontSize: 30,
9 | fontWeight: '900',
10 | },
11 | subtitle: {
12 | fontSize: 20,
13 | fontWeight: '400',
14 | },
15 | section: {
16 | backgroundColor: '#e0e0e0',
17 | padding: 10,
18 | },
19 | header: {
20 | justifyContent: 'flex-start',
21 | flexDirection: 'column',
22 | },
23 | spacer: {
24 | height: 40,
25 | },
26 | smallHeader: {
27 | flexDirection: 'row',
28 | alignItems: 'center',
29 | },
30 | logoWrapper: {
31 | alignItems: 'center',
32 | marginRight: 10,
33 | },
34 | logo: {
35 | width: 30,
36 | height: 30,
37 | borderRadius: 30,
38 | },
39 | input: {
40 | width: '100%',
41 | height: 30,
42 | backgroundColor: '#e0e0e0',
43 | opacity: 0.8,
44 | borderRadius: 15,
45 | paddingVertical: 0,
46 | paddingHorizontal: 15,
47 | },
48 | background: {
49 | position: 'absolute',
50 | width: '100%',
51 | height: '100%',
52 | },
53 | item: {
54 | height: 60,
55 | borderWidth: 1,
56 | borderRadius: 5,
57 | marginVertical: 5,
58 | marginHorizontal: 10,
59 | padding: 10,
60 | flexDirection: 'row',
61 | alignItems: 'center',
62 | },
63 | text: {
64 | flex: 1,
65 | textAlign: 'center',
66 | },
67 | image: {
68 | width: 40,
69 | height: 40,
70 | },
71 | });
72 |
--------------------------------------------------------------------------------
/Examples/assets/img/150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannidavi/react-native-animated-screen/2e8c0b27a6238fe141101e88f63e5d2bb61bc32d/Examples/assets/img/150.png
--------------------------------------------------------------------------------
/Examples/assets/img/Example1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannidavi/react-native-animated-screen/2e8c0b27a6238fe141101e88f63e5d2bb61bc32d/Examples/assets/img/Example1.gif
--------------------------------------------------------------------------------
/Examples/assets/img/Example2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannidavi/react-native-animated-screen/2e8c0b27a6238fe141101e88f63e5d2bb61bc32d/Examples/assets/img/Example2.gif
--------------------------------------------------------------------------------
/Examples/assets/img/Example3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannidavi/react-native-animated-screen/2e8c0b27a6238fe141101e88f63e5d2bb61bc32d/Examples/assets/img/Example3.gif
--------------------------------------------------------------------------------
/Examples/assets/img/Example4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannidavi/react-native-animated-screen/2e8c0b27a6238fe141101e88f63e5d2bb61bc32d/Examples/assets/img/Example4.gif
--------------------------------------------------------------------------------
/Examples/assets/img/header-img.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/giovannidavi/react-native-animated-screen/2e8c0b27a6238fe141101e88f63e5d2bb61bc32d/Examples/assets/img/header-img.jpg
--------------------------------------------------------------------------------
/Examples/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Example1 } from './Example1/Example1';
2 | export { default as Example2 } from './Example2/Example2';
3 | export { default as Example3 } from './Example3/Example3';
4 | export { default as Example4 } from './Example4/Example4';
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2020 Giovanni Davì
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Native Animated Screen
2 |
3 | [](https://www.npmjs.com/package/react-native-animated-screen) [](https://www.npmjs.com/package/react-native-animated-screen) [](https://github.com/giovannidavi/react-native-animated-screen)
4 |
5 | 
6 |
7 |
8 | This module includes all the necessary components to generate outstanding **scroll-based** **animated screens** and **screens header**.
9 |
10 |
11 |
12 | The package is both **Android** and **iOS** compatible.
13 |
14 | ## Try it out
15 |
16 | You can play with our ready-to-use **examples** from our GitHub repository
17 |
18 |
19 |
20 | |Example 1|Example 2|Example 3|Example 4|
21 | |---|---|---|---|
22 | |||||
23 |
24 |
25 | ```
26 |
27 | $ git clone https://github.com/giovannidavi/react-native-animated-screen.git
28 |
29 | ```
30 |
31 |
32 | You can check the examples code here [examples](https://github.com/giovannidavi/react-native-animated-screen/tree/master/Examples)
33 |
34 |
35 | ## Installation
36 |
37 | With npm
38 |
39 | ```
40 | $ npm install react-native-animated-screen
41 |
42 | ```
43 |
44 | With yarn
45 |
46 | ```
47 | $ yarn add react-native-animated-screen
48 | ```
49 |
50 |
51 |
52 | The solution is implemented in JavaScript so no native module linking is required.
53 |
54 |
55 |
56 | ## Usage
57 |
58 | By default the component exposes 7 sub-components to help you creating the stage.
59 |
60 | - AnimatedScreen.Wrapper
61 | - AnimatedScreen.Header
62 | - AnimatedScreen.ScrollView
63 | - AnimatedScreen.FlatList
64 | - AnimatedScreen.SectionList
65 | - AnimatedScreen.Element
66 | - AniamtedScreen.CollapsibleElement
67 |
68 | For the animation to work properly you need to wrap your entire view in an AnimatedScreen.Wrapper component which will include the AnimatedScreen.Header and at least one of the scrollable elements:
69 |
70 | - AnimatedScreen.ScrollView
71 | - AnimatedScreen.FlatList
72 | - AnimatedScreen.SectionList
73 |
74 |
75 |
76 |
77 | ```javascript
78 |
79 | import React from 'react';
80 | import { Image, Text, View } from 'react-native';
81 | import AnimatedScreen from 'react-native-animated-screen';
82 |
83 | const Component = () => {
84 | return (
85 |
86 |
87 |
88 | Title
89 |
90 | Subtitle
91 |
92 |
93 |
94 |
95 |
96 |
97 | Body
98 |
99 |
100 |
101 |
102 | );
103 | };
104 | ```
105 |
106 |
107 |
108 | ### AnimatedScreen.Wrapper
109 |
110 | Is the main component and the wrapper of the entire animated screen, **required**
111 |
112 |
113 |
114 | #### Basic parameters
115 |
116 |
117 | ```javascript
118 |
119 | ) => void;
137 | onScroll={() => null}
138 |
139 | >
140 | // at least an AnimatedScreen.Header and an AnimatedScreen scrollable element required as children
141 | { children }
142 |
143 |
144 | ```
145 |
146 | ### AnimatedScreen.Header
147 |
148 | Is one of the children of the **Wrapper**, is required, and it contains all the header elements. Its dimensions are inherited from the wrapper and define the scroll animation behaviours.
149 | It expect at least one child component.
150 |
151 |
152 |
153 | #### Basic parameters
154 |
155 |
156 | ```javascript
157 |
158 | ViewStyle;
165 | animatedStyle={ scrollY => ({
166 | opacity: scrollY.interpolate({
167 | inputRange: [0, 200], // from 0 scrolled to headerMaxHeight scrolled
168 | outputRange: [1, 0]
169 | })
170 | })
171 | }
172 |
173 | // Expects a callback returning the component to be used as background
174 | // renderBackground?: (scrollY: Animated.Value) => JSX.Element;
175 | renderBackground={ scrollY => (
176 |
177 | )
178 |
179 | // Set if your header should generate a shadow on scroll
180 | // withShadow?: boolean; (default: false)
181 | withShadow={ true }
182 |
183 | // Set if your header should have a translucent blured effect on scroll
184 | // (ONLY Android - not recommended in iOS)
185 | // blur?: boolean; (default: false)
186 | blur={ false }
187 |
188 | // Expects a string or undefined containing the background color to be passed to the style
189 | // backgroundColor?: string; (default: undefined)
190 | backgroundColor="#303030"
191 |
192 | // Select if the header should take care of SafeArea
193 | // withSafeArea?: boolean; (default: true)
194 | withSafeArea={ true }
195 |
196 | // Expect a style object that can extend the header style
197 | // (preferred to animatedStyle if static)
198 | // style?: ViewStyle; (default: undefined)
199 | style={{ paddingBottom: 10 }}
200 | >
201 | // at least one child is required, can be any component (AnimatedScreen.CollapsibleElement and AnimatedScreen.Element are provided to create animated effects)
202 | { children }
203 |
204 |
205 | ```
206 |
207 |
208 | ### AnimatedScreen.ScrollView
209 |
210 | Extends the react-native **ScrollView** and can be used as scrollable element for the screen.
211 | For the entire list of possible props refer to react-native ScrollView documentation.
212 | Never to be used in combination with AnimatedScreen.FlatList or AnimatedScreen.SectionList.
213 |
214 |
215 | #### Basic parameters
216 |
217 |
218 | ```javascript
219 |
220 | ViewStyle;
223 | animatedStyle={ scrollY => ({
224 | opacity: scrollY.interpolate({
225 | inputRange: [0, 200], // from 0 scrolled to headerMaxHeight scrolled
226 | outputRange: [1, 0]
227 | })
228 | })
229 | }
230 |
231 | // Expect a style object that can extend the header style
232 | // (preferred to animatedStyle if static)
233 | // style?: ViewStyle; (default: undefined)
234 | style={{ paddingBottom: 10 }}
235 |
236 | // Expect a React.RefObject to be attached to the ScrollView
237 | // scrollViewRef?: React.RefObject;
238 | scrollViewRef={ scrollViewRef }
239 | >
240 | { children }
241 |
242 |
243 | ```
244 |
245 |
246 | ### AnimatedScreen.FlatList
247 |
248 | Extends the react-native **FlatList** and can be used as scrollable element for the screen.
249 | For the entire list of possible props refer to react-native FlatList documentation.
250 | Never to be used in combination with AnimatedScreen.ScrollView or AnimatedScreen.SectionList.
251 |
252 |
253 | #### Basic parameters
254 |
255 |
256 | ```javascript
257 |
258 | ViewStyle;
261 | animatedStyle={ scrollY => ({
262 | opacity: scrollY.interpolate({
263 | inputRange: [0, 200], // from 0 scrolled to headerMaxHeight scrolled
264 | outputRange: [1, 0]
265 | })
266 | })
267 | }
268 |
269 | // Expect a style object that can extend the header style
270 | // (preferred to animatedStyle if static)
271 | // style?: ViewStyle; (default: undefined)
272 | style={{ paddingBottom: 10 }}
273 |
274 | // Expect a React.RefObject to be attached to the ScrollView
275 | // flatlistRef?: React.RefObject>;
276 | flatlistRef={ flatlistRef }
277 |
278 | { ...restOfFlatListProps }
279 | />
280 |
281 | ```
282 |
283 |
284 | ### AnimatedScreen.SectionList
285 |
286 | Extends the react-native **SectionList** and can be used as scrollable element for the screen.
287 | For the entire list of possible props refer to react-native SectionList documentation.
288 | Never to be used in combination with AnimatedScreen.ScrollView or AnimatedScreen.FlatList.
289 |
290 |
291 | #### Basic parameters
292 |
293 |
294 | ```javascript
295 |
296 | ViewStyle;
299 | animatedStyle={ scrollY => ({
300 | opacity: scrollY.interpolate({
301 | inputRange: [0, 200], // from 0 scrolled to headerMaxHeight scrolled
302 | outputRange: [1, 0]
303 | })
304 | })
305 | }
306 |
307 | // Expect a style object that can extend the header style
308 | // (preferred to animatedStyle if static)
309 | // style?: ViewStyle; (default: undefined)
310 | style={{ paddingBottom: 10 }}
311 |
312 | // Expect a React.RefObject to be attached to the ScrollView
313 | // sectionlistRef?: React.RefObject>;
314 | sectionlistRef={ sectionlistRef }
315 |
316 | { ...restOfSectionListProps }
317 | />
318 |
319 | ```
320 |
321 |
322 | ### AnimatedScreen.CollapsibleElement
323 |
324 | Can only be used as a **child of AnimatedScreen.Header**, it can wrap any type of component that you want to **disappear** while scrolling
325 |
326 |
327 | #### Basic parameters
328 |
329 |
330 | ```javascript
331 |
332 | ViewStyle;
335 | animatedStyle={ scrollY => ({
336 | opacity: scrollY.interpolate({
337 | inputRange: [0, 200], // from 0 scrolled to headerMaxHeight scrolled
338 | outputRange: [1, 0]
339 | })
340 | })
341 | }
342 |
343 | // Expect a style object that can extend the header style
344 | // (preferred to animatedStyle if static)
345 | // style?: ViewStyle; (default: undefined)
346 | style={{ paddingBottom: 10 }}
347 |
348 | // Set if you want the content of the element to overflow the boxed header (and take full-width)
349 | // only to be used if AnimatedScreen.Header is set to `boxed`
350 | // unboxed?: boolean; (default: false)
351 | unboxed={ true }
352 |
353 | // Expects an object containing all the style elements to interpolate on scroll, can be considered as a shortcut for animatedStyle
354 | // interpolate?: { [key: string]: string[] | number[] };
355 | interpolate={ height: [200, 0] } // will interpolated from an height of 200 to an height of 0 while scrolling
356 | >
357 | { children }
358 |
359 |
360 | ```
361 |
362 |
363 | ### AnimatedScreen.Element
364 |
365 | As the CollapsibleElement can only be used as a **child of AnimatedScreen.Header** but it should wrap element that you **don't want to disappear** while scrolling
366 |
367 |
368 | #### Basic parameters
369 |
370 |
371 | ```javascript
372 |
373 | ViewStyle;
376 | animatedStyle={ scrollY => ({
377 | opacity: scrollY.interpolate({
378 | inputRange: [0, 200], // from 0 scrolled to headerMaxHeight scrolled
379 | outputRange: [1, 0]
380 | })
381 | })
382 | }
383 |
384 | // Expect a style object that can extend the header style
385 | // (preferred to animatedStyle if static)
386 | // style?: ViewStyle; (default: undefined)
387 | style={{ paddingBottom: 10 }}
388 |
389 | // Set if you want the content of the element to overflow the boxed header (and take full-width)
390 | // only to be used if AnimatedScreen.Header is set to `boxed`
391 | // unboxed?: boolean; (default: false)
392 | unboxed={ true }
393 |
394 | // Expects an object containing all the style elements to interpolate on scroll, can be considered as a shortcut for animatedStyle
395 | // interpolate?: { [key: string]: string[] | number[] };
396 | interpolate={ height: [200, 0] } // will interpolated from an height of 200 to an height of 0 while scrolling
397 | >
398 | { children }
399 |
400 |
401 | ```
402 |
403 |
404 | ## Author
405 |
406 | * [Giovanni Davì](https://github.com/giovannidavi)
407 |
408 |
409 | ## Contributing
410 |
411 |
412 |
413 | Pull requests are most welcome!
414 |
415 | Don't forget to add a **title** and a **description** that explain the issue you're trying to solve and your suggested solution. Screenshots and gifs are very helpful.
416 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-animated-screen",
3 | "version": "1.1.1",
4 | "description": "React Native Animated Screens made easy",
5 | "main": "lib/index.js",
6 | "types": "lib/index.d.ts",
7 | "scripts": {
8 | "build": "rm -rf ./lib && tsc --project ./tsconfig.json"
9 | },
10 | "dependencies": {
11 | "@react-native-community/blur": "^3.6.0",
12 | "@types/react": "^16.9.56",
13 | "@types/react-native": "^0.63.33",
14 | "react-native-responsive-screen": "^1.4.1",
15 | "react-native-safe-area-context": "^3.1.1"
16 | },
17 | "devDependencies": {
18 | "@react-native-community/eslint-config": "^1.1.0",
19 | "tslint": "^6.1.3",
20 | "tslint-config-prettier": "^1.18.0",
21 | "tslint-react": "^5.0.0",
22 | "typescript": "^3.8.3"
23 | },
24 | "peerDependencies": {
25 | "react": "*",
26 | "react-dom": "*",
27 | "react-native": "*"
28 | },
29 | "files": [
30 | "lib/**/*"
31 | ],
32 | "keywords": [
33 | "animated screen",
34 | "parallax header",
35 | "animated header"
36 | ],
37 | "homepage": "https://github.com/giovannidavi/react-native-animated-screen",
38 | "author": "Giovanni Davì",
39 | "license": "MIT"
40 | }
41 |
--------------------------------------------------------------------------------
/src/AnimatedScreenCollapsibleElement/AnimatedScreenCollapsibleElement.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useMemo } from 'react';
2 | import { Animated, ViewStyle } from 'react-native';
3 | import { widthPercentageToDP } from 'react-native-responsive-screen';
4 | import AnimatedScreenContext from '../AnimatedScreenContext';
5 |
6 | type Props = {
7 | animatedStyle?: (scrollY: Animated.Value) => ViewStyle;
8 | children: JSX.Element | JSX.Element[];
9 | unboxed?: boolean;
10 | style?: ViewStyle;
11 | interpolate?: { [key: string]: string[] | number[] };
12 | };
13 |
14 | type Interpolations = {
15 | [key: string]: Animated.AnimatedInterpolation;
16 | };
17 |
18 | const AnimatedScreenCollapsibleElement: React.FC = ({
19 | animatedStyle,
20 | children,
21 | unboxed,
22 | interpolate,
23 | style,
24 | }) => {
25 | const { scrollY, headerMaxHeight } = useContext(AnimatedScreenContext);
26 | const interpolations = useMemo(() => {
27 | const interpolationsStyle: Interpolations = {};
28 | if (interpolate) {
29 | Object.keys(interpolate).forEach(property => {
30 | interpolationsStyle[property] = scrollY.interpolate({
31 | inputRange: [0, headerMaxHeight],
32 | outputRange: interpolate[property],
33 | extrapolate: 'clamp',
34 | });
35 | });
36 | }
37 | return interpolationsStyle;
38 | }, [headerMaxHeight, interpolate, scrollY]);
39 | return (
40 | <>
41 | {React.Children.map(children, (child: JSX.Element) => (
42 |
62 | {React.cloneElement(child)}
63 |
64 | ))}
65 | >
66 | );
67 | };
68 |
69 | export default React.memo(AnimatedScreenCollapsibleElement);
70 |
--------------------------------------------------------------------------------
/src/AnimatedScreenCollapsibleElement/index.ts:
--------------------------------------------------------------------------------
1 | import AnimatedScreenCollapsibleElement from './AnimatedScreenCollapsibleElement';
2 |
3 | export default AnimatedScreenCollapsibleElement;
4 |
--------------------------------------------------------------------------------
/src/AnimatedScreenContext.tsx:
--------------------------------------------------------------------------------
1 | import { createContext } from 'react';
2 | import {
3 | Animated,
4 | NativeSyntheticEvent,
5 | NativeScrollEvent,
6 | } from 'react-native';
7 | import { heightPercentageToDP as hp } from 'react-native-responsive-screen';
8 |
9 | type Props = {
10 | headerMaxHeight?: number;
11 | headerMinHeight?: number;
12 | };
13 |
14 | type Context = {
15 | headerMaxHeight: number;
16 | headerMinHeight: number;
17 | onScroll?: (e: NativeSyntheticEvent) => void;
18 | scrollY: Animated.Value;
19 | disableParallaxEffect?: boolean;
20 | };
21 |
22 | const AnimatedScreenContext = createContext({
23 | headerMaxHeight: hp(30),
24 | headerMinHeight: 100,
25 | scrollY: new Animated.Value(0),
26 | });
27 |
28 | export default AnimatedScreenContext;
29 |
--------------------------------------------------------------------------------
/src/AnimatedScreenElement/AnimatedScreenElement.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useMemo } from 'react';
2 | import { widthPercentageToDP } from 'react-native-responsive-screen';
3 | import { Animated, ViewStyle, StyleProp } from 'react-native';
4 | import AnimatedScreenContext from '../AnimatedScreenContext';
5 |
6 | type Props = {
7 | animatedStyle?: (
8 | scrollY: Animated.Value,
9 | ) => Animated.WithAnimatedValue>;
10 | children: JSX.Element | JSX.Element[];
11 | unboxed?: boolean;
12 | interpolate?: { [key: string]: string[] | number[] };
13 | style?: ViewStyle;
14 | };
15 |
16 | type Interpolations = {
17 | [key: string]: Animated.AnimatedInterpolation;
18 | };
19 |
20 | const AnimatedScreenElement: React.FC = ({
21 | animatedStyle,
22 | children,
23 | unboxed,
24 | interpolate,
25 | style,
26 | }) => {
27 | const { scrollY, headerMaxHeight } = useContext(AnimatedScreenContext);
28 | const interpolations = useMemo(() => {
29 | const interpolationsStyle: Interpolations = {};
30 | if (interpolate) {
31 | Object.keys(interpolate).forEach(property => {
32 | interpolationsStyle[property] = scrollY.interpolate({
33 | inputRange: [0, headerMaxHeight],
34 | outputRange: interpolate[property],
35 | extrapolate: 'clamp',
36 | });
37 | });
38 | }
39 | return interpolationsStyle;
40 | }, [headerMaxHeight, interpolate, scrollY]);
41 | return (
42 | <>
43 | {React.Children.map(children, (child: JSX.Element) => (
44 |
57 | {React.cloneElement(child)}
58 |
59 | ))}
60 | >
61 | );
62 | };
63 |
64 | export default React.memo(AnimatedScreenElement);
65 |
--------------------------------------------------------------------------------
/src/AnimatedScreenElement/index.ts:
--------------------------------------------------------------------------------
1 | import AnimatedScreenElement from './AnimatedScreenElement';
2 |
3 | export default AnimatedScreenElement;
4 |
--------------------------------------------------------------------------------
/src/AnimatedScreenFlatList/AnimatedScreenFlatList.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState, useMemo, useCallback } from 'react';
2 | import {
3 | Animated,
4 | NativeSyntheticEvent,
5 | NativeScrollEvent,
6 | ViewStyle,
7 | FlatListProps,
8 | View,
9 | FlatList,
10 | } from 'react-native';
11 | import AnimatedScreenContext from '../AnimatedScreenContext';
12 | import styles from './styles';
13 |
14 | type Props = Animated.AnimatedProps> & {
15 | animatedStyle?: (scrollY: Animated.Value) => ViewStyle;
16 | style?: ViewStyle;
17 | ListFooterComponent?: () => JSX.Element | null;
18 | contentContainerStyle?: ViewStyle;
19 | flatlistRef?: React.RefObject>;
20 | };
21 |
22 | const AnimatedScreenFlatList: React.FC = ({
23 | animatedStyle,
24 | style,
25 | ListFooterComponent = () => null,
26 | contentContainerStyle = {},
27 | onScroll: onScrollFromProps,
28 | flatlistRef,
29 | ...flatListProps
30 | }) => {
31 | const [contentHeight, setContentHeight] = useState(0);
32 | const [containerHeight, setContainerHeight] = useState(0);
33 | const {
34 | scrollY,
35 | onScroll,
36 | headerMaxHeight,
37 | headerMinHeight,
38 | disableParallaxEffect,
39 | } = useContext(AnimatedScreenContext);
40 |
41 | const shouldScroll = useMemo(
42 | () => contentHeight > containerHeight - headerMaxHeight,
43 | [contentHeight, containerHeight, headerMaxHeight],
44 | );
45 | const handleSetContent = useCallback(
46 | (_, height) => setContentHeight(height),
47 | [],
48 | );
49 | const handleSetContainer = useCallback(
50 | e => setContainerHeight(e.nativeEvent.layout.height),
51 | [],
52 | );
53 | const contentStyle = useMemo(
54 | () => ({
55 | minHeight: shouldScroll ? containerHeight + headerMaxHeight : undefined,
56 | ...contentContainerStyle,
57 | }),
58 | [shouldScroll, containerHeight, headerMaxHeight, contentContainerStyle],
59 | );
60 | const offSet = disableParallaxEffect
61 | ? headerMaxHeight + headerMinHeight
62 | : headerMinHeight;
63 |
64 | const listFooter = useMemo(() => {
65 | return (
66 | <>
67 |
68 |
69 | >
70 | );
71 | }, [ListFooterComponent, offSet]);
72 |
73 | const flatListStyle = useMemo(() => {
74 | return [
75 | animatedStyle && animatedStyle(scrollY),
76 | {
77 | paddingTop: scrollY.interpolate({
78 | inputRange: [0, headerMaxHeight],
79 | outputRange: [headerMaxHeight, offSet],
80 | extrapolate: 'clamp',
81 | }),
82 | },
83 | style,
84 | ];
85 | }, [animatedStyle, headerMaxHeight, headerMinHeight, scrollY, style]);
86 |
87 | const handleScroll = (e: NativeSyntheticEvent): void => {
88 | if (onScroll) {
89 | onScroll(e);
90 | }
91 |
92 | if (onScrollFromProps) {
93 | onScrollFromProps(e);
94 | }
95 |
96 | scrollY.setValue(e.nativeEvent.contentOffset.y);
97 | };
98 |
99 | return (
100 |
113 | );
114 | };
115 |
116 | export default React.memo(AnimatedScreenFlatList);
117 |
--------------------------------------------------------------------------------
/src/AnimatedScreenFlatList/index.ts:
--------------------------------------------------------------------------------
1 | import AnimatedScreenFlatList from './AnimatedScreenFlatList';
2 |
3 | export default AnimatedScreenFlatList;
4 |
--------------------------------------------------------------------------------
/src/AnimatedScreenFlatList/styles.ts:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 |
3 | export default StyleSheet.create({
4 | wrapper: {
5 | height: 200,
6 | },
7 | });
8 |
--------------------------------------------------------------------------------
/src/AnimatedScreenHeader/AnimatedScreenHeader.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { View, Animated, ViewStyle, SafeAreaView } from 'react-native';
3 | import { BlurView } from '@react-native-community/blur';
4 | import AnimatedScreenContext from '../AnimatedScreenContext';
5 | import useStyle from './style';
6 |
7 | type Props = {
8 | boxed?: boolean;
9 | children?: JSX.Element | JSX.Element[];
10 | animatedStyle?: (scrollY: Animated.Value) => ViewStyle;
11 | renderBackground?: (scrollY: Animated.Value) => JSX.Element;
12 | withShadow?: boolean;
13 | blur?: boolean;
14 | backgroundColor?: string;
15 | withSafeArea?: boolean;
16 | style?: ViewStyle;
17 | };
18 |
19 | const AnimatedScreenHeader: React.FC = ({
20 | children,
21 | boxed,
22 | animatedStyle,
23 | blur,
24 | withShadow,
25 | backgroundColor = 'white',
26 | renderBackground,
27 | withSafeArea = true,
28 | style,
29 | }) => {
30 | const { scrollY, headerMaxHeight, headerMinHeight } = useContext(
31 | AnimatedScreenContext,
32 | );
33 | const styles = useStyle({
34 | boxed,
35 | blur,
36 | withShadow,
37 | backgroundColor,
38 | });
39 |
40 | const shadowOpacity = scrollY.interpolate({
41 | inputRange: [0, headerMaxHeight],
42 | outputRange: [0, 0.3],
43 | extrapolate: 'clamp',
44 | });
45 |
46 | const elevation = scrollY.interpolate({
47 | inputRange: [0, headerMaxHeight],
48 | outputRange: [0, 14],
49 | extrapolate: 'clamp',
50 | });
51 |
52 | return (
53 |
68 | {blur ? (
69 |
74 | ) : (
75 |
76 | )}
77 | {renderBackground && renderBackground(scrollY)}
78 | {withSafeArea ? {children} : <>{children}>}
79 |
80 | );
81 | };
82 |
83 | export default React.memo(AnimatedScreenHeader);
84 |
--------------------------------------------------------------------------------
/src/AnimatedScreenHeader/index.ts:
--------------------------------------------------------------------------------
1 | import AnimatedScreenHeader from './AnimatedScreenHeader';
2 |
3 | export default AnimatedScreenHeader;
4 |
--------------------------------------------------------------------------------
/src/AnimatedScreenHeader/style.ts:
--------------------------------------------------------------------------------
1 | import { StyleSheet, ViewStyle } from 'react-native';
2 | import { widthPercentageToDP } from 'react-native-responsive-screen';
3 |
4 | type AnimatedScreen = {
5 | animatedWrapper: ViewStyle;
6 | headerBackground: ViewStyle;
7 | };
8 |
9 | type Props = {
10 | backgroundColor: string;
11 | boxed?: boolean;
12 | blur?: boolean;
13 | withShadow?: boolean;
14 | };
15 |
16 | export default ({
17 | boxed,
18 | blur,
19 | withShadow,
20 | backgroundColor,
21 | }: Props): AnimatedScreen =>
22 | StyleSheet.create({
23 | animatedWrapper: {
24 | paddingHorizontal: boxed ? widthPercentageToDP('8%') : undefined,
25 | position: 'absolute',
26 | width: '100%',
27 | zIndex: 5,
28 | justifyContent: 'center',
29 | backgroundColor: withShadow ? backgroundColor : undefined,
30 | shadowColor: '#000',
31 | shadowRadius: 6.68,
32 | shadowOffset: {
33 | width: 0,
34 | height: 2,
35 | },
36 | },
37 | headerBackground: {
38 | position: 'absolute',
39 | height: '100%',
40 | width: boxed ? widthPercentageToDP('116%') : '100%',
41 | backgroundColor: blur ? 'rgba(255, 255, 255, 0.5)' : backgroundColor,
42 | },
43 | });
44 |
--------------------------------------------------------------------------------
/src/AnimatedScreenScrollView/AnimatedScreenScrollView.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import {
3 | Animated,
4 | NativeSyntheticEvent,
5 | NativeScrollEvent,
6 | ViewStyle,
7 | ScrollViewProps,
8 | View,
9 | ScrollView,
10 | } from 'react-native';
11 | import AnimatedScreenContext from '../AnimatedScreenContext';
12 |
13 | type Props = Animated.AnimatedProps & {
14 | animatedStyle?: (scrollY: Animated.Value) => ViewStyle;
15 | children: JSX.Element | JSX.Element[] | null;
16 | style?: ViewStyle;
17 | scrollViewRef?: React.RefObject;
18 | };
19 |
20 | const AnimatedScreenScrollView: React.FC = ({
21 | animatedStyle,
22 | children,
23 | style,
24 | scrollViewRef,
25 | ...scrollViewProps
26 | }) => {
27 | const {
28 | scrollY,
29 | onScroll,
30 | headerMaxHeight,
31 | headerMinHeight,
32 | disableParallaxEffect,
33 | } = useContext(AnimatedScreenContext);
34 | const offSet = disableParallaxEffect
35 | ? headerMaxHeight + headerMinHeight
36 | : headerMinHeight;
37 |
38 | const handleScroll = (e: NativeSyntheticEvent): void => {
39 | if (onScroll) {
40 | onScroll(e);
41 | }
42 | scrollY.setValue(e.nativeEvent.contentOffset.y);
43 | };
44 |
45 | return (
46 |
65 | {children}
66 |
67 |
68 | );
69 | };
70 |
71 | export default React.memo(AnimatedScreenScrollView);
72 |
--------------------------------------------------------------------------------
/src/AnimatedScreenScrollView/index.ts:
--------------------------------------------------------------------------------
1 | import AnimatedScreenScrollView from './AnimatedScreenScrollView';
2 |
3 | export default AnimatedScreenScrollView;
4 |
--------------------------------------------------------------------------------
/src/AnimatedScreenSectionList/AnimatedScreenSectionList.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState, useMemo, useCallback } from 'react';
2 | import {
3 | Animated,
4 | NativeSyntheticEvent,
5 | NativeScrollEvent,
6 | SectionListProps,
7 | ViewStyle,
8 | View,
9 | SectionList,
10 | } from 'react-native';
11 | import AnimatedScreenContext from '../AnimatedScreenContext';
12 |
13 | type Props = Animated.AnimatedProps> & {
14 | animatedStyle?: (scrollY: Animated.Value) => ViewStyle;
15 | style?: ViewStyle;
16 | ListFooterComponent?: () => JSX.Element | null;
17 | sectionlistRef?: React.RefObject>;
18 | };
19 |
20 | const AnimatedScreenSectionList: React.FC = ({
21 | animatedStyle,
22 | style,
23 | ListFooterComponent = () => null,
24 | sectionlistRef,
25 | ...sectionListProps
26 | }) => {
27 | const [contentHeight, setContentHeight] = useState(0);
28 | const [containerHeight, setContainerHeight] = useState(0);
29 | const { scrollY, onScroll, headerMaxHeight, headerMinHeight } = useContext(
30 | AnimatedScreenContext,
31 | );
32 | const shouldScroll = useMemo(
33 | () => contentHeight > containerHeight - headerMaxHeight,
34 | [contentHeight, containerHeight, headerMaxHeight],
35 | );
36 | const handleSetContent = useCallback(
37 | (_, height) => setContentHeight(height),
38 | [],
39 | );
40 | const handleSetContainer = useCallback(
41 | e => setContainerHeight(e.nativeEvent.layout.height),
42 | [],
43 | );
44 | const contentStyle = useMemo(
45 | () => ({
46 | minHeight: shouldScroll ? containerHeight + headerMaxHeight : undefined,
47 | }),
48 | [shouldScroll, containerHeight, headerMaxHeight],
49 | );
50 |
51 | const handleScroll = (e: NativeSyntheticEvent): void => {
52 | if (onScroll) {
53 | onScroll(e);
54 | }
55 | scrollY.setValue(e.nativeEvent.contentOffset.y);
56 | };
57 |
58 | return (
59 | <>
60 |
72 |
73 |
74 | >
75 | }
76 | style={[
77 | animatedStyle && animatedStyle(scrollY),
78 | {
79 | paddingTop: scrollY.interpolate({
80 | inputRange: [0, headerMaxHeight],
81 | outputRange: [headerMaxHeight, headerMinHeight],
82 | extrapolate: 'clamp',
83 | }),
84 | },
85 | style,
86 | ]}
87 | // eslint-disable-next-line react/jsx-props-no-spreading
88 | {...sectionListProps}
89 | />
90 | >
91 | );
92 | };
93 |
94 | export default React.memo(AnimatedScreenSectionList);
95 |
--------------------------------------------------------------------------------
/src/AnimatedScreenSectionList/index.ts:
--------------------------------------------------------------------------------
1 | import AnimatedScreenSectionList from './AnimatedScreenSectionList';
2 |
3 | export default AnimatedScreenSectionList;
4 |
--------------------------------------------------------------------------------
/src/AnimatedScreenWrapper/AnimatedScreenWrapper.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, ReactElement } from 'react';
2 | import { initialWindowMetrics } from 'react-native-safe-area-context';
3 | import {
4 | Animated,
5 | NativeSyntheticEvent,
6 | NativeScrollEvent,
7 | } from 'react-native';
8 | import AnimatedScreenContext from '../AnimatedScreenContext';
9 | import AnimatedScreenHeader from '../AnimatedScreenHeader';
10 |
11 | type Props = {
12 | headerMaxHeight?: number;
13 | headerMinHeight?: number;
14 | avoidSafeArea?: boolean;
15 | disableParallaxEffect?: boolean;
16 | onScroll?: (e: NativeSyntheticEvent) => void;
17 | children: [
18 | ReactElement>,
19 | JSX.Element,
20 | ];
21 | };
22 |
23 | const AnimatedScreenWrapper: React.FC = ({
24 | children,
25 | onScroll,
26 | headerMaxHeight = 200,
27 | headerMinHeight = 70,
28 | avoidSafeArea = false,
29 | disableParallaxEffect,
30 | }) => {
31 | const [scrollY] = useState(new Animated.Value(0));
32 | const { top: safeTop } = initialWindowMetrics?.insets || { top: 0 };
33 |
34 | return (
35 |
44 | {children}
45 |
46 | );
47 | };
48 |
49 | export default React.memo(AnimatedScreenWrapper);
50 |
--------------------------------------------------------------------------------
/src/AnimatedScreenWrapper/index.ts:
--------------------------------------------------------------------------------
1 | import AnimatedScreenWrapper from './AnimatedScreenWrapper';
2 |
3 | export default AnimatedScreenWrapper;
4 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import AnimatedScreenHeader from './AnimatedScreenHeader';
2 | import AnimatedScreenWrapper from './AnimatedScreenWrapper';
3 | import AnimatedScreenFlatList from './AnimatedScreenFlatList';
4 | import AnimatedScreenSectionList from './AnimatedScreenSectionList';
5 | import AnimatedScreenScrollView from './AnimatedScreenScrollView';
6 | import AnimatedScreenCollapsibleElement from './AnimatedScreenCollapsibleElement';
7 | import AnimatedScreenElement from './AnimatedScreenElement/AnimatedScreenElement';
8 |
9 | const AnimatedScreen = {
10 | Header: AnimatedScreenHeader,
11 | Element: AnimatedScreenElement,
12 | Wrapper: AnimatedScreenWrapper,
13 | FlatList: AnimatedScreenFlatList,
14 | ScrollView: AnimatedScreenScrollView,
15 | SectionList: AnimatedScreenSectionList,
16 | CollapsibleElement: AnimatedScreenCollapsibleElement,
17 | };
18 |
19 | export default AnimatedScreen;
20 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | // `tsconfig.json` for react native TypeScript package
2 | {
3 | "compilerOptions": {
4 | "module": "es6",
5 | "declaration": true,
6 | "outDir": "./lib",
7 | "skipLibCheck": true,
8 | "allowJs": true,
9 | "allowSyntheticDefaultImports": true,
10 | "esModuleInterop": true,
11 | "isolatedModules": true,
12 | "jsx": "react",
13 | "lib": [
14 | "es6"
15 | ],
16 | "moduleResolution": "node",
17 | "strict": true,
18 | "target": "esnext"
19 | },
20 | "include": [
21 | "src"
22 | ],
23 | "exclude": [
24 | "node_modules",
25 | "**/__tests__/*"
26 | ]
27 | }
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "tslint:recommended"
4 | ]
5 | }
--------------------------------------------------------------------------------