feat: add DotPattern component and its demo example to MagicuiPage

This commit is contained in:
javayhu 2025-04-26 10:14:40 +08:00
parent efba35272d
commit b6e36365f2
3 changed files with 176 additions and 0 deletions

View File

@ -1,5 +1,6 @@
import { AnimatedListDemo } from '@/components/magicui/example/animated-list-example';
import { BentoDemo } from '@/components/magicui/example/bento-grid-example';
import { DotPatternDemo } from '@/components/magicui/example/dot-pattern-example';
import { HeroVideoDialogDemoTopInBottomOut } from '@/components/magicui/example/hero-video-dialog-example';
import { MarqueeDemoVertical } from '@/components/magicui/example/marquee-example';
@ -15,6 +16,7 @@ export default async function MagicuiPage() {
<BentoDemo />
<MarqueeDemoVertical />
<AnimatedListDemo />
<DotPatternDemo />
</div>
);
}

View File

@ -0,0 +1,158 @@
"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>
);
}

View File

@ -0,0 +1,16 @@
"use client";
import { cn } from "@/lib/utils";
import { DotPattern } from "@/components/magicui/dot-pattern";
export function DotPatternDemo() {
return (
<div className="relative flex h-[500px] w-full flex-col items-center justify-center overflow-hidden rounded-lg border bg-background">
<DotPattern
className={cn(
"[mask-image:radial-gradient(300px_circle_at_center,white,transparent)]",
)}
/>
</div>
);
}