/* eslint-disable react-native/no-inline-styles */
import Animated, {
clamp,
Easing,
useAnimatedStyle,
useSharedValue,
withDelay,
withSequence,
withTiming,
} from 'react-native-reanimated';
import React, { useEffect } from 'react';
import { Image, type ImageSourcePropType } from 'react-native';
const ConfettiFallItem = ({
start,
stop,
speed,
itemSize,
img,
svg,
direction,
layout,
fromCenter,
}: {
start?: number;
stop?: number;
speed?: number;
itemSize?: number;
img?: ImageSourcePropType;
svg?: any;
direction?: 'left' | 'right';
layout: { width: number; height: number };
fromCenter?: boolean;
}) => {
const renderDelay = Math.floor(Math.random() * 5000);
const [ready, setReady] = React.useState(false);
const y = useSharedValue(start ?? layout.height);
const x = useSharedValue(0);
const opacity = useSharedValue(1);
const randomBeziers = Easing.bezier(
Math.random(),
Math.random(),
Math.random(),
Math.random()
);
const randomDuration = Math.floor(Math.random() * 1000) + 1000;
const randomOpacityDuration = Math.floor(Math.random() * 2000) + 1000;
const randomX = layout.width * Math.random();
const xAxis = direction === 'left' ? -randomX : randomX;
const randomDelay = Math.floor(Math.random() * 100);
useEffect(() => {
setTimeout(() => {
setReady(true);
}, renderDelay);
}, [renderDelay]);
useEffect(() => {
if (!ready) {
return;
}
x.value = fromCenter ? xAxis : clamp(xAxis, 0, layout.width);
y.value = stop ?? 0;
opacity.value = 0;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ready, fromCenter]);
const animatedStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateY: withDelay(
randomDelay,
withTiming(y.value, {
duration: speed ?? randomDuration,
easing: randomBeziers,
})
),
},
{
translateX: withDelay(
randomDelay,
withTiming(x.value, {
duration: speed ?? randomDuration,
easing: randomBeziers,
})
),
},
{
rotate: withTiming(`${Math.random() * 360}deg`, {
duration: speed ?? randomDuration,
easing: randomBeziers,
}),
},
],
opacity: withTiming(opacity.value, {
duration: speed ?? randomOpacityDuration,
}),
};
});
if (!ready) {
return null;
}
if (svg) {
return (
{svg}
);
}
if (img) {
return (
);
}
return null;
};
const ConfettiTumbleItem = ({
start,
stop,
speed,
itemSize,
img,
svg,
direction,
layout,
fromCenter,
}: {
start?: number;
stop?: number;
speed?: number;
itemSize: number;
img?: ImageSourcePropType;
svg?: string;
direction?: 'left' | 'right';
layout: { width: number; height: number };
fromCenter?: boolean;
}) => {
const renderDelay = Math.floor(Math.random() * 5000);
const [ready, setReady] = React.useState(false);
const y = useSharedValue(start ?? layout.height);
const x = useSharedValue(0);
const opacity = useSharedValue(1);
const randomBeziers = Easing.bezier(
Math.random(),
Math.random(),
Math.random(),
Math.random()
);
const randomDuration = Math.floor(Math.random() * 1000) + 1000;
const randomOpacityDuration = Math.floor(Math.random() * 2000) + 1000;
const randomX = layout.width * Math.random();
const xAxis = direction === 'left' ? -randomX : randomX;
const randomDelay = Math.floor(Math.random() * 100);
useEffect(() => {
setTimeout(() => {
setReady(true);
}, renderDelay);
}, [renderDelay]);
useEffect(() => {
if (!ready) {
return;
}
x.value = fromCenter ? xAxis : clamp(xAxis, 0, layout.width);
y.value = stop ?? 0;
opacity.value = 0;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ready, fromCenter]);
// @ts-ignore
const animatedStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateY: withSequence(
withDelay(
randomDelay,
withTiming(y.value, {
duration:
(speed ?? randomDuration) * clamp(Math.random(), 0.4, 0.7),
easing: randomBeziers,
})
),
// @ts-ignore
withTiming(start, {
duration:
(speed ?? randomDuration) * clamp(Math.random(), 0.4, 0.7),
easing: randomBeziers,
})
),
},
{
translateX: withDelay(
randomDelay,
withTiming(x.value, {
duration: speed ?? randomDuration,
easing: randomBeziers,
})
),
},
{
rotate: withTiming(`${Math.random() * 360}deg`, {
duration: speed ?? randomDuration,
easing: randomBeziers,
}),
},
],
opacity: withSequence(
withTiming(1, {
duration:
(speed ?? randomOpacityDuration) * clamp(Math.random(), 0.4, 0.7),
}),
withTiming(0, {
duration:
(speed ?? randomOpacityDuration) * clamp(Math.random(), 0.4, 0.7),
})
),
};
});
if (!ready) {
return null;
}
if (svg) {
return (
{svg}
);
}
if (img) {
return (
);
}
return null;
};
export const ConfettiItemFall = React.memo(ConfettiFallItem);
export const ConfettiItemTumble = React.memo(ConfettiTumbleItem);