// Taxed from https://www.joshwcomeau.com/react/animated-sparkles-in-react/ import React, { CSSProperties, forwardRef, HTMLAttributes, PropsWithChildren, useState, } from "react"; import classNames from "classnames"; import random from "lodash.random"; import range from "lodash.range"; import { usePrefersReducedMotion, useRandomInterval } from "../../hooks"; import { bemHOF } from "../../utilities"; import { Box } from "../Box"; import { Svg } from "../Svg"; const cn = bemHOF("Sparkles"); const DEFAULT_COLOR = "#ffc700"; const generateSparkle = (color = DEFAULT_COLOR) => { const sparkle = { id: String(random(10000, 99999)), createdAt: Date.now(), color, size: random(10, 20), style: { top: `${random(0, 100)}%`, left: `${random(0, 100)}%`, }, }; return sparkle; }; interface SparkleProps { size: number; color: string; style: CSSProperties; } const Sparkle = ({ size, color, style }: SparkleProps) => { const path = "M16 0c0 0 0.857 8.258 4.299 11.701s11.701 4.299 11.701 4.299-8.259 0.857-11.701 4.299c-3.442 3.442-4.299 11.701-4.299 11.701s-0.857-8.259-4.299-11.701c-3.442-3.442-11.701-4.299-11.701-4.299s8.258-0.857 11.701-4.299c3.442-3.442 4.299-11.701 4.299-11.701z"; return ( ); }; export type SparklesProps = PropsWithChildren>; export const Sparkles = forwardRef( ({ children, color = DEFAULT_COLOR, className, ...rest }, ref) => { const prefersReducedMotion = usePrefersReducedMotion(); const [sparkles, setSparkles] = useState(() => { return range(3).map(() => generateSparkle(color)); }); useRandomInterval({ callback: () => { const sparkle = generateSparkle(color); const now = Date.now(); const nextSparkles = sparkles.filter((sp) => { const delta = now - sp.createdAt; return delta < 750; }); nextSparkles.push(sparkle); setSparkles(nextSparkles); }, minDelay: prefersReducedMotion ? 0 : 50, maxDelay: prefersReducedMotion ? 0 : 450, }); return ( {sparkles.map((sparkle) => ( ))} {children} ); }, );