├── .gitignore
├── Example
├── .gitignore
├── App.js
├── app.json
├── assets
│ ├── cashMoney.js
│ └── icon.png
├── babel.config.js
└── package.json
├── LICENSE.MD
├── README.MD
├── package.json
└── src
├── confetti.js
├── index.js
└── makeItRain.js
/.gitignore:
--------------------------------------------------------------------------------
1 | coverage
2 | jsconfig.json
3 | .vscode
4 |
5 | # OSX
6 | #
7 | .DS_Store
8 |
9 | # Xcode
10 | #
11 | build/
12 | *.pbxuser
13 | !default.pbxuser
14 | *.mode1v3
15 | !default.mode1v3
16 | *.mode2v3
17 | !default.mode2v3
18 | *.perspectivev3
19 | !default.perspectivev3
20 | xcuserdata
21 | *.xccheckout
22 | *.moved-aside
23 | DerivedData
24 | *.hmap
25 | *.ipa
26 |
27 | # Android/IJ
28 | #
29 | .idea
30 | .gradle
31 | local.properties
32 |
33 | # node.js
34 | #
35 | node_modules/
36 | npm-debug.log
37 | Example/react-native-make-it-rain/
38 | package-lock.json
39 | yarn.lock
40 |
41 | # BUCK
42 | buck-out/
43 | \.buckd/
44 | android/app/libs
45 | android/keystores/debug.keystore
46 |
--------------------------------------------------------------------------------
/Example/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 | *.jks
5 | *.p8
6 | *.p12
7 | *.key
8 | *.mobileprovision
9 | *.orig.*
10 | web-build/
11 | web-report/
12 |
13 | # macOS
14 | .DS_Store
15 |
--------------------------------------------------------------------------------
/Example/App.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { StyleSheet, Text, View, Switch } from 'react-native';
3 | import MakeItRain from './react-native-make-it-rain';
4 | import CashMoney from './assets/cashMoney';
5 |
6 | export default function App() {
7 | const [isConfetti, setIsConfetti] = useState(true);
8 | const toggleSwitch = () => setIsConfetti(previousState => !previousState);
9 |
10 | return (
11 |
12 | Make It Rain
13 | { isConfetti ?
14 | 🤍}
17 | itemTintStrength={0.8}
18 | />
19 | :
20 | }
24 | itemColors={['#ffffff00']}
25 | flavor={"rain"}
26 | />
27 | }
28 |
29 |
30 | rain
31 |
39 | arrive
40 |
41 |
42 | );
43 | }
44 |
45 | const styles = StyleSheet.create({
46 | container: {
47 | flex: 1,
48 | backgroundColor: '#fff',
49 | alignItems: 'center',
50 | justifyContent: 'space-around',
51 | },
52 | switchRow: {
53 | flexDirection: 'row',
54 | paddingVertical: 30,
55 | alignItems: 'center',
56 | justifyContent: 'center',
57 | },
58 | font: {
59 | fontSize: 40,
60 | },
61 | switch: {
62 | marginHorizontal: 20,
63 | transform: [{ scaleX: 2 }, { scaleY: 2 }]
64 | }
65 | });
66 |
--------------------------------------------------------------------------------
/Example/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "Make It Rain Example",
4 | "slug": "Example",
5 | "platforms": [
6 | "ios",
7 | "android",
8 | "web"
9 | ],
10 | "version": "1.0.0",
11 | "orientation": "portrait",
12 | "icon": "./assets/icon.png",
13 | "splash": {
14 | "image": "./assets/icon.png",
15 | "resizeMode": "contain",
16 | "backgroundColor": "#ffffff"
17 | },
18 | "updates": {
19 | "fallbackToCacheTimeout": 0
20 | },
21 | "assetBundlePatterns": [
22 | "**/*"
23 | ],
24 | "ios": {
25 | "supportsTablet": true
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Example/assets/cashMoney.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import {
4 | Svg,
5 | Circle,
6 | Ellipse,
7 | G,
8 | LinearGradient,
9 | RadialGradient,
10 | Line,
11 | Path,
12 | Polygon,
13 | Polyline,
14 | Rect,
15 | Symbol,
16 | Text,
17 | Use,
18 | Defs,
19 | Stop
20 | } from 'react-native-svg';
21 |
22 | // Note: This SVG is offset to the right which prevents the tint overlay from aligning properly.
23 | export default function CashMoney(props) {
24 | return (
25 |
49 | );
50 | }
51 |
--------------------------------------------------------------------------------
/Example/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peacechen/react-native-make-it-rain/0ec65022db0492c504726d8f1b1661360baa7cdf/Example/assets/icon.png
--------------------------------------------------------------------------------
/Example/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(true);
3 | return {
4 | presets: ['babel-preset-expo'],
5 | };
6 | };
7 |
--------------------------------------------------------------------------------
/Example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "node_modules/expo/AppEntry.js",
3 | "scripts": {
4 | "start": "expo start",
5 | "cp": "cpx ../src/** react-native-make-it-rain -u",
6 | "postinstall": "npm run cp",
7 | "android": "expo start --android",
8 | "ios": "expo start --ios",
9 | "web": "expo start --web",
10 | "eject": "expo eject"
11 | },
12 | "dependencies": {
13 | "expo": "~37.0.3",
14 | "react": "~16.9.0",
15 | "react-dom": "~16.9.0",
16 | "react-native": "https://github.com/expo/react-native/archive/sdk-37.0.1.tar.gz",
17 | "react-native-reanimated": "^1.8.0",
18 | "react-native-svg": "11.0.1",
19 | "react-native-web": "~0.11.7"
20 | },
21 | "devDependencies": {
22 | "@babel/core": "^7.8.6",
23 | "babel-preset-expo": "~8.1.0",
24 | "cpx": "^1.5.0"
25 | },
26 | "private": true
27 | }
28 |
--------------------------------------------------------------------------------
/LICENSE.MD:
--------------------------------------------------------------------------------
1 | The MIT License (MIT) Copyright (c) 2020 Peace Chen
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.
8 |
--------------------------------------------------------------------------------
/README.MD:
--------------------------------------------------------------------------------
1 | # React Native "Make It Rain"
2 |
3 |
17 |
18 |
23 |
24 | ### v1.0.0 breaking changes
25 | [Prop names](#props) have been generalized to represent that any type of component can be passed in as the falling pieces.
26 | The `itemComponent` (previously `moneyComponent`) no longer defaults to an SVG. This allowed for removal of the dependency on `react-native-svg`. Refer to the Example project for how to pass in an SVG `itemComponent`.
27 |
28 |
29 | ## Summary
30 |
31 | This is a self-contained component that may be placed in any component. It takes up the entire parent view and is positioned absolutely, overlaying other content in the parent.
32 |
33 | The default animation was adapted from [Shopify's Arrive confetti example](https://engineering.shopify.com/blogs/engineering/building-arrives-confetti-in-react-native-with-reanimated).
34 |
35 |
36 |
37 | The original *Make It Rain* animation was adapted from Joel Arvidsson's [MakeItRain example](https://github.com/oblador/react-native-animatable/tree/master/Examples/MakeItRain). It has been rewritten to use `react-native-reanimated`.
38 |
39 |
40 |
41 | The `flavor` [prop](#props) selects between them.
42 |
43 |
44 | ## Sample Code
45 | ```jsx
46 | import React, {Component} from 'react';
47 | import { View, Text } from 'react-native';
48 | import MakeItRain from 'react-native-make-it-rain';
49 |
50 | class Demo extends Component {
51 | render() {
52 | return (
53 |
54 | Make It Rain
55 | 🤍}
59 | itemTintStrength={0.8}
60 | />
61 |
62 | );
63 | }
64 | }
65 | ```
66 |
67 | ## Usage
68 |
69 | > npm install react-native-make-it-rain --save
70 |
71 | ## Props
72 | | Prop | Description | Type | Default |
73 | | ------------------------------ | ---------------------------------------------------- | -------- | ---------- |
74 | | **`numItems`** | How many items fall | Integer | 100 |
75 | | **`itemComponent`** | Replaces the built-in item component. Can use any React component (Icon, Image, View, SVG etc) | Component | `` |
76 | | **`itemDimensions`** | Size of item. If `itemComponent` is supplied, size should be set to match. | Object | `{ width: 20, height: 10 }` |
77 | | **`itemColors`** | Colors of falling items. | Array | ['#00e4b2', '#09aec5', '#107ed5'] |
78 | | **`itemTintStrength`** | Opacity of color overlay on item (0 - 1.0) | Number | 1.0 |
79 | | **`flavor`** | The animation behavior ("arrive" or "rain") | String | "arrive" |
80 | | **`fallSpeed`** | How fast the item falls | Integer | 50 |
81 | | **`flipSpeed`** | Item flip speed | Integer | 3 |
82 | | **`horizSpeed`** | How fast the item moves horizontally | Integer | 50 |
83 | | **`continuous`** | Rain down continuously | Boolean | true |
84 |
85 |
86 | # Sample Application
87 |
88 | If you'd like to see it in action, run these commands:
89 | ```sh
90 | cd Example
91 | npm run cp
92 | npm install
93 | npm start
94 | ```
95 |
96 | The sample app is an Expo project created with `create-react-native-app`.
97 |
98 | ## Development
99 |
100 | The source files are copied from the project root directory into `Example/react-native-make-it-rain` using `npm run cp`. If a source file is modified, it must be copied over again with `npm run cp`.
101 |
102 | Ideally the Example project could include the parent's source files using package.json, but that causes a babel interopRequireDefault runtime error.
103 |
104 |
105 | ## Credits
106 | Cash SVG in the Example project was sourced from [IconFinder](https://www.iconfinder.com/icons/1889190/currency_currency_exchange_dollar_euro_exchange_finance_money_icon) and is used under the Creative Commons license.
107 |
108 | ## ToDo
109 | * Tests
110 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-make-it-rain",
3 | "version": "1.1.1",
4 | "main": "src/index.js",
5 | "author": "Peace Chen",
6 | "license": "MIT",
7 | "scripts": {
8 | "start": "node node_modules/react-native/local-cli/cli.js start"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/peacechen/react-native-make-it-rain.git"
13 | },
14 | "bugs": {
15 | "url": "https://github.com/peacechen/react-native-make-it-rain/issues"
16 | },
17 | "homepage": "https://github.com/peacechen/react-native-make-it-rain#readme",
18 | "keywords": [
19 | "react native",
20 | "react native make it rain",
21 | "react-native-make-it-rain",
22 | "Make It Rain",
23 | "confetti",
24 | "rain",
25 | "money"
26 | ],
27 | "devDependencies": {
28 | "react": ">=16.8.0",
29 | "react-native": ">=0.60.0"
30 | },
31 | "dependencies": {
32 | "react-native-reanimated": "^1.8.0"
33 | },
34 | "peerDependencies": {}
35 | }
36 |
--------------------------------------------------------------------------------
/src/confetti.js:
--------------------------------------------------------------------------------
1 | // Inpsired by
2 | // https://engineering.shopify.com/blogs/engineering/building-arrives-confetti-in-react-native-with-reanimated
3 | // https://dev.to/hrastnik/implementing-gravity-and-collision-detection-in-react-native-2hk5
4 |
5 | import React, { useEffect, useMemo } from 'react'
6 | import Animated from 'react-native-reanimated'
7 | import {View, Dimensions, StyleSheet} from 'react-native'
8 |
9 | const {
10 | View: ReanimatedView,
11 | Clock,
12 | Value,
13 | useCode,
14 | block,
15 | startClock,
16 | stopClock,
17 | set,
18 | add,
19 | sub,
20 | divide,
21 | diff,
22 | multiply,
23 | cond,
24 | clockRunning,
25 | greaterThan,
26 | lessThan,
27 | lessOrEq,
28 | eq,
29 | } = Animated;
30 |
31 | const Confetti = props => {
32 | const [containerDims, setContainerDims] = React.useState(Dimensions.get('screen'));
33 | const [confetti, setConfetti] = React.useState(createConfetti(containerDims));
34 | const clock = new Clock();
35 |
36 | useEffect(() => {
37 | return () => { // func indicates unmount
38 | stopClock(clock);
39 | setConfetti([]);
40 | }
41 | }, []);
42 |
43 | // Update confetti positioning if screen changes (e.g. rotation)
44 | const onLayout = (event => {
45 | setContainerDims({
46 | width: event.nativeEvent.layout.width,
47 | height: event.nativeEvent.layout.height,
48 | });
49 | });
50 |
51 | function createConfetti(dimensions) {
52 | return useMemo(() => {
53 | const { width, height } = dimensions;
54 | // Adapt velocity props
55 | const xVelMax = props.horizSpeed * 8;
56 | const yVelMax = props.fallSpeed * 3;
57 | const angleVelMax = props.flipSpeed;
58 |
59 | return [...new Array(props.numItems)].map((_, index) => {
60 | return {
61 | index,
62 | // Spawn confetti from two different sources, a quarter
63 | // from the left and a quarter from the right edge of the screen.
64 | x: new Value(
65 | width * (index % 2 ? 0.25 : 0.75) - props.itemDimensions.width / 2
66 | ),
67 | y: new Value(-props.itemDimensions.height * 2),
68 | angle: new Value(0),
69 | xVel: new Value(Math.random() * xVelMax - (xVelMax / 2)),
70 | yVel: new Value(Math.random() * yVelMax + yVelMax),
71 | angleVel: new Value((Math.random() * angleVelMax - (angleVelMax / 2)) * Math.PI),
72 | delay: new Value(Math.floor(index / 10) * 0.3),
73 | elasticity: Math.random() * 0.9 + 0.1,
74 | color: props.itemColors[index % props.itemColors.length],
75 | }
76 | })
77 | return confetti;
78 | }, [dimensions]);
79 | }
80 |
81 | const useDraw = _confetti => {
82 | const nativeCode = useMemo(() => {
83 | const timeDiff = diff(clock);
84 | const nativeCode = _confetti.map(({
85 | x,
86 | y,
87 | angle,
88 | xVel,
89 | yVel,
90 | angleVel,
91 | color,
92 | elasticity,
93 | delay,
94 | }) => {
95 | const dt = divide(timeDiff, 1000)
96 | const dy = multiply(dt, yVel)
97 | const dx = multiply(dt, xVel)
98 | const dAngle = multiply(dt, angleVel)
99 |
100 | return [
101 | cond(
102 | lessOrEq(y, containerDims.height + props.itemDimensions.height),
103 | cond(
104 | greaterThan(delay, 0),
105 | [set(delay, sub(delay, dt))],
106 | [
107 | set(y, add(y, dy)),
108 | set(x, add(x, dx)),
109 | set(angle, add(angle, dAngle)),
110 | ]
111 | )
112 | ),
113 | cond(greaterThan(x, containerDims.width - props.itemDimensions.width), [
114 | set(x, containerDims.width - props.itemDimensions.width),
115 | set(xVel, multiply(xVel, -elasticity)),
116 | ]),
117 | cond(lessThan(x, 0), [
118 | set(x, 0),
119 | set(xVel, multiply(xVel, -elasticity)),
120 | ]),
121 | cond(
122 | eq(props.continuous, true),
123 | cond(
124 | greaterThan(y, containerDims.height + props.itemDimensions.height),
125 | set(y, -props.itemDimensions.height * 2),
126 | )
127 | ),
128 | ];
129 | });
130 |
131 | nativeCode.push(cond(clockRunning(clock), 0, startClock(clock)), clock);
132 | return block(nativeCode);
133 | }, [_confetti]);
134 |
135 | useCode(() => nativeCode, [nativeCode]);
136 | };
137 |
138 | useDraw(confetti);
139 |
140 | return (
141 |
142 | {confetti.map(
143 | ({ index, x, y, angle, color: backgroundColor }) => {
144 | return (
145 |
159 | { props.itemComponent }
160 |
161 |
162 | )
163 | }
164 | )}
165 |
166 | )
167 | }
168 |
169 | const styles = StyleSheet.create({
170 | animContainer: {
171 | position: 'absolute',
172 | top: 0,
173 | left: 0,
174 | alignItems: 'center',
175 | justifyContent: 'center',
176 | overflow: 'hidden',
177 | },
178 | confettiContainer: {
179 | position: 'absolute',
180 | top: 0,
181 | left: 0,
182 | }
183 | })
184 |
185 | export default Confetti
186 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View, Text } from 'react-native';
3 | import MakeItRain from './makeItRain';
4 | import Confetti from './confetti';
5 |
6 | function Root(props) {
7 | const itemComponent = props.itemComponent || ;
8 |
9 | switch (props.flavor) {
10 | case "arrive":
11 | return ( );
12 | case "rain":
13 | return ( );
14 | default:
15 | return ( Unsupported flavor );
16 | }
17 | }
18 |
19 | Root.defaultProps = {
20 | numItems: 100,
21 | itemDimensions: { width: 20, height: 10 },
22 | itemColors: ['#00e4b2', '#09aec5', '#107ed5'],
23 | itemTintStrength: 1.0,
24 | flavor: "arrive",
25 | fallSpeed: 50,
26 | flipSpeed: 3,
27 | horizSpeed: 50,
28 | continuous: true,
29 | };
30 |
31 | export default Root;
32 |
--------------------------------------------------------------------------------
/src/makeItRain.js:
--------------------------------------------------------------------------------
1 | // Spring physics from https://blog.swmansion.com/simple-physics-with-reanimated-part-3-a168d69faa51
2 |
3 | import React, { useEffect, useMemo } from 'react'
4 | import Animated from 'react-native-reanimated'
5 | import {View, Dimensions, StyleSheet} from 'react-native'
6 |
7 | const {
8 | View: ReanimatedView,
9 | Clock,
10 | Value,
11 | useCode,
12 | block,
13 | startClock,
14 | stopClock,
15 | set,
16 | abs,
17 | add,
18 | sub,
19 | divide,
20 | diff,
21 | multiply,
22 | cond,
23 | clockRunning,
24 | greaterThan,
25 | lessThan,
26 | lessOrEq,
27 | eq,
28 | } = Animated;
29 |
30 | const randomize = (max, base = 0) => Math.random() * max + base;
31 |
32 | const MakeItRain = props => {
33 | const [containerDims, setContainerDims] = React.useState(Dimensions.get('screen'));
34 | const [items, setItems] = React.useState(createItems(containerDims));
35 | const clock = new Clock();
36 |
37 | useEffect(() => {
38 | return () => { // func indicates unmount
39 | stopClock(clock);
40 | setItems([]);
41 | }
42 | }, []);
43 |
44 | // Update item positioning if screen changes (e.g. rotation)
45 | const onLayout = (event => {
46 | setContainerDims({
47 | width: event.nativeEvent.layout.width,
48 | height: event.nativeEvent.layout.height,
49 | });
50 | });
51 |
52 | function createItems(dimensions) {
53 | return useMemo(() => {
54 | const { width, height } = dimensions;
55 | // Adapt velocity props
56 | const xVelMax = props.horizSpeed;
57 | const yVelMax = props.fallSpeed * 3;
58 | const angleVelMax = props.flipSpeed;
59 |
60 | return [...new Array(props.numItems)].map((_, index) => {
61 | let x = randomize(width - props.itemDimensions.width);
62 | let y = -props.itemDimensions.height * 4;
63 | let anchor = randomize(width / 3, width / 12);
64 | let xArc = Math.abs(x - anchor);
65 |
66 | return {
67 | index,
68 | x: new Value(x),
69 | y: new Value(y),
70 | xArc: new Value(xArc),
71 | yBase: new Value(y),
72 | angle: new Value(0),
73 | anchor: new Value(anchor),
74 | tension: new Value(4 * Math.min(xArc, width/2) / width),
75 | xVel: new Value(randomize(xVelMax / 2, xVelMax / 2)),
76 | yVel: new Value(randomize(yVelMax, yVelMax)),
77 | angleVel: new Value(randomize(angleVelMax / 2, angleVelMax / 2)),
78 | delay: new Value((index / props.numItems) * height / yVelMax),
79 | color: props.itemColors[index % props.itemColors.length],
80 | }
81 | });
82 | }, [dimensions]);
83 | }
84 |
85 | const spring = (dt, position, velocity, anchor, tension = 50, mass = 1 ) => {
86 | const dist = sub(position, anchor);
87 | const acc = divide(multiply(-1, tension, dist), mass);
88 | return set(velocity, add(velocity, multiply(dt, acc)));
89 | }
90 |
91 | const swingArc = (x, y, xArc, yBase, anchor ) => {
92 | const percentArc = divide(abs(sub(x, anchor)), xArc);
93 | const yOffset = multiply(percentArc, divide(xArc, 4));
94 | return set(y, sub(yBase, yOffset));
95 | }
96 |
97 | const useDraw = _items => {
98 | const nativeCode = useMemo(() => {
99 | const timeDiff = diff(clock);
100 | const nativeCode = _items.map(({
101 | x,
102 | y,
103 | xArc,
104 | yBase,
105 | angle,
106 | xVel,
107 | yVel,
108 | angleVel,
109 | color,
110 | anchor,
111 | tension,
112 | delay,
113 | }) => {
114 | const dt = divide(timeDiff, 1000)
115 | const dy = multiply(dt, yVel)
116 | const dAngle = multiply(dt, angleVel)
117 |
118 | return [
119 | cond(
120 | lessOrEq(yBase, containerDims.height + props.itemDimensions.height),
121 | cond(
122 | greaterThan(delay, 0),
123 | [set(delay, sub(delay, dt))],
124 | [
125 | set(yBase, add(yBase, dy)),
126 | spring(dt, x, xVel, anchor, tension), // swinging motion
127 | set(x, add(x, multiply(xVel, dt))),
128 | swingArc(x, y, xArc, yBase, anchor ), // create dip in swing
129 | set(angle, add(angle, dAngle)),
130 | ]
131 | )
132 | ),
133 | cond(
134 | eq(props.continuous, true),
135 | cond(
136 | greaterThan(yBase, containerDims.height + props.itemDimensions.height),
137 | set(yBase, -props.itemDimensions.height * 4),
138 | )
139 | ),
140 | ];
141 | });
142 |
143 | nativeCode.push(cond(clockRunning(clock), 0, startClock(clock)), clock);
144 | return block(nativeCode);
145 | }, [_items]);
146 |
147 | useCode(() => nativeCode, [nativeCode]);
148 | };
149 |
150 | useDraw(items);
151 |
152 | return (
153 |
154 | {items.map(
155 | ({ index, x, y, angle, color: backgroundColor }) => {
156 | return (
157 |
171 | { props.itemComponent }
172 |
173 |
174 | )
175 | }
176 | )}
177 |
178 | )
179 | }
180 |
181 | export default MakeItRain;
182 |
183 | const styles = StyleSheet.create({
184 | animContainer: {
185 | position: 'absolute',
186 | top: 0,
187 | left: 0,
188 | alignItems: 'center',
189 | justifyContent: 'center',
190 | overflow: 'hidden',
191 | },
192 | itemContainer: {
193 | position: 'absolute',
194 | top: 0,
195 | left: 0,
196 | }
197 | })
198 |
--------------------------------------------------------------------------------