Skip to content

Instantly share code, notes, and snippets.

@imjamescrain
Last active December 15, 2023 06:59
Show Gist options
  • Save imjamescrain/e86893a1d6f85328174d036a9b263dd0 to your computer and use it in GitHub Desktop.
Save imjamescrain/e86893a1d6f85328174d036a9b263dd0 to your computer and use it in GitHub Desktop.
Re-write of Shopify's -> Arrive's Confetti in Reanimated v2
import React, { useEffect } from "react";
import Animated, { FadeOut, withDelay, withTiming, useSharedValue, useAnimatedStyle, cancelAnimation } from "react-native-reanimated";
import PropTypes from "prop-types";
import { Dimensions, StyleSheet } from "react-native";
import FastImage from "react-native-fast-image";
import ConfettiImage from "../../../assets/img/confetti.png";
const NUM_CONFETTI = 100;
const COLORS = ["#31ECBC", "#E0DAFE", "#6345F9"];
const CONFETTI_SIZE = 16;
const { width: screenWidth, height: screenHeight } = Dimensions.get("window");
const styles = StyleSheet.create({
confettiContainer: {
position: "absolute",
top: 0,
left: 0,
},
confetti: {
width: CONFETTI_SIZE,
height: CONFETTI_SIZE,
},
});
const ConfettiPiece = ({ x, y, xVel, angle, delay, yVel, angleVel, color, elasticity }) => {
const clock = useSharedValue(0);
const duration = useSharedValue(getDuration());
const localX = useSharedValue(x);
const localY = useSharedValue(y);
const localXVel = useSharedValue(xVel);
const localAngle = useSharedValue(angle);
const timeDiff = useSharedValue(0);
const dt = useSharedValue(0);
const dy = useSharedValue(0);
const dx = useSharedValue(0);
const dAngle = useSharedValue(0);
function getDuration() {
// Adding an extra 100 to the screen's height to ensure it goes off the screen.
// Then using time = distance / speed for the time calc.
let a = screenHeight + 100;
return (a / yVel) * 1000;
}
useEffect(() => {
// delay is multiplied by 1000 to convert into milliseconds
clock.value = withDelay(delay * 1000, withTiming(1, { duration: duration.value }));
return () => {
cancelAnimation(clock);
};
});
const uas = useAnimatedStyle(() => {
// Because our clock.value is going from 0 to 1, it's value will let us
// get the actual number of milliseconds by taking it multiplied by the
// total duration of the animation.
timeDiff.value = clock.value * duration.value;
dt.value = timeDiff.value / 1000;
dy.value = dt.value * yVel;
dx.value = dt.value * localXVel.value;
dAngle.value = dt.value * angleVel;
localY.value = y + dy.value;
localX.value = x + dx.value;
localAngle.value += dAngle.value;
if (localX.value > screenWidth - CONFETTI_SIZE) {
localX.value = screenWidth - CONFETTI_SIZE;
localXVel.value = localXVel.value * -1 * elasticity;
}
if (localX.value < 0) {
localX.value = 0;
localXVel.value = xVel.value * -1 * elasticity;
}
return {
transform: [{ translateX: localX.value }, { translateY: localY.value }, { rotate: localAngle.value + "deg" }, { rotateX: localAngle.value + "deg" }, { rotateY: localAngle.value + "deg" }],
};
});
return (
<Animated.View style={[styles.confettiContainer, uas]}>
<FastImage source={ConfettiImage} tintColor={color} style={styles.confetti} />
</Animated.View>
);
};
const Confetti = ({ run }) => {
const confetti = [...new Array(NUM_CONFETTI)].map((_, index) => {
// For 'x', spawn confetti from two different sources, a quarter
// from the left and a quarter from the right edge of the screen.
return {
key: index,
x: screenWidth * (index % 2 ? 0.25 : 0.75) - CONFETTI_SIZE / 2,
y: -60,
angle: 0,
xVel: Math.random() * 400 - 200,
yVel: Math.random() * 165 + 165,
angleVel: (Math.random() * 3 - 1.5) * Math.PI,
delay: Math.floor(index / 10) * 0.5,
elasticity: Math.random() * 0.3 + 0.1,
color: COLORS[index % COLORS.length],
};
});
return run ? (
<Animated.View pointerEvents="none" style={StyleSheet.absoluteFill} exiting={FadeOut.duration(500)}>
{confetti.map((e) => {
return <ConfettiPiece key={e.key} {...e} />;
})}
</Animated.View>
) : null;
};
Confetti.propTypes = {
run: PropTypes.bool,
};
export default Confetti;
@danieloi
Copy link

danieloi commented Jun 5, 2023

a link to the image would be super awesome

@imjamescrain
Copy link
Author

Hi @itsramiel and @danieloi - sorry for the delay here, I missed these comments. I've added the images. Best of luck!

@danieloi
Copy link

danieloi commented Jun 5, 2023

awesomenesss. Thanks man @imcrainjames

@fukemy
Copy link

fukemy commented Aug 2, 2023

Hi, how can i detect when confetti finish? THanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment