DotGrid.tsx 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. import { motion } from 'framer-motion'
  2. interface DotGridProps {
  3. rows: number
  4. columns: number
  5. count: number
  6. }
  7. export const DotGrid = ({ rows, columns, count }: DotGridProps) => {
  8. const container = {
  9. hidden: { opacity: 1 },
  10. visible: {
  11. opacity: 1,
  12. transition: {
  13. staggerChildren: 0.05,
  14. delayChildren: 0.05,
  15. },
  16. },
  17. }
  18. const item = {
  19. hidden: { opacity: 0 },
  20. visible: {
  21. opacity: [1, 0.5, 1],
  22. transition: {
  23. repeat: Infinity,
  24. duration: 0.5,
  25. repeatDelay: 1.5,
  26. ease: 'easeInOut',
  27. },
  28. },
  29. }
  30. return (
  31. <div
  32. className="relative w-full h-full"
  33. style={{
  34. maskImage: 'linear-gradient(to bottom, black, transparent)',
  35. WebkitMaskImage: 'linear-gradient(to bottom, black, transparent)',
  36. }}
  37. >
  38. <motion.div
  39. className="grid w-full h-full justify-between items-space-between items-start"
  40. style={{
  41. gridTemplateColumns: `repeat(${columns}, 1px)`,
  42. gridTemplateRows: `repeat(${rows}, 1fr)`,
  43. rowGap: 'auto',
  44. }}
  45. variants={container}
  46. initial="hidden"
  47. animate="visible"
  48. aria-label={`Grid of ${rows * columns} dots, ${count} highlighted`}
  49. >
  50. {Array.from({ length: rows * columns }).map((_, index) => {
  51. return (
  52. <motion.div
  53. key={index}
  54. variants={item}
  55. className={`w-px h-px rounded-full bg-foreground`}
  56. />
  57. )
  58. })}
  59. </motion.div>
  60. </div>
  61. )
  62. }