231 lines
6.9 KiB
JavaScript
231 lines
6.9 KiB
JavaScript
// Mobile Menu Toggle
|
|
const mobileToggle = document.getElementById('mobileToggle');
|
|
const navMenu = document.getElementById('navMenu');
|
|
|
|
mobileToggle.addEventListener('click', () => {
|
|
navMenu.classList.toggle('active');
|
|
|
|
// Animate hamburger to X
|
|
const spans = mobileToggle.querySelectorAll('span');
|
|
if (navMenu.classList.contains('active')) {
|
|
spans[0].style.transform = 'rotate(45deg) translate(5px, 5px)';
|
|
spans[1].style.opacity = '0';
|
|
spans[2].style.transform = 'rotate(-45deg) translate(7px, -6px)';
|
|
} else {
|
|
spans[0].style.transform = 'none';
|
|
spans[1].style.opacity = '1';
|
|
spans[2].style.transform = 'none';
|
|
}
|
|
});
|
|
|
|
// Close mobile menu when clicking on a link
|
|
const navLinks = document.querySelectorAll('.nav-link');
|
|
navLinks.forEach(link => {
|
|
link.addEventListener('click', () => {
|
|
navMenu.classList.remove('active');
|
|
const spans = mobileToggle.querySelectorAll('span');
|
|
spans[0].style.transform = 'none';
|
|
spans[1].style.opacity = '1';
|
|
spans[2].style.transform = 'none';
|
|
});
|
|
});
|
|
|
|
// Navbar scroll effect
|
|
const nav = document.getElementById('nav');
|
|
let lastScroll = 0;
|
|
|
|
window.addEventListener('scroll', () => {
|
|
const currentScroll = window.pageYOffset;
|
|
|
|
if (currentScroll > 50) {
|
|
nav.classList.add('scrolled');
|
|
} else {
|
|
nav.classList.remove('scrolled');
|
|
}
|
|
|
|
lastScroll = currentScroll;
|
|
});
|
|
|
|
// Smooth scroll for anchor links
|
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
|
anchor.addEventListener('click', function (e) {
|
|
e.preventDefault();
|
|
const target = document.querySelector(this.getAttribute('href'));
|
|
if (target) {
|
|
const offsetTop = target.offsetTop - 80;
|
|
window.scrollTo({
|
|
top: offsetTop,
|
|
behavior: 'smooth'
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
// Animate on Scroll (AOS) implementation
|
|
function initAOS() {
|
|
const observerOptions = {
|
|
threshold: 0.1,
|
|
rootMargin: '0px 0px -50px 0px'
|
|
};
|
|
|
|
const observer = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add('aos-animate');
|
|
}
|
|
});
|
|
}, observerOptions);
|
|
|
|
// Observe all elements with data-aos attribute
|
|
document.querySelectorAll('[data-aos]').forEach(element => {
|
|
observer.observe(element);
|
|
});
|
|
}
|
|
|
|
// Initialize AOS when DOM is loaded
|
|
document.addEventListener('DOMContentLoaded', initAOS);
|
|
|
|
// Animate skill bars when they come into view
|
|
const skillObserver = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
const progressBars = entry.target.querySelectorAll('.skill-progress');
|
|
progressBars.forEach(bar => {
|
|
const width = bar.style.width;
|
|
bar.style.width = '0';
|
|
setTimeout(() => {
|
|
bar.style.width = width;
|
|
}, 100);
|
|
});
|
|
skillObserver.unobserve(entry.target);
|
|
}
|
|
});
|
|
}, { threshold: 0.5 });
|
|
|
|
const skillsSection = document.querySelector('.skills');
|
|
if (skillsSection) {
|
|
skillObserver.observe(skillsSection);
|
|
}
|
|
|
|
// Contact Form Handling
|
|
const contactForm = document.getElementById('contactForm');
|
|
|
|
contactForm.addEventListener('submit', (e) => {
|
|
e.preventDefault();
|
|
|
|
// Get form data
|
|
const formData = {
|
|
name: document.getElementById('name').value,
|
|
email: document.getElementById('email').value,
|
|
subject: document.getElementById('subject').value,
|
|
message: document.getElementById('message').value
|
|
};
|
|
|
|
// In a real application, you would send this data to a server
|
|
console.log('Form submitted:', formData);
|
|
|
|
// Show success message
|
|
showNotification('Thank you for your message! I\'ll get back to you soon.', 'success');
|
|
|
|
// Reset form
|
|
contactForm.reset();
|
|
});
|
|
|
|
// Notification function
|
|
function showNotification(message, type = 'info') {
|
|
const notification = document.createElement('div');
|
|
notification.className = `notification notification-${type}`;
|
|
notification.textContent = message;
|
|
|
|
notification.style.cssText = `
|
|
position: fixed;
|
|
top: 100px;
|
|
right: 20px;
|
|
background: ${type === 'success' ? '#10b981' : '#3b82f6'};
|
|
color: white;
|
|
padding: 1rem 1.5rem;
|
|
border-radius: 8px;
|
|
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
|
|
z-index: 10000;
|
|
animation: slideIn 0.3s ease;
|
|
`;
|
|
|
|
document.body.appendChild(notification);
|
|
|
|
setTimeout(() => {
|
|
notification.style.animation = 'slideOut 0.3s ease';
|
|
setTimeout(() => {
|
|
document.body.removeChild(notification);
|
|
}, 300);
|
|
}, 3000);
|
|
}
|
|
|
|
// Add animation keyframes
|
|
const style = document.createElement('style');
|
|
style.textContent = `
|
|
@keyframes slideIn {
|
|
from {
|
|
transform: translateX(400px);
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
transform: translateX(0);
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
@keyframes slideOut {
|
|
from {
|
|
transform: translateX(0);
|
|
opacity: 1;
|
|
}
|
|
to {
|
|
transform: translateX(400px);
|
|
opacity: 0;
|
|
}
|
|
}
|
|
`;
|
|
document.head.appendChild(style);
|
|
|
|
// Parallax effect for hero section
|
|
window.addEventListener('scroll', () => {
|
|
const scrolled = window.pageYOffset;
|
|
const hero = document.querySelector('.hero');
|
|
|
|
if (hero && scrolled < window.innerHeight) {
|
|
const parallaxElements = hero.querySelectorAll('.floating-card');
|
|
parallaxElements.forEach((element, index) => {
|
|
const speed = 0.5 + (index * 0.1);
|
|
element.style.transform = `translateY(${scrolled * speed}px)`;
|
|
});
|
|
}
|
|
});
|
|
|
|
// Add active state to navigation based on scroll position
|
|
window.addEventListener('scroll', () => {
|
|
const sections = document.querySelectorAll('section[id]');
|
|
const scrollY = window.pageYOffset;
|
|
|
|
sections.forEach(section => {
|
|
const sectionHeight = section.offsetHeight;
|
|
const sectionTop = section.offsetTop - 100;
|
|
const sectionId = section.getAttribute('id');
|
|
const navLink = document.querySelector(`.nav-link[href="#${sectionId}"]`);
|
|
|
|
if (navLink && scrollY > sectionTop && scrollY <= sectionTop + sectionHeight) {
|
|
document.querySelectorAll('.nav-link').forEach(link => {
|
|
link.style.color = '';
|
|
});
|
|
navLink.style.color = 'var(--primary-color)';
|
|
}
|
|
});
|
|
});
|
|
|
|
// Preload animation for page load
|
|
window.addEventListener('load', () => {
|
|
document.body.style.opacity = '0';
|
|
setTimeout(() => {
|
|
document.body.style.transition = 'opacity 0.5s ease';
|
|
document.body.style.opacity = '1';
|
|
}, 100);
|
|
}); |