Spaces:
Running
Running
| // ── LOADER | |
| window.addEventListener('load', () => { | |
| setTimeout(() => { | |
| document.getElementById('loader').classList.add('hidden'); | |
| }, 2000); | |
| }); | |
| // ── CUSTOM CURSOR | |
| const cursor = document.getElementById('cursor'); | |
| const follower = document.getElementById('cursor-follower'); | |
| let mouseX = 0, mouseY = 0; | |
| let followerX = 0, followerY = 0; | |
| document.addEventListener('mousemove', (e) => { | |
| mouseX = e.clientX; | |
| mouseY = e.clientY; | |
| cursor.style.transform = `translate(${mouseX - 4}px, ${mouseY - 4}px)`; | |
| }); | |
| function animateFollower() { | |
| followerX += (mouseX - followerX) * 0.12; | |
| followerY += (mouseY - followerY) * 0.12; | |
| follower.style.transform = `translate(${followerX - 18}px, ${followerY - 18}px)`; | |
| requestAnimationFrame(animateFollower); | |
| } | |
| animateFollower(); | |
| document.querySelectorAll('a, button, .portfolio-item, .filter-btn').forEach(el => { | |
| el.addEventListener('mouseenter', () => { | |
| cursor.style.transform += ' scale(2)'; | |
| follower.style.width = '60px'; | |
| follower.style.height = '60px'; | |
| follower.style.opacity = '0.5'; | |
| }); | |
| el.addEventListener('mouseleave', () => { | |
| follower.style.width = '36px'; | |
| follower.style.height = '36px'; | |
| follower.style.opacity = '1'; | |
| }); | |
| }); | |
| // ── NAV SCROLL | |
| const nav = document.getElementById('nav'); | |
| window.addEventListener('scroll', () => { | |
| nav.classList.toggle('scrolled', window.scrollY > 60); | |
| }); | |
| // ── MOBILE MENU | |
| const hamburger = document.getElementById('hamburger'); | |
| let mobileMenu = document.createElement('div'); | |
| mobileMenu.className = 'mobile-menu'; | |
| const menuLinks = ['#about|O Nas', '#portfolio|Portfolio', '#process|Proces', '#testimonials|Opinie', '#contact|Kontakt']; | |
| menuLinks.forEach(item => { | |
| const [href, label] = item.split('|'); | |
| const a = document.createElement('a'); | |
| a.href = href; | |
| a.textContent = label; | |
| a.addEventListener('click', () => mobileMenu.classList.remove('open')); | |
| mobileMenu.appendChild(a); | |
| }); | |
| document.body.appendChild(mobileMenu); | |
| hamburger.addEventListener('click', () => { | |
| mobileMenu.classList.toggle('open'); | |
| }); | |
| // ── COUNTER ANIMATION | |
| function animateCounter(el) { | |
| const target = parseInt(el.getAttribute('data-count')); | |
| const duration = 2000; | |
| const start = performance.now(); | |
| function update(time) { | |
| const elapsed = time - start; | |
| const progress = Math.min(elapsed / duration, 1); | |
| const eased = 1 - Math.pow(1 - progress, 3); | |
| el.textContent = Math.round(eased * target); | |
| if (progress < 1) requestAnimationFrame(update); | |
| } | |
| requestAnimationFrame(update); | |
| } | |
| // ── INTERSECTION OBSERVER | |
| const observerOptions = { threshold: 0.15, rootMargin: '0px 0px -60px 0px' }; | |
| const revealObserver = new IntersectionObserver((entries) => { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting) { | |
| entry.target.classList.add('visible'); | |
| revealObserver.unobserve(entry.target); | |
| } | |
| }); | |
| }, observerOptions); | |
| document.querySelectorAll('.about-grid, .portfolio-item, .process-step, .testimonial-card, .award-item, .contact-grid, .fp-content, .section-header').forEach((el, i) => { | |
| el.classList.add('reveal'); | |
| revealObserver.observe(el); | |
| }); | |
| const counterObserver = new IntersectionObserver((entries) => { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting) { | |
| animateCounter(entry.target); | |
| counterObserver.unobserve(entry.target); | |
| } | |
| }); | |
| }, { threshold: 0.5 }); | |
| document.querySelectorAll('.stat-num').forEach(el => counterObserver.observe(el)); | |
| // ── PORTFOLIO FILTER | |
| const filterBtns = document.querySelectorAll('.filter-btn'); | |
| const portfolioItems = document.querySelectorAll('.portfolio-item'); | |
| filterBtns.forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| filterBtns.forEach(b => b.classList.remove('active')); | |
| btn.classList.add('active'); | |
| const filter = btn.getAttribute('data-filter'); | |
| portfolioItems.forEach(item => { | |
| const cat = item.getAttribute('data-category'); | |
| if (filter === 'all' || cat === filter) { | |
| item.style.opacity = '1'; | |
| item.style.transform = 'scale(1)'; | |
| item.style.display = ''; | |
| } else { | |
| item.style.opacity = '0'; | |
| item.style.transform = 'scale(0.95)'; | |
| setTimeout(() => { if (filter !== 'all' && cat !== filter) item.style.display = 'none'; }, 300); | |
| } | |
| }); | |
| }); | |
| }); | |
| // ── SMOOTH SCROLL | |
| document.querySelectorAll('a[href^="#"]').forEach(a => { | |
| a.addEventListener('click', (e) => { | |
| const target = document.querySelector(a.getAttribute('href')); | |
| if (target) { | |
| e.preventDefault(); | |
| target.scrollIntoView({ behavior: 'smooth', block: 'start' }); | |
| } | |
| }); | |
| }); | |
| // ── PARALLAX HERO | |
| window.addEventListener('scroll', () => { | |
| const scrollY = window.scrollY; | |
| const heroImg = document.querySelector('.hero-img'); | |
| const orbs = document.querySelectorAll('.hero-orb'); | |
| if (heroImg) heroImg.style.transform = `scale(1.05) translateY(${scrollY * 0.12}px)`; | |
| orbs.forEach((orb, i) => { | |
| orb.style.transform = `translateY(${scrollY * (0.05 + i * 0.03)}px)`; | |
| }); | |
| }); | |
| // ── FORM SUBMIT | |
| document.getElementById('contactForm')?.addEventListener('submit', (e) => { | |
| e.preventDefault(); | |
| const btn = e.target.querySelector('.btn-primary'); | |
| const original = btn.innerHTML; | |
| btn.innerHTML = '<span>Wysłano! Odezwiemy się wkrótce ✓</span>'; | |
| btn.style.background = '#2D7A3A'; | |
| setTimeout(() => { | |
| btn.innerHTML = original; | |
| btn.style.background = ''; | |
| e.target.reset(); | |
| }, 4000); | |
| }); | |
| // ── STAGGER CHILDREN | |
| document.querySelectorAll('.process-steps .process-step').forEach((el, i) => { | |
| el.style.transitionDelay = `${i * 0.12}s`; | |
| }); | |
| document.querySelectorAll('.testimonials-grid .testimonial-card').forEach((el, i) => { | |
| el.style.transitionDelay = `${i * 0.1}s`; | |
| }); | |
| document.querySelectorAll('.portfolio-item').forEach((el, i) => { | |
| el.style.transitionDelay = `${i * 0.08}s`; | |
| }); |