LaserOverlay.tsx 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. 'use client';
  2. import { motion } from 'motion/react';
  3. import type { PercentageGeometry } from '@/lib/types/action';
  4. interface LaserOverlayProps {
  5. geometry: PercentageGeometry;
  6. color?: string;
  7. duration?: number;
  8. }
  9. /**
  10. * Laser pointer overlay component
  11. *
  12. * Features:
  13. * - Smoothly flies in from the nearest corner to the element center
  14. * - Elegant light dot with soft breathing glow
  15. * - Uses percentage positioning (0-100)
  16. */
  17. export function LaserOverlay({
  18. geometry,
  19. color = '#ff3b30',
  20. duration: _duration = 3000,
  21. }: LaserOverlayProps) {
  22. const { centerX, centerY } = geometry;
  23. const startPos = {
  24. x: centerX > 50 ? 105 : -5,
  25. y: centerY > 50 ? 105 : -5,
  26. };
  27. return (
  28. <motion.div
  29. key={`laser-${centerX}-${centerY}`}
  30. initial={{
  31. opacity: 0,
  32. left: `${startPos.x}%`,
  33. top: `${startPos.y}%`,
  34. }}
  35. animate={{
  36. opacity: 1,
  37. left: `${centerX}%`,
  38. top: `${centerY}%`,
  39. }}
  40. exit={{
  41. opacity: 0,
  42. left: `${startPos.x}%`,
  43. top: `${startPos.y}%`,
  44. transition: { duration: 0.25, ease: [0.4, 0, 1, 1] },
  45. }}
  46. transition={{
  47. left: { duration: 0.5, ease: [0.22, 1, 0.36, 1] },
  48. top: { duration: 0.5, ease: [0.22, 1, 0.36, 1] },
  49. opacity: { duration: 0.15 },
  50. }}
  51. className="absolute z-[101] pointer-events-none"
  52. >
  53. <div className="relative -translate-x-1/2 -translate-y-1/2">
  54. {/* Ring pulse */}
  55. <motion.div
  56. animate={{ scale: [1, 2.8], opacity: [0.6, 0] }}
  57. transition={{
  58. repeat: Infinity,
  59. duration: 1.5,
  60. ease: 'easeOut',
  61. repeatDelay: 0.3,
  62. }}
  63. className="absolute inset-0 rounded-full"
  64. style={{ border: `1.5px solid ${color}` }}
  65. />
  66. {/* Light core */}
  67. <div
  68. className="w-2.5 h-2.5 rounded-full"
  69. style={{
  70. backgroundColor: color,
  71. boxShadow: `0 0 8px 2px ${color}60`,
  72. }}
  73. />
  74. </div>
  75. </motion.div>
  76. );
  77. }