UNPKG

expo-confetti

Version:

Animated confetti component for React Native (Expo). Compatible with iOS, Android. Written in JavaScript.

122 lines (105 loc) 3.21 kB
import React, { useState } from "react"; import { View, StyleSheet, Dimensions } from "react-native"; import { Canvas } from "@shopify/react-native-skia"; import { Group, RoundedRect, runTiming, Skia, useComputedValue, useValue, vec } from "@shopify/react-native-skia"; import { processTransform3d, toMatrix3 } from "react-native-redash"; const NUM_OF_CONFETTI = 70; const relativeSin = (yPosition, offsetId) => { const rand = Math.sin((yPosition - 500) * (Math.PI / 540)); const otherrand = Math.cos((yPosition - 500) * (Math.PI / 540)); return offsetId % 2 === 0 ? rand : -otherrand; }; export const startAnimation = (setConfettiPieces, colors = ["#deb7ff", "#c785ec", "#a86add", "#8549a7", "#634087"]) => { const { width, height } = Dimensions.get("window"); const pieces = []; for (let i = 0; i < NUM_OF_CONFETTI; i++) { const startingXOffset = Math.random() * width; const startingYOffset = -Math.random() * (height * 3); const id = i + Math.random() + ""; pieces.push({ offsetId: id, startingXOffset, startingYOffset, colorCode: i % colors.length, }); } setConfettiPieces(pieces); }; const ConfettiLibrary = ({ confettiPieces, setConfettiPieces, colors = ["#deb7ff", "#c785ec", "#a86add", "#8549a7", "#634087"] }) => { return ( <View style={styles.container}> <Canvas style={styles.canvas}> {confettiPieces.map((offset) => ( <ConfettiPiece key={offset.offsetId} colors={colors} {...offset} /> ))} </Canvas> </View> ); }; const styles = StyleSheet.create({ container: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, }, canvas: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, }, }); export default ConfettiLibrary; const ConfettiPiece = ({ colors, startingXOffset, startingYOffset, offsetId, colorCode, }) => { const { width, height } = Dimensions.get("window"); const WIDTH = 10; const HEIGHT = 30; const seed = Math.random() * 4; const centerY = useValue(0); const yPosition = useValue(startingYOffset); const origin = useComputedValue(() => { centerY.current = yPosition.current + HEIGHT / 2; const centerX = startingXOffset + WIDTH / 2; return vec(centerX, centerY.current); }, [yPosition]); runTiming(yPosition, height * 3, { duration: 3500, }); const matrix = useComputedValue(() => { const rotateZ = relativeSin(yPosition.current, Math.round(Number(offsetId))) * seed * 2.5; const rotateY = relativeSin(yPosition.current, Math.round(Number(offsetId))) * seed * 1.5; const rotateX = relativeSin(yPosition.current, Math.round(Number(offsetId))) * seed * 1.5; const mat3 = toMatrix3( processTransform3d([ { rotateY: rotateY }, { rotateX: rotateX }, { rotateZ: rotateZ }, ]) ); return Skia.Matrix(mat3); }, [yPosition]); return ( <Group matrix={matrix} origin={origin}> <RoundedRect r={8} x={startingXOffset} y={yPosition} height={WIDTH} width={HEIGHT} color={colors[colorCode]} /> </Group> ); };