NRVM

Back to Discover
BackgroundsAdvanced

Interactive Constellation

Floating nodes that connect to your cursor.

Live Preview

Interactive Constellation

"use client";
import { useRef, useEffect } from "react";
import { motion, useMotionValue, useSpring, useTransform, useMotionTemplate } from "framer-motion";

export function RealityDistortionCard() {
  const cardRef = useRef<HTMLDivElement>(null);
  
  const mouseX = useMotionValue(0);
  const mouseY = useMotionValue(0);
  
  const smoothX = useSpring(mouseX, { stiffness: 300, damping: 30 });
  const smoothY = useSpring(mouseY, { stiffness: 300, damping: 30 });

  const rotateX = useTransform(smoothY, [0, 400], [5, -5]); 
  const rotateY = useTransform(smoothX, [0, 320], [-5, 5]);

  const bgX = useTransform(smoothX, [0, 320], [15, -15]);
  const bgY = useTransform(smoothY, [0, 400], [15, -15]);

  const spotlight = useMotionTemplate`radial-gradient(300px circle at ${smoothX}px ${smoothY}px, rgba(255,255,255,0.15), transparent 40%)`;

  useEffect(() => {
    if (cardRef.current) {
      const rect = cardRef.current.getBoundingClientRect();
      mouseX.set(rect.width / 2);
      mouseY.set(rect.height / 2);
    }
  }, [mouseX, mouseY]);

  return (
    <div 
      className="flex items-center justify-center w-full h-[600px] bg-cover bg-fixed bg-center rounded-xl border border-border relative overflow-hidden"
      style={{ backgroundImage: "url('https://images.unsplash.com/photo-1550684848-fac1c5b4e853?q=80&w=2070&auto=format&fit=crop')" }}
    >
      <svg className="hidden">
        <filter id="distortionFilter">
          <feTurbulence type="fractalNoise" baseFrequency="0.015" numOctaves="3" result="noise">
            <animate attributeName="baseFrequency" values="0.015;0.02;0.015" dur="6s" repeatCount="indefinite" />
          </feTurbulence>
          <feDisplacementMap in="SourceGraphic" in2="noise" scale="25" xChannelSelector="R" yChannelSelector="G" />
        </filter>
      </svg>

      <motion.div
        ref={cardRef}
        onMouseMove={(e) => {
          if (!cardRef.current) return;
          const rect = cardRef.current.getBoundingClientRect();
          mouseX.set(e.clientX - rect.left);
          mouseY.set(e.clientY - rect.top);
        }}
        onMouseLeave={() => {
          if (!cardRef.current) return;
          const rect = cardRef.current.getBoundingClientRect();
          mouseX.set(rect.width / 2);
          mouseY.set(rect.height / 2);
        }}
        style={{
          rotateX,
          rotateY,
          transformStyle: "preserve-3d",
          transformPerspective: 1000,
        }}
        className="w-80 h-[400px] rounded-2xl relative flex flex-col justify-end p-6 overflow-hidden group cursor-crosshair transition-shadow duration-500 hover:shadow-2xl hover:shadow-white/5"
      >
        {/* Layer 2: Refraction Background */}
        <motion.div 
          className="absolute inset-[-20%] w-[140%] h-[140%] bg-cover bg-fixed bg-center z-0 opacity-0 group-hover:opacity-100 transition-opacity duration-700 pointer-events-none"
          style={{
            backgroundImage: "url('https://images.unsplash.com/photo-1550684848-fac1c5b4e853?q=80&w=2070&auto=format&fit=crop')",
            x: bgX,
            y: bgY,
            filter: "url(#distortionFilter)",
          }}
        />

        {/* Layer 3: Glass Frosting */}
        <div 
          className="absolute inset-0 z-0 bg-white/[0.02] backdrop-blur-[12px]"
        />

        {/* Layer 4: Interactive Spotlight Lens */}
        <motion.div 
          className="absolute inset-0 z-0 opacity-0 group-hover:opacity-100 transition-opacity duration-500 pointer-events-none mix-blend-overlay"
          style={{ background: spotlight }}
        />

        {/* Layer 5: Apple-style Highlights & Borders */}
        <div 
          className="absolute inset-0 z-0 rounded-2xl pointer-events-none"
          style={{
            border: "1px solid rgba(255, 255, 255, 0.1)",
            boxShadow: "inset 0 1px 1px rgba(255, 255, 255, 0.15), 0 4px 24px rgba(0, 0, 0, 0.4)",
          }}
        />

        {/* Content */}
        <div className="relative z-10 pointer-events-none drop-shadow-md transform-gpu" style={{ transform: "translateZ(30px)" }}>
          <h3 className="text-2xl font-bold text-white mb-2 tracking-tight">Reality Distortion</h3>
          <p className="text-white/70 text-sm">Move your cursor to witness true physical refraction.</p>
        </div>
      </motion.div>
    </div>
  );
}