| import { useEffect, useRef, useState } from "react"; | |
| import { motion } from "framer-motion"; | |
| const TrueFocus = ({ | |
| sentence = "Luna OCR", | |
| manualMode = false, | |
| blurAmount = 0, | |
| borderColor = "#8b5cf6", | |
| glowColor = "rgba(139, 92, 246, 0.6)", | |
| animationDuration = 0.8, | |
| pauseBetweenAnimations = 1.5, | |
| }) => { | |
| const words = sentence.split(" "); | |
| const [currentIndex, setCurrentIndex] = useState(0); | |
| const [lastActiveIndex, setLastActiveIndex] = useState(null); | |
| const containerRef = useRef(null); | |
| const wordRefs = useRef([]); | |
| const [focusRect, setFocusRect] = useState({ x: 0, y: 0, width: 0, height: 0 }); | |
| useEffect(() => { | |
| if (!manualMode) { | |
| const interval = setInterval(() => { | |
| setCurrentIndex((prev) => (prev + 1) % words.length); | |
| }, (animationDuration + pauseBetweenAnimations) * 1000); | |
| return () => clearInterval(interval); | |
| } | |
| }, [manualMode, animationDuration, pauseBetweenAnimations, words.length]); | |
| useEffect(() => { | |
| if (currentIndex === null || currentIndex === -1) return; | |
| if (!wordRefs.current[currentIndex] || !containerRef.current) return; | |
| const parentRect = containerRef.current.getBoundingClientRect(); | |
| const activeRect = wordRefs.current[currentIndex].getBoundingClientRect(); | |
| setFocusRect({ | |
| x: activeRect.left - parentRect.left, | |
| y: activeRect.top - parentRect.top, | |
| width: activeRect.width, | |
| height: activeRect.height, | |
| }); | |
| }, [currentIndex, words.length]); | |
| const handleMouseEnter = (index) => { | |
| if (manualMode) { | |
| setLastActiveIndex(index); | |
| setCurrentIndex(index); | |
| } | |
| }; | |
| const handleMouseLeave = () => { | |
| if (manualMode) { | |
| setCurrentIndex(lastActiveIndex); | |
| } | |
| }; | |
| return ( | |
| <div className="focus-container" ref={containerRef}> | |
| {words.map((word, index) => { | |
| const isActive = index === currentIndex; | |
| return ( | |
| <span | |
| key={index} | |
| ref={(el) => (wordRefs.current[index] = el)} | |
| className={`focus-word ${manualMode ? "manual" : ""} ${isActive && !manualMode ? "active" : ""}`} | |
| style={{ | |
| filter: manualMode | |
| ? isActive | |
| ? `blur(0px)` | |
| : `blur(${blurAmount}px)` | |
| : isActive | |
| ? `blur(0px)` | |
| : `blur(${blurAmount}px)`, | |
| "--border-color": borderColor, | |
| "--glow-color": glowColor, | |
| transition: `filter ${animationDuration}s ease`, | |
| }} | |
| onMouseEnter={() => handleMouseEnter(index)} | |
| onMouseLeave={handleMouseLeave} | |
| > | |
| {word} | |
| </span> | |
| ); | |
| })} | |
| <motion.div | |
| className="focus-frame" | |
| animate={{ | |
| x: focusRect.x, | |
| y: focusRect.y, | |
| width: focusRect.width, | |
| height: focusRect.height, | |
| opacity: currentIndex >= 0 ? 1 : 0, | |
| }} | |
| transition={{ | |
| duration: animationDuration, | |
| }} | |
| style={{ | |
| "--border-color": borderColor, | |
| "--glow-color": glowColor, | |
| }} | |
| > | |
| <span className="corner top-left"></span> | |
| <span className="corner top-right"></span> | |
| <span className="corner bottom-left"></span> | |
| <span className="corner bottom-right"></span> | |
| </motion.div> | |
| </div> | |
| ); | |
| }; | |
| export default TrueFocus; |