Files
2026-03-31 13:10:16 +02:00

131 lines
4.6 KiB
JavaScript

/**
* Portfolio Alexandre — Scripts JS
* Filtres dynamiques, navigation mobile, lightbox
*/
document.addEventListener('DOMContentLoaded', () => {
// ─── Navigation mobile ─────────────────────────────────────────────────
const navToggle = document.getElementById('navToggle');
const navLinks = document.querySelector('.nav-links');
if (navToggle && navLinks) {
navToggle.addEventListener('click', () => {
navLinks.classList.toggle('open');
});
// Fermer le menu au clic sur un lien
navLinks.querySelectorAll('.nav-link').forEach(link => {
link.addEventListener('click', () => navLinks.classList.remove('open'));
});
}
// ─── Filtres dynamiques (page projets) ────────────────────────────────
const filterPills = document.querySelectorAll('.filter-pill:not(.pill-more)');
filterPills.forEach(pill => {
pill.addEventListener('click', () => {
const filterType = pill.dataset.filter; // 'category' ou 'tech'
const filterValue = pill.dataset.value;
// Mettre à jour l'input hidden correspondant
const inputId = filterType === 'category' ? 'categoryInput' : 'techInput';
const hiddenInput = document.getElementById(inputId);
if (hiddenInput) {
hiddenInput.value = filterValue;
}
// Mettre à jour l'apparence des pills du même groupe
const group = pill.closest('.filter-pills');
if (group) {
group.querySelectorAll('.filter-pill').forEach(p => p.classList.remove('active'));
pill.classList.add('active');
}
// Soumettre le formulaire automatiquement
const form = document.getElementById('filtersForm');
if (form) form.submit();
});
});
// ─── Smooth scroll pour ancres internes ───────────────────────────────
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', (e) => {
const targetId = anchor.getAttribute('href').slice(1);
const target = document.getElementById(targetId);
if (target) {
e.preventDefault();
const navHeight = parseInt(
getComputedStyle(document.documentElement).getPropertyValue('--nav-height')
) || 68;
const top = target.getBoundingClientRect().top + window.scrollY - navHeight - 16;
window.scrollTo({ top, behavior: 'smooth' });
}
});
});
// ─── Animation d'apparition des cartes ────────────────────────────────
const cards = document.querySelectorAll('.project-card');
if ('IntersectionObserver' in window) {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry, i) => {
if (entry.isIntersecting) {
setTimeout(() => {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}, i * 80);
observer.unobserve(entry.target);
}
});
},
{ threshold: 0.1 }
);
cards.forEach(card => {
card.style.opacity = '0';
card.style.transform = 'translateY(20px)';
card.style.transition = 'opacity 0.4s ease, transform 0.4s ease';
observer.observe(card);
});
}
// ─── Navbar : réduire au scroll ───────────────────────────────────────
const navbar = document.querySelector('.navbar');
let lastScrollY = window.scrollY;
window.addEventListener('scroll', () => {
if (window.scrollY > 80) {
navbar?.classList.add('scrolled');
} else {
navbar?.classList.remove('scrolled');
}
lastScrollY = window.scrollY;
}, { passive: true });
});
// ─── Lightbox (fonctions globales appelées depuis HTML) ──────────────────
function openLightbox(src) {
const lightbox = document.getElementById('lightbox');
const lightboxImg = document.getElementById('lightboxImg');
if (lightbox && lightboxImg) {
lightboxImg.src = src;
lightbox.classList.add('open');
document.body.style.overflow = 'hidden';
}
}
function closeLightbox() {
const lightbox = document.getElementById('lightbox');
if (lightbox) {
lightbox.classList.remove('open');
document.body.style.overflow = '';
}
}
// Fermer la lightbox avec Échap
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') closeLightbox();
});