"use client"; import { useState, useRef, useEffect } from "react"; import { Switch } from "@/components/ui/switch"; import { motion, Variants } from "framer-motion"; import { Shield, EyeOff } from "lucide-react"; import { Dictionary } from "@/lib/IDict"; import { CyberButton } from "../ui/cyber-button"; import { NetrunnerMatrix } from "../graphics/NetrunnerMatrix"; import { useTheme } from "next-themes"; function CyberEye({ isClosed, vpnOn, targetX, targetY, }: { isClosed: boolean; vpnOn: boolean; targetX: number; targetY: number; }) { const eyeRef = useRef(null); const [offset, setOffset] = useState({ x: 0, y: 0 }); const [direction, setDirection] = useState("center"); // Теперь глаз только вычисляет углы на основе переданных ему координат useEffect(() => { if (isClosed || !eyeRef.current) return; const rect = eyeRef.current.getBoundingClientRect(); const eyeCenterX = rect.left + rect.width / 2; const eyeCenterY = rect.top + rect.height / 2; const dx = targetX - eyeCenterX; const dy = targetY - eyeCenterY; if (Math.abs(dx) < 30 && Math.abs(dy) < 30) { setDirection("center"); } else if (Math.abs(dx) > Math.abs(dy)) { setDirection(dx > 0 ? "right" : "left"); } else { setDirection(dy > 0 ? "down" : "up"); } const angle = Math.atan2(dy, dx); const maxRadius = 22; // Чуть усилили множитель (0.15), чтобы глаза были более "отзывчивыми" const distance = Math.min(maxRadius, Math.hypot(dx, dy) * 0.15); setOffset({ x: Math.cos(angle) * distance, y: Math.sin(angle) * distance }); }, [targetX, targetY, isClosed]); const eyeVariants: Variants = { open: { height: 72, width: 120, borderRadius: 36, borderWidth: 4, transition: { type: "spring", stiffness: 450, damping: 12 }, }, closed: { height: 6, width: 140, borderRadius: 8, borderWidth: 3, transition: { type: "spring", stiffness: 600, damping: 14 }, }, }; const borderColor = vpnOn ? "border-purple-600 dark:border-primary shadow-[0_0_15px_rgba(139,61,255,0.3)]" : "border-cyan-600 dark:border-secondary shadow-[0_0_15px_rgba(0,229,242,0.3)]"; return ( ); } function CyberWatcherEyes({ vpnOn }: { vpnOn: boolean }) { const [isBlinking, setIsBlinking] = useState(false); const [targetCoords, setTargetCoords] = useState({ x: 0, y: 0 }); const timerRef = useRef(null); const lastInteraction = useRef(0); // Логика моргания useEffect(() => { const schedule = () => { timerRef.current = setTimeout( () => { setIsBlinking(true); setTimeout(() => { setIsBlinking(false); schedule(); }, 140); }, Math.random() * 3500 + 1500, ); }; if (!vpnOn) schedule(); return () => clearTimeout(timerRef.current); }, [vpnOn]); // Логика слежения и случайного блуждания useEffect(() => { // Изначально смотрим в центр setTargetCoords({ x: window.innerWidth / 2, y: window.innerHeight / 2 }); const handleInteract = (e: MouseEvent | TouchEvent) => { lastInteraction.current = Date.now(); let clientX, clientY; if ("touches" in e && e.touches.length > 0) { clientX = e.touches[0].clientX; clientY = e.touches[0].clientY; } else { clientX = (e as MouseEvent).clientX; clientY = (e as MouseEvent).clientY; } setTargetCoords({ x: clientX, y: clientY }); }; window.addEventListener("mousemove", handleInteract); window.addEventListener("touchstart", handleInteract, { passive: true }); window.addEventListener("pointerdown", handleInteract); // Интервал для "живого" взгляда на мобилках const randomLookInterval = setInterval(() => { const isMobile = window.innerWidth <= 768 || "ontouchstart" in window; // Если на мобилке и юзер не трогал экран последние 2.5 секунды if (isMobile && Date.now() - lastInteraction.current > 2500) { // Выбираем случайную точку, но не слишком близко к краям экрана setTargetCoords({ x: window.innerWidth * 0.2 + Math.random() * (window.innerWidth * 0.6), y: window.innerHeight * 0.2 + Math.random() * (window.innerHeight * 0.6), }); } }, 2000); // Раз в 2 секунды глаза могут сменить цель return () => { window.removeEventListener("mousemove", handleInteract); window.removeEventListener("touchstart", handleInteract); window.removeEventListener("pointerdown", handleInteract); clearInterval(randomLookInterval); }; }, []); const shouldBeClosed = vpnOn || isBlinking; return (
); } export function Hero({ dict, lang, }: { dict: Dictionary["hero"]; lang: string; }) { const [isVpnOn, setIsVpnOn] = useState(false); const { resolvedTheme } = useTheme(); return (
{/* --- RUST BACKGROUND --- */}
{isVpnOn ? ( ) : ( )} {isVpnOn ? dict.badgeSecure : dict.badge}

{dict.titleStart}{" "} {dict.titleHighlight}

{dict.subtitle}

Netrunner VPN
{dict.ctaPrimary} {dict.ctaSecondary}
); }