diff --git a/src/app/components/Home.jsx b/src/app/components/Home.jsx
index 9ccde9d..a32af80 100644
--- a/src/app/components/Home.jsx
+++ b/src/app/components/Home.jsx
@@ -7,6 +7,7 @@ import { EffectComposer, Bloom } from "@react-three/postprocessing";
import { motion } from "framer-motion";
import Navbar from "./Navbar";
import { ChevronRight } from "lucide-react";
+import * as THREE from "three";
const Glow = ({ className = "" }) => (
(
/>
);
-function AnimatedNeuralNode({ position, color, size = 0.08, delay = 0 }) {
+// Optimized animation progress hook
+function useAnimationProgress(duration = 2000, delay = 500) {
+ const [progress, setProgress] = useState(0);
+ const [isComplete, setIsComplete] = useState(false);
+ const rafRef = useRef();
+
+ useEffect(() => {
+ let startTime = null;
+ let animationFrameId = null;
+
+ const animate = (timestamp) => {
+ if (!startTime) startTime = timestamp;
+ const elapsed = timestamp - startTime;
+ const rawProgress = Math.min(elapsed / duration, 1);
+
+ // Smooth easing function
+ const easedProgress = 1 - Math.pow(1 - rawProgress, 3);
+ setProgress(easedProgress);
+
+ if (rawProgress < 1) {
+ animationFrameId = requestAnimationFrame(animate);
+ } else {
+ setIsComplete(true);
+ }
+ };
+
+ const startAnimation = () => {
+ startTime = null;
+ animationFrameId = requestAnimationFrame(animate);
+ };
+
+ const timeoutId = setTimeout(startAnimation, delay);
+
+ return () => {
+ clearTimeout(timeoutId);
+ if (animationFrameId) cancelAnimationFrame(animationFrameId);
+ };
+ }, [duration, delay]);
+
+ return { progress, isComplete };
+}
+
+function AnimatedNeuralNode({ position, color, size = 0.08, delay = 0, animationProgress = 1 }) {
const ref = useRef();
+ const initialPos = useMemo(() => new THREE.Vector3(0, 0, 0), []);
+ const targetPos = useMemo(() => new THREE.Vector3(...position), [position]);
useFrame((state) => {
if (ref.current) {
- const time = state.clock.elapsedTime + delay;
- ref.current.scale.x = 1 + Math.sin(time * 2) * 0.15;
- ref.current.scale.y = 1 + Math.sin(time * 2) * 0.15;
- ref.current.scale.z = 1 + Math.sin(time * 2) * 0.15;
+ // Position animation
+ ref.current.position.lerpVectors(initialPos, targetPos, animationProgress);
+
+ // Scale animation only when animation is mostly complete
+ if (animationProgress > 0.8) {
+ const time = state.clock.elapsedTime + delay;
+ const scale = 1 + Math.sin(time * 2) * 0.15;
+ ref.current.scale.setScalar(scale);
+ } else {
+ ref.current.scale.setScalar(1);
+ }
}
});
return (
-
+
new THREE.Vector3(0, 0, 0), []);
+ const targetPos = useMemo(() => new THREE.Vector3(...position), [position]);
+
const nodes = useMemo(() => {
const nodePositions = [];
- const nodeCount = 12;
+ const nodeCount = 8; // Reduced for performance
const radius = 1.2;
for (let i = 0; i < nodeCount; i++) {
@@ -66,50 +123,58 @@ function NeuralStructure({
const conns = [];
const nodeCount = nodes.length;
+ // Simplified connection logic
for (let i = 0; i < nodeCount; i++) {
- const distances = nodes.map((node, j) => ({
- index: j,
- distance: Math.hypot(
- node[0] - nodes[i][0],
- node[1] - nodes[i][1],
- node[2] - nodes[i][2]
- ),
- }));
-
- distances.sort((a, b) => a.distance - b.distance);
-
- for (let j = 1; j <= 3 && j < distances.length; j++) {
- const targetIndex = distances[j].index;
- if (i < targetIndex) {
- conns.push([i, targetIndex]);
- }
+ for (let j = i + 1; j < Math.min(i + 3, nodeCount); j++) {
+ conns.push([i, j]);
}
}
return conns;
}, [nodes]);
+ useFrame(() => {
+ if (groupRef.current) {
+ groupRef.current.position.lerpVectors(initialPos, targetPos, animationProgress);
+ }
+ });
+
return (
-
+
{nodes.map((pos, i) => (
))}
- {connections.map(([start, end], i) => (
-
- ))}
+ {connections.map(([start, end], i) => {
+ // Calculate positions based on animation progress
+ const startPos = [
+ nodes[start][0] * animationProgress,
+ nodes[start][1] * animationProgress,
+ nodes[start][2] * animationProgress
+ ];
+ const endPos = [
+ nodes[end][0] * animationProgress,
+ nodes[end][1] * animationProgress,
+ nodes[end][2] * animationProgress
+ ];
+
+ return (
+
+ );
+ })}
);
}
@@ -181,6 +246,11 @@ export default function Home() {
];
const [isMobile, setIsMobile] = useState(false);
+ const [showText, setShowText] = useState(false);
+ const [showButton, setShowButton] = useState(false);
+
+ // Use optimized animation hook
+ const { progress: animationProgress, isComplete } = useAnimationProgress(2000, 300);
useEffect(() => {
const checkMobile = () => {
@@ -189,8 +259,21 @@ export default function Home() {
checkMobile();
window.addEventListener('resize', checkMobile);
+
+ // Show text when animation is complete
+ if (isComplete) {
+ const textTimeout = setTimeout(() => {
+ setShowText(true);
+ const buttonTimeout = setTimeout(() => {
+ setShowButton(true);
+ }, 800);
+ return () => clearTimeout(buttonTimeout);
+ }, 200);
+ return () => clearTimeout(textTimeout);
+ }
+
return () => window.removeEventListener('resize', checkMobile);
- }, []);
+ }, [isComplete]);
const structures = useMemo(() => {
const positions = [
@@ -227,20 +310,21 @@ export default function Home() {
[0, -5, -4],
];
- return positions.map((position, i) => ({
+ // Reduce structures on mobile for performance
+ const filteredPositions = isMobile ? positions.slice(0, 15) : positions;
+
+ return filteredPositions.map((position, i) => ({
position,
color: colors[i % colors.length],
structureIndex: i,
}));
- }, [colors]);
+ }, [colors, isMobile]);
const handleExploreClick = () => {
- // Dispatch custom event to open navbar
if (typeof window !== "undefined") {
window.dispatchEvent(new CustomEvent("open-navigation"));
}
- // Also scroll to next section if needed
window.scrollTo({
top: window.innerHeight,
behavior: 'smooth'
@@ -256,66 +340,49 @@ export default function Home() {
- {/* Main Content Container */}
-
-
-
- MACHINE LEARNING FOR EVERYONE
-
-
-
- (ML4E)
-
+ {/* Main Content Container - Animation controlled by showText state */}
+
+ {/* Text appears only after neural networks spread */}
+ {showText && (
+
+
+ MACHINE LEARNING FOR EVERYONE
+
+
+
+ (ML4E)
+
-
- THE OFFICIAL MACHINE LEARNING CLUB OF NIT ROURKELA
-
+
+ THE OFFICIAL MACHINE LEARNING CLUB OF NIT ROURKELA
+
+
+ )}
- {/* Eye-catching Learn More Button */}
+ {/* Button appears after text animation */}
+ {showButton && (
@@ -340,32 +407,35 @@ export default function Home() {
-
-
+ )}
+
- {/* 3D Canvas with touch event handling */}
+ {/* 3D Canvas */}