| |
| document.addEventListener('DOMContentLoaded', function() { |
| const observerOptions = { |
| threshold: 0.1, |
| rootMargin: '0px 0px -50px 0px' |
| }; |
|
|
| const observer = new IntersectionObserver((entries) => { |
| entries.forEach(entry => { |
| if (entry.isIntersecting) { |
| entry.target.style.opacity = '1'; |
| entry.target.classList.add('animate-fade-in'); |
| } |
| }); |
| }, observerOptions); |
|
|
| |
| const elementsToObserve = document.querySelectorAll('section'); |
| elementsToObserve.forEach(el => { |
| observer.observe(el); |
| }); |
|
|
| |
| const animateCounters = () => { |
| const counters = document.querySelectorAll('[data-count]'); |
| |
| counters.forEach(counter => { |
| const target = parseInt(counter.getAttribute('data-count')); |
| const duration = 2000; |
| const step = target / (duration / 16); |
| let current = 0; |
|
|
| const timer = setInterval(() => { |
| current += step; |
| if (current >= target) { |
| counter.textContent = target; |
| clearInterval(timer); |
| } else { |
| counter.textContent = Math.floor(current); |
| } |
| }, 16); |
| }); |
| }; |
|
|
| |
| const statsSection = document.querySelector('.grid'); |
| if (statsSection) { |
| const statsObserver = new IntersectionObserver((entries) => { |
| entries.forEach(entry => { |
| if (entry.isIntersecting) { |
| animateCounters(); |
| statsObserver.unobserve(entry.target); |
| } |
| }); |
| }, { threshold: 0.5 }); |
| |
| statsObserver.observe(statsSection); |
| } |
| }); |
|
|
| |
| function toggleMobileMenu() { |
| const mobileMenu = document.querySelector('.mobile-menu'); |
| const overlay = document.querySelector('.overlay'); |
| const hamburger = document.querySelector('.hamburger'); |
| |
| if (mobileMenu && overlay && hamburger) { |
| mobileMenu.classList.toggle('active'); |
| overlay.classList.toggle('active'); |
| hamburger.classList.toggle('active'); |
| } |
| } |
|
|
| |
| function closeMobileMenu() { |
| const mobileMenu = document.querySelector('.mobile-menu'); |
| const overlay = document.querySelector('.overlay'); |
| const hamburger = document.querySelector('.hamburger'); |
| |
| if (mobileMenu && overlay && hamburger) { |
| mobileMenu.classList.remove('active'); |
| overlay.classList.remove('active'); |
| hamburger.classList.remove('active'); |
| } |
| } |
|
|
| |
| let selectedCategory = 'all'; |
|
|
| function showCategory(category) { |
| selectedCategory = category; |
| |
| |
| const tabButtons = document.querySelectorAll('.tab-button'); |
| tabButtons.forEach(btn => { |
| if (btn.getAttribute('onclick').includes(category)) { |
| btn.classList.add('bg-gray-900', 'text-white'); |
| btn.classList.remove('bg-gray-200', 'text-gray-700'); |
| } else { |
| btn.classList.add('bg-gray-200', 'text-gray-700'); |
| btn.classList.remove('bg-gray-900', 'text-white'); |
| } |
| }); |
| |
| |
| const tabContents = document.querySelectorAll('.tab-content'); |
| tabContents.forEach(content => { |
| content.classList.remove('active'); |
| }); |
| |
| const activeContent = document.querySelector(`.tab-content[data-category="${category}"]`); |
| if (activeContent) { |
| setTimeout(() => { |
| activeContent.classList.add('active'); |
| }, 100); |
| } |
| } |
|
|
| |
| function openProject(title) { |
| const modal = document.createElement('div'); |
| modal.className = 'fixed inset-0 z-50 flex items-center justify-center p-4 modal-backdrop'; |
| modal.style.backgroundColor = 'rgba(0, 0, 0, 0.8)'; |
| |
| modal.innerHTML = ` |
| <div class="bg-white rounded-lg max-w-4xl w-full max-h-[90vh] overflow-y-auto relative transform scale-95 transition-transform duration-300"> |
| <button onclick="closeProject()" class="absolute top-4 right-4 z-10 w-10 h-10 bg-white rounded-full flex items-center justify-center hover:bg-gray-100 transition-colors"> |
| <i data-feather="x" class="w-6 h-6"></i> |
| </button> |
| <img src="http://static.photos/640x360/42" alt="${title}" class="w-full h-auto"> |
| <div class="p-8"> |
| <h2 class="text-3xl font-light mb-4">${title}</h2> |
| <p class="text-gray-600 mb-6">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.</p> |
| <div class="flex flex-wrap gap-2 mb-6"> |
| <span class="px-3 py-1 bg-gray-200 text-gray-700 rounded-full text-sm">Web Design</span> |
| <span class="px-3 py-1 bg-gray-200 text-gray-700 rounded-full text-sm">Branding</span> |
| <span class="px-3 py-1 bg-gray-200 text-gray-700 rounded-full text-sm">UI/UX</span> |
| </div> |
| <a href="contact.html" class="inline-flex items-center gap-2 px-6 py-3 bg-gray-900 text-white rounded-lg hover:bg-gray-800 transition-all"> |
| Start a Similar Project |
| <i data-feather="arrow-right" class="w-4 h-4"></i> |
| </a> |
| </div> |
| </div> |
| `; |
| |
| document.body.appendChild(modal); |
| document.body.style.overflow = 'hidden'; |
| |
| |
| setTimeout(() => { |
| modal.firstElementChild.style.transform = 'scale(1)'; |
| }, 10); |
| |
| |
| if (window.feather) { |
| feather.replace(); |
| } |
| } |
|
|
| function closeProject() { |
| const modal = document.querySelector('.modal-backdrop'); |
| if (modal) { |
| modal.firstElementChild.style.transform = 'scale(0.95)'; |
| setTimeout(() => { |
| modal.remove(); |
| document.body.style.overflow = ''; |
| }, 300); |
| } |
| } |
|
|
| |
| document.addEventListener('click', function(e) { |
| if (e.target.classList.contains('modal-backdrop')) { |
| closeProject(); |
| } |
| }); |
|
|
| |
| function submitContactForm(e) { |
| e.preventDefault(); |
| const form = e.target; |
| const formData = new FormData(form); |
| const submitBtn = form.querySelector('button[type="submit"]'); |
| |
| |
| const originalBtnText = submitBtn.innerHTML; |
| submitBtn.innerHTML = '<span class="loading-dots">Sending</span>'; |
| submitBtn.disabled = true; |
| |
| |
| setTimeout(() => { |
| |
| const successMessage = document.createElement('div'); |
| successMessage.className = 'mb-6 p-4 bg-green-100 text-green-700 rounded-lg'; |
| successMessage.innerHTML = ` |
| <div class="flex items-center gap-3"> |
| <i data-feather="check-circle" class="w-6 h-6"></i> |
| <span>Thank you for your message! We'll get back to you soon.</span> |
| </div> |
| `; |
| |
| form.parentNode.insertBefore(successMessage, form); |
| form.reset(); |
| |
| |
| submitBtn.innerHTML = originalBtnText; |
| submitBtn.disabled = false; |
| |
| |
| if (window.feather) { |
| feather.replace(); |
| } |
| |
| |
| setTimeout(() => { |
| successMessage.remove(); |
| }, 5000); |
| }, 2000); |
| } |
|
|
| |
| function scrollToTop() { |
| window.scrollTo({ |
| top: 0, |
| behavior: 'smooth' |
| }); |
| } |
|
|
| |
| window.addEventListener('scroll', () => { |
| const scrolled = window.pageYOffset; |
| const parallax = document.querySelector('.parallax-bg'); |
| |
| if (parallax && window.innerWidth > 768) { |
| const yPos = -(scrolled * 0.5); |
| parallax.style.transform = `translateY(${yPos}px)`; |
| } |
| }); |
|
|
| |
| document.addEventListener('DOMContentLoaded', () => { |
| const projectCards = document.querySelectorAll('.project-card'); |
| |
| projectCards.forEach(card => { |
| card.addEventListener('mouseenter', () => { |
| card.style.transform = 'translateY(-8px)'; |
| }); |
| |
| card.addEventListener('mouseleave', () => { |
| card.style.transform = 'translateY(0)'; |
| }); |
| }); |
| }); |
|
|
| |
| function lazyLoadImages() { |
| const images = document.querySelectorAll('img[loading="lazy"]'); |
| |
| if ('IntersectionObserver' in window) { |
| const imageObserver = new IntersectionObserver((entries, observer) => { |
| entries.forEach(entry => { |
| if (entry.isIntersecting) { |
| const img = entry.target; |
| img.src = img.dataset.src || img.src; |
| img.classList.remove('opacity-0'); |
| img.classList.add('opacity-100'); |
| observer.unobserve(img); |
| } |
| }); |
| }); |
| |
| images.forEach(img => imageObserver.observe(img)); |
| } |
| } |
|
|
| |
| lazyLoadImages(); |
|
|
| |
| document.addEventListener('keydown', (e) => { |
| if (e.key === 'Escape') { |
| closeProject(); |
| closeMobileMenu(); |
| } |
| }); |