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.

Revisions

  1. imjamescrain revised this gist Jun 5, 2023. 3 changed files with 0 additions and 0 deletions.
    Binary file added confetti.png
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
    Binary file added [email protected]
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
    Binary file added [email protected]
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
  2. imjamescrain revised this gist Mar 2, 2022. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions confetti.js
    Original file line number Diff line number Diff line change
    @@ -44,6 +44,7 @@ const ConfettiPiece = ({ x, y, xVel, angle, delay, yVel, angleVel, color, elasti
    }

    useEffect(() => {
    // delay is multiplied by 1000 to convert into milliseconds
    clock.value = withDelay(delay * 1000, withTiming(1, { duration: duration.value }));
    return () => {
    cancelAnimation(clock);
  3. imjamescrain revised this gist Mar 2, 2022. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions confetti.js
    Original file line number Diff line number Diff line change
    @@ -37,6 +37,8 @@ const ConfettiPiece = ({ x, y, xVel, angle, delay, yVel, angleVel, color, elasti
    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;
    }
  4. imjamescrain created this gist Mar 2, 2022.
    117 changes: 117 additions & 0 deletions confetti.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,117 @@
    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() {
    let a = screenHeight + 100;
    return (a / yVel) * 1000;
    }

    useEffect(() => {
    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;