Template
59 lines
1.5 KiB
TypeScript
59 lines
1.5 KiB
TypeScript
import React, { type ComponentPropsWithoutRef, type CSSProperties } from "react"
|
|
|
|
import { cn } from "@/lib/utils"
|
|
|
|
interface RippleProps extends ComponentPropsWithoutRef<"div"> {
|
|
mainCircleSize?: number
|
|
mainCircleOpacity?: number
|
|
numCircles?: number
|
|
}
|
|
|
|
export const Ripple = React.memo(function Ripple({
|
|
mainCircleSize = 210,
|
|
mainCircleOpacity = 0.24,
|
|
numCircles = 8,
|
|
className,
|
|
...props
|
|
}: RippleProps) {
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"pointer-events-none absolute inset-0 mask-[linear-gradient(to_bottom,white,transparent)] select-none",
|
|
className
|
|
)}
|
|
{...props}
|
|
>
|
|
{Array.from({ length: numCircles }, (_, i) => {
|
|
const size = mainCircleSize + i * 70
|
|
const opacity = mainCircleOpacity - i * 0.03
|
|
const animationDelay = `${i * 0.06}s`
|
|
const borderStyle = "solid"
|
|
|
|
return (
|
|
<div
|
|
key={i}
|
|
className={`animate-ripple bg-foreground/25 absolute rounded-full border shadow-xl`}
|
|
style={
|
|
{
|
|
"--i": i,
|
|
width: `${size}px`,
|
|
height: `${size}px`,
|
|
opacity,
|
|
animationDelay,
|
|
borderStyle,
|
|
borderWidth: "1px",
|
|
borderColor: `var(--foreground)`,
|
|
top: "50%",
|
|
left: "50%",
|
|
transform: "translate(-50%, -50%) scale(1)",
|
|
} as CSSProperties
|
|
}
|
|
/>
|
|
)
|
|
})}
|
|
</div>
|
|
)
|
|
})
|
|
|
|
Ripple.displayName = "Ripple"
|