File size: 3,520 Bytes
373c769 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
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; |