// Wait for DOM to be fully loaded document.addEventListener('DOMContentLoaded', function() { // Register GSAP plugins and initialize animations if (typeof gsap !== 'undefined') { // Register ScrollTrigger plugin if (typeof ScrollTrigger !== 'undefined') { gsap.registerPlugin(ScrollTrigger); console.log("ScrollTrigger registered successfully"); } else { console.warn("GSAP ScrollTrigger plugin not found"); } // Initialize GSAP animations with a slight delay to ensure all elements are loaded setTimeout(initGSAPAnimations, 300); // Increased delay for better reliability } else { console.warn("GSAP not loaded"); } // Mobile Menu Toggle const menuToggle = document.querySelector('.menu-toggle'); const navMenu = document.querySelector('.nav-menu'); if (menuToggle && navMenu) { menuToggle.addEventListener('click', function() { navMenu.classList.toggle('active'); menuToggle.classList.toggle('active'); }); } // Close mobile menu when clicking on a nav link const navLinks = document.querySelectorAll('.nav-menu a'); navLinks.forEach(link => { link.addEventListener('click', () => { if (navMenu && menuToggle) { navMenu.classList.remove('active'); menuToggle.classList.remove('active'); } }); }); // Navbar scroll behavior const navbar = document.querySelector('.navbar'); if (navbar) { window.addEventListener('scroll', function() { if (window.scrollY > 50) { navbar.classList.add('sticky'); } else { navbar.classList.remove('sticky'); } }); } // Back to top button visibility const backToTopBtn = document.querySelector('.back-to-top'); if (backToTopBtn) { window.addEventListener('scroll', () => { if (window.scrollY > 300) { backToTopBtn.classList.add('show'); } else { backToTopBtn.classList.remove('show'); } }); } // Smooth scroll for all anchor links document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function(e) { e.preventDefault(); const targetId = this.getAttribute('href'); if (targetId === '#') return; const targetElement = document.querySelector(targetId); if (targetElement) { window.scrollTo({ top: targetElement.offsetTop - 70, behavior: 'smooth' }); // Update active navigation link setTimeout(updateActiveNav, 500); } }); }); // Function to handle all GSAP animations function initGSAPAnimations() { try { console.log("Initializing GSAP animations..."); // Safely animate elements with error handling function safeAnimate(selector, animationProps, customErrorMsg) { try { const elements = document.querySelectorAll(selector); if (elements && elements.length > 0) { gsap.utils.toArray(elements).forEach(element => { // Create a new scrollTrigger for each element if it exists in props const props = {...animationProps}; if (props.scrollTrigger) { props.scrollTrigger = { ...props.scrollTrigger, trigger: element, }; } gsap.from(element, props); }); return true; } else { console.warn(`No elements found for selector: ${selector}`); return false; } } catch (err) { console.warn(customErrorMsg || `Animation error for ${selector}:`, err); return false; } } // Hero section animations (should run immediately upon page load) const heroContent = document.querySelector('.hero-content'); if (heroContent) { gsap.to(heroContent, { opacity: 1, y: 0, duration: 1, ease: 'power2.out' }); console.log("Hero animation initialized"); } // Scroll down indicator animation const scrollDown = document.querySelector('.scroll-down'); if (scrollDown) { gsap.to(scrollDown, { y: 15, repeat: -1, yoyo: true, duration: 1.5, ease: 'sine.inOut' }); } // Fade up animations - improved error handling safeAnimate('.gsap-fade-up', { scrollTrigger: { start: 'top 85%', toggleActions: 'play none none none' }, y: 50, opacity: 0, duration: 0.8, stagger: 0.15, ease: 'power2.out' }, "Failed to initialize fade up animations"); // Fade right animations safeAnimate('.gsap-fade-right', { scrollTrigger: { start: 'top 85%', toggleActions: 'play none none none' }, x: -50, opacity: 0, duration: 0.8, stagger: 0.15, ease: 'power2.out' }, "Failed to initialize fade right animations"); // Fade left animations safeAnimate('.gsap-fade-left', { scrollTrigger: { start: 'top 85%', toggleActions: 'play none none none' }, x: 50, opacity: 0, duration: 0.8, stagger: 0.15, ease: 'power2.out' }, "Failed to initialize fade left animations"); // Scale in animations safeAnimate('.gsap-scale-in', { scrollTrigger: { start: 'top 85%', toggleActions: 'play none none none' }, scale: 0.8, opacity: 0, duration: 0.8, stagger: 0.15, ease: 'back.out(1.7)' }, "Failed to initialize scale in animations"); // Animate section headers and their underlines const sectionHeaders = document.querySelectorAll('.section-header'); if (sectionHeaders.length > 0) { sectionHeaders.forEach(header => { const underline = header.querySelector('.underline'); if (underline) { gsap.from(underline, { scrollTrigger: { trigger: header, start: 'top 85%', toggleActions: 'play none none none' }, width: 0, duration: 1, delay: 0.3, ease: 'power2.inOut' }); } }); console.log("Section header animations initialized"); } // Animate skill bars with better error handling const progressBars = document.querySelectorAll('.progress'); if (progressBars.length > 0) { progressBars.forEach(bar => { try { // Get the intended width from data attribute const targetWidth = bar.getAttribute('data-width') || '0%'; gsap.to(bar, { scrollTrigger: { trigger: bar, start: 'top 90%', toggleActions: 'play none none none' }, width: targetWidth, duration: 1.5, ease: 'power2.out' }); } catch (err) { console.warn("Error animating progress bar:", err); } }); console.log("Progress bar animations initialized"); } // Add a cool rotation effect for skill icons safeAnimate('.skill-icon', { scrollTrigger: { start: 'top 85%', toggleActions: 'play none none none' }, rotation: -180, scale: 0, duration: 0.8, stagger: 0.1, ease: 'back.out(1.7)' }, "Failed to initialize skill icon animations"); // Timeline animations - safely animate dots const timelineDots = document.querySelectorAll('.timeline-dot'); if (timelineDots.length > 0) { timelineDots.forEach(dot => { try { // Initial scale animation gsap.from(dot, { scrollTrigger: { trigger: dot, start: 'top 85%', toggleActions: 'play none none none' }, scale: 0, duration: 0.5, ease: 'back.out(1.7)' }); // Add a pulse animation after dot appears gsap.to(dot, { scrollTrigger: { trigger: dot, start: 'top 85%', toggleActions: 'play none none none' }, boxShadow: '0 0 0 5px rgba(0, 123, 255, 0.3)', repeat: -1, yoyo: true, duration: 1.5, delay: 0.5, ease: 'sine.inOut' }); } catch (err) { console.warn("Error animating timeline dot:", err); } }); console.log("Timeline dot animations initialized"); } // Contact section animations - Email button pulsating effect const emailButton = document.getElementById('email-button'); if (emailButton) { const buttonText = emailButton.querySelector('.button-text'); if (buttonText) { gsap.to(buttonText, { scale: 1.1, duration: 0.8, repeat: -1, yoyo: true, ease: 'power1.inOut' }); console.log("Email button animation initialized"); } } // Project hover effects const projectItems = document.querySelectorAll('.project-item'); if (projectItems.length > 0) { projectItems.forEach(project => { const projectImg = project.querySelector('.project-img img'); const projectOverlay = project.querySelector('.project-overlay'); if (projectImg && projectOverlay) { // Add hover events project.addEventListener('mouseenter', () => { gsap.to(projectImg, { scale: 1.1, duration: 0.4, ease: 'power2.out' }); gsap.to(projectOverlay, { opacity: 1, duration: 0.3, ease: 'power2.out' }); }); project.addEventListener('mouseleave', () => { gsap.to(projectImg, { scale: 1, duration: 0.4, ease: 'power2.out' }); gsap.to(projectOverlay, { opacity: 0, duration: 0.3, ease: 'power2.out' }); }); } }); console.log("Project hover animations initialized"); } // Project filtering with GSAP animations initProjectFilters(); console.log("All GSAP animations initialized successfully"); } catch (error) { console.error("Error in GSAP animations:", error); } } // Update active navigation link based on scroll position function updateActiveNav() { const sections = document.querySelectorAll('section'); const navLinks = document.querySelectorAll('.nav-menu a'); if (sections.length === 0 || navLinks.length === 0) return; // Get current scroll position let scrollPosition = window.scrollY; // Check if near bottom of page if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 150) { navLinks.forEach(link => { link.classList.remove('active'); if (link.getAttribute('href') === '#contact') { link.classList.add('active'); } }); return; } // Check each section position sections.forEach(section => { const sectionTop = section.offsetTop - 100; const sectionBottom = sectionTop + section.offsetHeight; const sectionId = section.getAttribute('id'); if (scrollPosition >= sectionTop && scrollPosition < sectionBottom) { navLinks.forEach(link => { link.classList.remove('active'); if (link.getAttribute('href') === `#${sectionId}`) { link.classList.add('active'); } }); } }); } // Listen for scroll and update nav window.addEventListener('scroll', updateActiveNav); window.addEventListener('load', updateActiveNav); // Project filtering with GSAP animations function initProjectFilters() { const filterBtns = document.querySelectorAll('.filter-btn'); const projectItems = document.querySelectorAll('.project-item'); if (filterBtns.length === 0 || projectItems.length === 0) { console.warn("Project filters or items not found"); return; } filterBtns.forEach(btn => { btn.addEventListener('click', () => { // Remove active class from all buttons filterBtns.forEach(filterBtn => { filterBtn.classList.remove('active'); }); // Add active class to clicked button btn.classList.add('active'); const filterValue = btn.getAttribute('data-filter'); projectItems.forEach(item => { if (filterValue === 'all' || item.getAttribute('data-category') === filterValue) { // Show this item gsap.to(item, { opacity: 1, scale: 1, duration: 0.4, ease: 'back.out(1.7)', onStart: function() { item.style.display = 'block'; } }); } else { // Hide this item gsap.to(item, { opacity: 0, scale: 0.8, duration: 0.3, ease: 'power2.in', onComplete: function() { item.style.display = 'none'; } }); } }); }); }); console.log("Project filters initialized"); } // Handle URL hash on page load if (window.location.hash) { setTimeout(function() { const targetElement = document.querySelector(window.location.hash); if (targetElement) { window.scrollTo({ top: targetElement.offsetTop - 70, behavior: 'smooth' }); updateActiveNav(); } }, 200); } // Initial checks updateActiveNav(); });