Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Building Arrive's Confetti in React Native with Reanimated - Confetti Final
import React, {useMemo} from 'react'
import Animated from 'react-native-reanimated'
import {View, Dimensions, StyleSheet} from 'react-native'
import FastImage from 'react-native-fast-image'
import ConfettiImage from 'assets/images/confetti.png'
const NUM_CONFETTI = 100
const COLORS = ['#00e4b2', '#09aec5', '#107ed5']
const CONFETTI_SIZE = 16
const createConfetti = () => {
const {width: screenWidth} = Dimensions.get('screen')
return [...new Array(NUM_CONFETTI)].map((_, i) => {
const clock = new Animated.Clock()
return {
key: i,
// Spawn confetti from two different sources, a quarter
// from the left and a quarter from the right edge of the screen.
x: new Animated.Value(
screenWidth * (i % 2 ? 0.25 : 0.75) - CONFETTI_SIZE / 2
),
y: new Animated.Value(-60),
angle: new Animated.Value(0),
xVel: new Animated.Value(Math.random() * 400 - 200),
yVel: new Animated.Value(Math.random() * 150 + 150),
angleVel: new Animated.Value((Math.random() * 3 - 1.5) * Math.PI),
delay: new Animated.Value(Math.floor(i / 10) * 0.3),
elasticity: Math.random() * 0.3 + 0.1,
color: COLORS[i % COLORS.length],
clock,
}
})
}
const Confetti = () => {
const confetti = useMemo(createConfetti, [])
return (
<View pointerEvents="none" style={StyleSheet.absoluteFill}>
{confetti.map(
({
key,
x,
y,
angle,
xVel,
yVel,
angleVel,
color,
elasticity,
delay,
clock,
}) => {
return (
<React.Fragment key={key}>
<Animated.Code>
{() => {
const {
startClock,
set,
add,
sub,
divide,
diff,
multiply,
cond,
clockRunning,
greaterThan,
lessThan,
} = Animated
const {width: screenWidth} = Dimensions.get('window')
const timeDiff = diff(clock)
const dt = divide(timeDiff, 1000)
const dy = multiply(dt, yVel)
const dx = multiply(dt, xVel)
const dAngle = multiply(dt, angleVel)
return cond(
clockRunning(clock),
[
cond(
greaterThan(delay, 0),
[set(delay, sub(delay, dt))],
[
set(y, add(y, dy)),
set(x, add(x, dx)),
set(angle, add(angle, dAngle)),
]
),
cond(greaterThan(x, screenWidth - CONFETTI_SIZE), [
set(x, screenWidth - CONFETTI_SIZE),
set(xVel, multiply(xVel, -elasticity)),
]),
cond(lessThan(x, 0), [
set(x, 0),
set(xVel, multiply(xVel, -elasticity)),
]),
],
[startClock(clock), timeDiff]
)
}}
</Animated.Code>
<Animated.View
style={[
styles.confettiContainer,
{
transform: [
{translateX: x},
{translateY: y},
{rotate: angle},
{rotateX: angle},
{rotateY: angle},
],
},
]}
>
<FastImage
tintColor={color}
source={ConfettiImage}
style={styles.confetti}
/>
</Animated.View>
</React.Fragment>
)
}
)}
</View>
)
}
const styles = StyleSheet.create({
confettiContainer: {
position: 'absolute',
top: 0,
left: 0,
},
confetti: {
width: CONFETTI_SIZE,
height: CONFETTI_SIZE,
},
})
export default Confetti
@peralmq

This comment has been minimized.

Copy link

@peralmq peralmq commented Apr 14, 2020

Thanks for a great guide!

We used this to build a nice confetti based on an SVG logo but ran into some performance problems (similar to software-mansion/react-native-reanimated#381) which we solved by moving the Animated.Code code into the Animated.useCode hook (and make it dependent on the confetti list).

Might make sense for you too? Since you're already have a functional component that is using hooks like the useMemo one.

@peacechen

This comment has been minimized.

Copy link

@peacechen peacechen commented Apr 16, 2020

@peralmq
I also get the "excessive number of pending callbacks" error. Would you mind sharing how you used the useCode hook? The Reanimated documentation is very sparse on that.

Here's the adaptation in an NPM package:
https://www.npmjs.com/package/react-native-make-it-rain

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.