mirror of
https://github.com/codebox283/everydayserieswebsite.git
synced 2025-06-20 12:40:53 +00:00
159 lines
4.2 KiB
TypeScript
159 lines
4.2 KiB
TypeScript
"use client";
|
|
|
|
import { cn } from "@/lib/utils";
|
|
import { motion } from "motion/react";
|
|
import React, { useEffect, useId, useRef, useState } from "react";
|
|
|
|
/**
|
|
* DotPattern Component Props
|
|
*
|
|
* @param {number} [width=16] - The horizontal spacing between dots
|
|
* @param {number} [height=16] - The vertical spacing between dots
|
|
* @param {number} [x=0] - The x-offset of the entire pattern
|
|
* @param {number} [y=0] - The y-offset of the entire pattern
|
|
* @param {number} [cx=1] - The x-offset of individual dots
|
|
* @param {number} [cy=1] - The y-offset of individual dots
|
|
* @param {number} [cr=1] - The radius of each dot
|
|
* @param {string} [className] - Additional CSS classes to apply to the SVG container
|
|
* @param {boolean} [glow=false] - Whether dots should have a glowing animation effect
|
|
*/
|
|
interface DotPatternProps extends React.SVGProps<SVGSVGElement> {
|
|
width?: number;
|
|
height?: number;
|
|
x?: number;
|
|
y?: number;
|
|
cx?: number;
|
|
cy?: number;
|
|
cr?: number;
|
|
className?: string;
|
|
glow?: boolean;
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
/**
|
|
* DotPattern Component
|
|
*
|
|
* A React component that creates an animated or static dot pattern background using SVG.
|
|
* The pattern automatically adjusts to fill its container and can optionally display glowing dots.
|
|
*
|
|
* @component
|
|
*
|
|
* @see DotPatternProps for the props interface.
|
|
*
|
|
* @example
|
|
* // Basic usage
|
|
* <DotPattern />
|
|
*
|
|
* // With glowing effect and custom spacing
|
|
* <DotPattern
|
|
* width={20}
|
|
* height={20}
|
|
* glow={true}
|
|
* className="opacity-50"
|
|
* />
|
|
*
|
|
* @notes
|
|
* - The component is client-side only ("use client")
|
|
* - Automatically responds to container size changes
|
|
* - When glow is enabled, dots will animate with random delays and durations
|
|
* - Uses Motion for animations
|
|
* - Dots color can be controlled via the text color utility classes
|
|
*/
|
|
|
|
export function DotPattern({
|
|
width = 16,
|
|
height = 16,
|
|
x = 0,
|
|
y = 0,
|
|
cx = 1,
|
|
cy = 1,
|
|
cr = 1,
|
|
className,
|
|
glow = false,
|
|
...props
|
|
}: DotPatternProps) {
|
|
const id = useId();
|
|
const containerRef = useRef<SVGSVGElement>(null);
|
|
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
|
|
|
|
useEffect(() => {
|
|
const updateDimensions = () => {
|
|
if (containerRef.current) {
|
|
const { width, height } = containerRef.current.getBoundingClientRect();
|
|
setDimensions({ width, height });
|
|
}
|
|
};
|
|
|
|
updateDimensions();
|
|
window.addEventListener("resize", updateDimensions);
|
|
return () => window.removeEventListener("resize", updateDimensions);
|
|
}, []);
|
|
|
|
const dots = Array.from(
|
|
{
|
|
length:
|
|
Math.ceil(dimensions.width / width) *
|
|
Math.ceil(dimensions.height / height),
|
|
},
|
|
(_, i) => {
|
|
const col = i % Math.ceil(dimensions.width / width);
|
|
const row = Math.floor(i / Math.ceil(dimensions.width / width));
|
|
return {
|
|
x: col * width + cx,
|
|
y: row * height + cy,
|
|
delay: Math.random() * 5,
|
|
duration: Math.random() * 3 + 2,
|
|
};
|
|
},
|
|
);
|
|
|
|
return (
|
|
<svg
|
|
ref={containerRef}
|
|
aria-hidden="true"
|
|
className={cn(
|
|
"pointer-events-none absolute inset-0 h-full w-full",
|
|
className,
|
|
)}
|
|
{...props}
|
|
>
|
|
<defs>
|
|
<radialGradient id={`${id}-gradient`}>
|
|
<stop offset="0%" stopColor="currentColor" stopOpacity="1" />
|
|
<stop offset="100%" stopColor="currentColor" stopOpacity="0" />
|
|
</radialGradient>
|
|
</defs>
|
|
{dots.map((dot, index) => (
|
|
<motion.circle
|
|
key={`${dot.x}-${dot.y}`}
|
|
cx={dot.x}
|
|
cy={dot.y}
|
|
r={cr}
|
|
fill={glow ? `url(#${id}-gradient)` : "currentColor"}
|
|
className="text-neutral-400/80"
|
|
initial={glow ? { opacity: 0.4, scale: 1 } : {}}
|
|
animate={
|
|
glow
|
|
? {
|
|
opacity: [0.4, 1, 0.4],
|
|
scale: [1, 1.5, 1],
|
|
}
|
|
: {}
|
|
}
|
|
transition={
|
|
glow
|
|
? {
|
|
duration: dot.duration,
|
|
repeat: Infinity,
|
|
repeatType: "reverse",
|
|
delay: dot.delay,
|
|
ease: "easeInOut",
|
|
}
|
|
: {}
|
|
}
|
|
/>
|
|
))}
|
|
</svg>
|
|
);
|
|
}
|