maalouf imad
V1
47f1575 verified
Raw
History Blame Contribute Delete
19.2 kB
/* ═══════════════════════════════════════════════════════════════════════════
ML ACADEMY β€” SHARED JAVASCRIPT
Animations et interactions communes Γ  toutes les pages
═══════════════════════════════════════════════════════════════════════════ */
// ═══════════════════════════════════════════════════════════════════════════
// NAVIGATION ACTIVE STATE
// ═══════════════════════════════════════════════════════════════════════════
document.addEventListener('DOMContentLoaded', () => {
// Mark active nav link based on current page
const currentPage = window.location.pathname.split('/').pop() || 'index.html';
document.querySelectorAll('.nav-link').forEach(link => {
const href = link.getAttribute('href');
if (href === currentPage || (currentPage === '' && href === 'index.html')) {
link.classList.add('active');
} else {
link.classList.remove('active');
}
});
});
// ═══════════════════════════════════════════════════════════════════════════
// 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 - 100; // Account for fixed navbar
window.scrollTo({
top: offsetTop,
behavior: 'smooth'
});
}
});
});
// ═══════════════════════════════════════════════════════════════════════════
// SCROLL ANIMATIONS OBSERVER
// ═══════════════════════════════════════════════════════════════════════════
const scrollObserverOptions = {
root: null,
rootMargin: '0px 0px -100px 0px',
threshold: 0.1
};
const scrollObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
// Add stagger delay for child elements if needed
const staggerChildren = entry.target.querySelectorAll('.stagger-child');
staggerChildren.forEach((child, index) => {
child.style.animationDelay = `${index * 0.1}s`;
child.classList.add('animate-in');
});
}
});
}, scrollObserverOptions);
// Observe all elements with scroll-animate class
document.querySelectorAll('.scroll-animate').forEach(el => {
scrollObserver.observe(el);
});
// ═══════════════════════════════════════════════════════════════════════════
// NAVBAR SCROLL EFFECT
// ═══════════════════════════════════════════════════════════════════════════
let lastScrollY = window.scrollY;
let ticking = false;
function updateNavbar() {
const navbar = document.querySelector('.navbar');
if (window.scrollY > 50) {
navbar.style.background = 'rgba(15, 15, 26, 0.95)';
navbar.style.boxShadow = '0 4px 20px rgba(0, 0, 0, 0.3)';
} else {
navbar.style.background = 'rgba(15, 15, 26, 0.85)';
navbar.style.boxShadow = 'none';
}
ticking = false;
}
window.addEventListener('scroll', () => {
lastScrollY = window.scrollY;
if (!ticking) {
window.requestAnimationFrame(updateNavbar);
ticking = true;
}
});
// ═══════════════════════════════════════════════════════════════════════════
// PARALLAX EFFECT FOR HERO BACKGROUND
// ═══════════════════════════════════════════════════════════════════════════
const heroBg = document.querySelector('.hero-bg');
if (heroBg) {
window.addEventListener('scroll', () => {
const scrolled = window.scrollY;
heroBg.style.transform = `translateY(${scrolled * 0.3}px)`;
});
}
// ═══════════════════════════════════════════════════════════════════════════
// TYPING EFFECT FOR HERO TEXT (optional)
// ═══════════════════════════════════════════════════════════════════════════
function typeWriter(element, text, speed = 50) {
let i = 0;
element.textContent = '';
function type() {
if (i < text.length) {
element.textContent += text.charAt(i);
i++;
setTimeout(type, speed);
}
}
type();
}
// ═══════════════════════════════════════════════════════════════════════════
// COUNTER ANIMATION FOR STATS
// ═══════════════════════════════════════════════════════════════════════════
function animateCounter(element, target, duration = 2000) {
let start = 0;
const increment = target / (duration / 16);
function updateCounter() {
start += increment;
if (start < target) {
element.textContent = Math.floor(start);
requestAnimationFrame(updateCounter);
} else {
element.textContent = target;
}
}
updateCounter();
}
// Observe stat elements and animate when visible
const statObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && !entry.target.classList.contains('counted')) {
entry.target.classList.add('counted');
const target = parseInt(entry.target.dataset.target);
if (!isNaN(target)) {
animateCounter(entry.target, target);
}
}
});
}, { threshold: 0.5 });
document.querySelectorAll('[data-target]').forEach(el => {
statObserver.observe(el);
});
// ═══════════════════════════════════════════════════════════════════════════
// MAGNETIC BUTTON EFFECT
// ═══════════════════════════════════════════════════════════════════════════
document.querySelectorAll('.btn').forEach(button => {
button.addEventListener('mousemove', (e) => {
const rect = button.getBoundingClientRect();
const x = e.clientX - rect.left - rect.width / 2;
const y = e.clientY - rect.top - rect.height / 2;
button.style.transform = `translate(${x * 0.1}px, ${y * 0.1}px)`;
});
button.addEventListener('mouseleave', () => {
button.style.transform = '';
});
});
// ═══════════════════════════════════════════════════════════════════════════
// CARD HOVER 3D EFFECT
// ═══════════════════════════════════════════════════════════════════════════
document.querySelectorAll('.card, .feature-card, .module-card').forEach(card => {
card.addEventListener('mousemove', (e) => {
const rect = card.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
const centerX = rect.width / 2;
const centerY = rect.height / 2;
const rotateX = (y - centerY) / 20;
const rotateY = (centerX - x) / 20;
card.style.transform = `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) translateZ(10px)`;
});
card.addEventListener('mouseleave', () => {
card.style.transform = '';
});
});
// ═══════════════════════════════════════════════════════════════════════════
// GLITCH EFFECT FOR TEXT (optional)
// ═══════════════════════════════════════════════════════════════════════════
function glitchText(element, originalText) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let iterations = 0;
const interval = setInterval(() => {
element.textContent = originalText
.split('')
.map((char, index) => {
if (index < iterations) {
return originalText[index];
}
return chars[Math.floor(Math.random() * chars.length)];
})
.join('');
if (iterations >= originalText.length) {
clearInterval(interval);
}
iterations += 1 / 3;
}, 30);
}
// ═══════════════════════════════════════════════════════════════════════════
// PARTICLE MOUSE INTERACTION
// ═══════════════════════════════════════════════════════════════════════════
const particlesContainer = document.querySelector('.particles-container');
if (particlesContainer) {
document.addEventListener('mousemove', (e) => {
const particles = particlesContainer.querySelectorAll('.particle');
const mouseX = e.clientX / window.innerWidth;
const mouseY = e.clientY / window.innerHeight;
particles.forEach((particle, index) => {
const speed = (index + 1) * 0.5;
const x = (mouseX - 0.5) * speed * 20;
const y = (mouseY - 0.5) * speed * 20;
particle.style.transform = `translate(${x}px, ${y}px)`;
});
});
}
// ═══════════════════════════════════════════════════════════════════════════
// LOADING ANIMATION
// ═══════════════════════════════════════════════════════════════════════════
window.addEventListener('load', () => {
document.body.classList.add('loaded');
// Animate elements with delay
document.querySelectorAll('[data-delay]').forEach(el => {
const delay = parseInt(el.dataset.delay);
setTimeout(() => {
el.classList.add('animate-in');
}, delay);
});
});
// ═══════════════════════════════════════════════════════════════════════════
// FORM VALIDATION HELPERS
// ═══════════════════════════════════════════════════════════════════════════
function validateEmail(email) {
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return re.test(email);
}
function validateRequired(value) {
return value.trim().length > 0;
}
// ═══════════════════════════════════════════════════════════════════════════
// TOOLTIP SYSTEM
// ═══════════════════════════════════════════════════════════════════════════
function createTooltip(element, text) {
const tooltip = document.createElement('div');
tooltip.className = 'tooltip';
tooltip.textContent = text;
tooltip.style.cssText = `
position: absolute;
background: var(--bg-tertiary);
color: var(--text-primary);
padding: 8px 12px;
border-radius: 6px;
font-size: 0.8rem;
white-space: nowrap;
z-index: 1000;
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
`;
document.body.appendChild(tooltip);
element.addEventListener('mouseenter', () => {
const rect = element.getBoundingClientRect();
tooltip.style.left = `${rect.left + rect.width / 2 - tooltip.offsetWidth / 2}px`;
tooltip.style.top = `${rect.top - tooltip.offsetHeight - 8}px`;
tooltip.style.opacity = '1';
});
element.addEventListener('mouseleave', () => {
tooltip.style.opacity = '0';
});
}
// ═══════════════════════════════════════════════════════════════════════════
// COPY TO CLIPBOARD
// ═══════════════════════════════════════════════════════════════════════════
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
showNotification('CopiΓ© dans le presse-papier !', 'success');
} catch (err) {
showNotification('Erreur lors de la copie', 'error');
}
}
// ═══════════════════════════════════════════════════════════════════════════
// NOTIFICATION SYSTEM
// ═══════════════════════════════════════════════════════════════════════════
function showNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.className = `notification notification-${type}`;
notification.textContent = message;
notification.style.cssText = `
position: fixed;
top: 80px;
right: 20px;
padding: 12px 24px;
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: 8px;
font-size: 0.9rem;
z-index: 10000;
animation: slideInRight 0.3s ease;
`;
if (type === 'success') {
notification.style.borderColor = 'var(--success)';
notification.style.color = 'var(--success)';
} else if (type === 'error') {
notification.style.borderColor = 'var(--danger)';
notification.style.color = 'var(--danger)';
}
document.body.appendChild(notification);
setTimeout(() => {
notification.style.animation = 'slideOutRight 0.3s ease';
setTimeout(() => notification.remove(), 300);
}, 3000);
}
// ═══════════════════════════════════════════════════════════════════════════
// KEYBOARD SHORTCUTS
// ═══════════════════════════════════════════════════════════════════════════
document.addEventListener('keydown', (e) => {
// Ctrl/Cmd + K for search (if implemented)
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
e.preventDefault();
// Open search modal
}
// Escape to close modals
if (e.key === 'Escape') {
document.querySelectorAll('.modal.open').forEach(modal => {
modal.classList.remove('open');
});
}
});
// ═══════════════════════════════════════════════════════════════════════════
// PREFERS REDUCED MOTION
// ═══════════════════════════════════════════════════════════════════════════
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
if (prefersReducedMotion.matches) {
document.documentElement.style.setProperty('--transition-fast', '0s');
document.documentElement.style.setProperty('--transition-base', '0s');
document.documentElement.style.setProperty('--transition-slow', '0s');
}
// ═══════════════════════════════════════════════════════════════════════════
// CONSOLE EASTER EGG
// ═══════════════════════════════════════════════════════════════════════════
console.log('%c🧠 ML Academy', 'font-size: 24px; font-weight: bold; color: #6366f1;');
console.log('%cFormation Machine Learning β€” GE-MCI 4A', 'font-size: 14px; color: #94a3b8;');
console.log('%cBienvenue dans la console ! Curieux de voir comment Γ§a marche ?', 'font-size: 12px; color: #64748b;');