Current directory: /home/klas4s23/domains/585455.klas4s23.mid-ica.nl/public_html/Gastenboek/uploads
// Typewriter effect for hero title
document.addEventListener('DOMContentLoaded', function () {
var typeTarget = document.getElementById('typewriter');
if (!typeTarget) return;
var text = 'Arne Meevis — Software Developer';
var i = 0;
function typeWriter() {
if (i <= text.length) {
typeTarget.textContent = text.substring(0, i);
i++;
setTimeout(typeWriter, 80);
}
}
typeWriter();
});
// (Hippy theme code removed)
// Animate About section on scroll or load
document.addEventListener('DOMContentLoaded', function () {
function animateAbout() {
var aboutImg = document.querySelector('.about-image');
var aboutHighlights = document.querySelector('.about-highlights');
if (aboutImg && aboutHighlights) {
var rect = aboutImg.getBoundingClientRect();
var inView = rect.top < window.innerHeight - 80;
if (inView) {
aboutImg.classList.add('visible');
aboutHighlights.classList.add('visible');
window.removeEventListener('scroll', animateAbout);
}
}
}
window.addEventListener('scroll', animateAbout);
animateAbout();
});
// Portfolio Interactive Features
document.addEventListener('DOMContentLoaded', function() {
// Theme toggle functionality
const themeToggle = document.getElementById('themeToggle');
const sunIcon = document.getElementById('sunIcon');
const moonIcon = document.getElementById('moonIcon');
const html = document.documentElement;
// Load saved theme from localStorage
const savedTheme = localStorage.getItem('theme') || 'dark';
html.setAttribute('data-theme', savedTheme);
// Ensure navbar appearance matches saved theme on load
window.dispatchEvent(new Event('scroll'));
// Update icon visibility based on current theme
function updateThemeIcon() {
const currentTheme = html.getAttribute('data-theme');
if (currentTheme === 'light') {
sunIcon.style.display = 'none';
moonIcon.style.display = 'block';
} else {
sunIcon.style.display = 'block';
moonIcon.style.display = 'none';
}
}
// Initialize icon state
updateThemeIcon();
// Toggle theme on button click
if (themeToggle) {
themeToggle.addEventListener('click', function() {
const currentTheme = html.getAttribute('data-theme');
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
html.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
updateThemeIcon();
// Update navbar inline styles to match the newly selected theme
window.dispatchEvent(new Event('scroll'));
});
}
// Mobile menu toggle
const mobileToggle = document.querySelector('.mobile-toggle');
const navMenu = document.querySelector('.nav-menu');
if (mobileToggle) {
mobileToggle.addEventListener('click', function() {
navMenu.classList.toggle('active');
});
}
// Smooth scrolling for navigation links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
const offset = 80; // Height of fixed navbar
const targetPosition = target.offsetTop - offset;
window.scrollTo({
top: targetPosition,
behavior: 'smooth'
});
// Close mobile menu if open
if (navMenu && navMenu.classList.contains('active')) {
navMenu.classList.remove('active');
}
}
});
});
// Navbar background on scroll
const navbar = document.querySelector('.navbar');
window.addEventListener('scroll', function() {
const currentTheme = html.getAttribute('data-theme') || 'dark';
if (window.scrollY > 50) {
if (currentTheme === 'light') {
navbar.style.background = 'rgba(255, 255, 255, 0.98)';
navbar.style.boxShadow = '0 2px 10px rgba(0, 0, 0, 0.1)';
} else {
navbar.style.background = 'rgba(10, 14, 39, 0.98)';
navbar.style.boxShadow = '0 2px 10px rgba(0, 0, 0, 0.3)';
}
} else {
navbar.style.background = '';
navbar.style.boxShadow = 'none';
}
});
// Animate skill bars on scroll
const skillBars = document.querySelectorAll('.skill-progress');
let skillsAnimated = false;
const animateSkills = () => {
if (skillsAnimated) return;
const skillsSection = document.getElementById('skills');
if (!skillsSection) return;
const sectionPosition = skillsSection.getBoundingClientRect().top;
const screenPosition = window.innerHeight / 1.3;
if (sectionPosition < screenPosition) {
skillsAnimated = true;
skillBars.forEach((bar, index) => {
const width = bar.getAttribute('data-width') || bar.style.width;
bar.setAttribute('data-width', width);
bar.style.width = '0%';
setTimeout(() => {
bar.style.width = width;
}, 100 + (index * 50));
});
}
};
window.addEventListener('scroll', animateSkills);
animateSkills(); // Run once on load
// Contact form submission
const contactForm = document.getElementById('contactForm');
const formMessage = document.getElementById('formMessage');
if (contactForm) {
contactForm.addEventListener('submit', async function(e) {
e.preventDefault();
const formData = new FormData(contactForm);
const submitButton = contactForm.querySelector('button[type="submit"]');
const originalButtonText = submitButton.textContent;
// Disable submit button
submitButton.disabled = true;
submitButton.textContent = 'Sending...';
try {
const response = await fetch('/contact-submit.php', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success) {
formMessage.textContent = result.message;
formMessage.className = 'success';
formMessage.style.display = 'block';
contactForm.reset();
} else {
formMessage.textContent = result.message;
formMessage.className = 'error';
formMessage.style.display = 'block';
}
} catch (error) {
formMessage.textContent = 'An error occurred. Please try again.';
formMessage.className = 'error';
formMessage.style.display = 'block';
} finally {
submitButton.disabled = false;
submitButton.textContent = originalButtonText;
// Hide message after 5 seconds
setTimeout(() => {
formMessage.style.display = 'none';
}, 5000);
}
});
}
// Fade in animations for project cards
const projectCards = document.querySelectorAll('.project-card');
const fadeInOnScroll = () => {
projectCards.forEach((card, index) => {
const cardPosition = card.getBoundingClientRect().top;
const screenPosition = window.innerHeight / 1.2;
if (cardPosition < screenPosition) {
setTimeout(() => {
card.style.opacity = '1';
card.style.transform = 'translateY(0)';
}, index * 100);
}
});
};
// Set initial state for project cards
projectCards.forEach(card => {
card.style.opacity = '0';
card.style.transform = 'translateY(20px)';
card.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
});
window.addEventListener('scroll', fadeInOnScroll);
fadeInOnScroll(); // Run once on load
// Active navigation highlight
const sections = document.querySelectorAll('section[id]');
const navLinks = document.querySelectorAll('.nav-menu a[href^="#"]');
const highlightNavigation = () => {
const scrollY = window.pageYOffset;
sections.forEach(section => {
const sectionHeight = section.offsetHeight;
const sectionTop = section.offsetTop - 100;
const sectionId = section.getAttribute('id');
if (scrollY > sectionTop && scrollY <= sectionTop + sectionHeight) {
navLinks.forEach(link => {
link.style.color = 'var(--text-secondary)';
if (link.getAttribute('href') === `#${sectionId}`) {
link.style.color = 'var(--primary-color)';
}
});
}
});
};
window.addEventListener('scroll', highlightNavigation);
// Form validation styling
const forms = document.querySelectorAll('form');
forms.forEach(form => {
const inputs = form.querySelectorAll('input, textarea, select');
inputs.forEach(input => {
input.addEventListener('invalid', function(e) {
e.preventDefault();
input.style.borderColor = 'var(--danger-color)';
});
input.addEventListener('input', function() {
if (input.validity.valid) {
input.style.borderColor = 'var(--border-color)';
}
});
});
});
// Image lazy loading fallback
const images = document.querySelectorAll('img[data-src]');
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.removeAttribute('data-src');
observer.unobserve(img);
}
});
});
images.forEach(img => imageObserver.observe(img));
// Add loading animation to buttons
const buttons = document.querySelectorAll('.btn');
buttons.forEach(button => {
button.addEventListener('click', function(e) {
if (this.type === 'submit' && !this.disabled) {
this.classList.add('loading');
}
});
});
// Console message
console.log('%c👋 Hello! Welcome to my portfolio.', 'color: #3b82f6; font-size: 16px; font-weight: bold;');
console.log('%cInterested in the code? Check out the source on GitHub!', 'color: #a1a1aa; font-size: 14px;');
});
// Add parallax effect to hero section
window.addEventListener('scroll', function() {
const hero = document.querySelector('.hero');
if (hero) {
const scrolled = window.pageYOffset;
hero.style.transform = `translateY(${scrolled * 0.5}px)`;
}
});
// Typing effect for hero title (optional enhancement)
function typeWriter(element, text, speed = 100) {
let i = 0;
element.textContent = '';
function type() {
if (i < text.length) {
element.textContent += text.charAt(i);
i++;
setTimeout(type, speed);
}
}
type();
}
// Uncomment to enable typing effect
// const heroTitle = document.querySelector('.hero-title .highlight');
// if (heroTitle) {
// const originalText = heroTitle.textContent;
// typeWriter(heroTitle, originalText, 100);
// }
// Carousel JS
let currentProject = 1; // Start with the middle card
function updateCarousel() {
const cards = document.querySelectorAll('.carousel-card');
cards.forEach((card, idx) => {
card.classList.remove('active', 'left', 'right');
card.style.display = 'block';
if (idx === currentProject) {
card.classList.add('active');
} else if (idx === currentProject - 1) {
card.classList.add('left');
} else if (idx === currentProject + 1) {
card.classList.add('right');
} else {
card.style.display = 'none';
}
});
}
function moveCarousel(dir) {
const cards = document.querySelectorAll('.carousel-card');
currentProject += dir;
if(currentProject < 0) currentProject = 0;
if(currentProject > cards.length-1) currentProject = cards.length-1;
updateCarousel();
}
document.addEventListener('DOMContentLoaded', function() {
updateCarousel();
const leftBtn = document.getElementById('carouselLeft');
const rightBtn = document.getElementById('carouselRight');
if(leftBtn) leftBtn.onclick = () => moveCarousel(-1);
if(rightBtn) rightBtn.onclick = () => moveCarousel(1);
});