Bubble Button
Um botão com animação de SVGs flutuantes ao clicar
Código:
'use client';
import CircularProgress from '@/components/circularProgress/circularProgress';
import { cn } from '@/lib/utils';
import { motion } from 'motion/react';
import { useState } from 'react';
interface Bubble {
id: number;
left: number;
delay: number;
initialRotation: number;
rotationDuration: number;
}
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
children: React.ReactNode;
loading: boolean;
bubbleIcon?: React.ReactNode;
svgDuration?: number;
svgDelay?: number;
}
export default function BubbleButton({
loading,
svgDelay = 0.05,
svgDuration = 2,
children,
bubbleIcon,
...props
}: ButtonProps) {
const [bubbles, setBubbles] = useState<Bubble[]>([]);
const [isCoolingDown, setIsCoolingDown] = useState(false);
const animateButton = () => {
if (isCoolingDown) return;
setIsCoolingDown(true);
const timestamp = Date.now();
const newBubbles: Bubble[] = Array.from({ length: 10 }).map((_, i) => ({
id: timestamp + i,
left: Math.random() * 100,
delay: i * svgDelay,
initialRotation: Math.random() * 360,
rotationDuration: svgDuration + Math.random() * svgDuration,
}));
setBubbles((prev) => [...prev, ...newBubbles]);
setTimeout(() => {
setBubbles((prev) => prev.slice(newBubbles.length));
}, 1200);
setTimeout(() => {
setIsCoolingDown(false);
}, 300);
};
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
props.onClick?.(e);
animateButton();
};
return (
<button
onClick={handleClick}
className={cn(
'relative flex items-center gap-1 overflow-hidden rounded border border-white px-6 py-2 font-mono tracking-wider text-white transition-all duration-100',
'disabled:brightness-75',
'[&:hover:not(:disabled):not(:active)]:-translate-y-0.5',
'[&:hover:not(:disabled):not(:active)]:shadow-[4px_4px_0_0_#fff]',
props.className
)}
disabled={loading || props.disabled}
>
{children}
{loading && <CircularProgress size={20} color='white' />}
{bubbles.map((bubble) => (
<motion.div
key={bubble.id}
initial={{
y: 15,
scale: 1,
opacity: 1,
rotate: bubble.initialRotation,
}}
animate={{
y: -150,
scale: 1.5,
opacity: 0,
rotate: bubble.initialRotation + 360,
}}
transition={{
duration: bubble.rotationDuration,
ease: 'easeOut',
delay: bubble.delay,
}}
style={{
left: `${bubble.left}%`, }}
className='pointer-events-none absolute bottom-0 flex items-center justify-center'
>
{bubbleIcon ?? <div className='h-3 w-2 rounded-full bg-white blur-[1px]' />}
</motion.div>
))}
</button>
);
}
yarn add motion