Dorian2B's picture
Créer un site web éducatifs sur la photosynthèse.
4fa2f14 verified
// ===== GESTIONNAIRE D'ÉTAT DE L'APPLICATION =====
class AppState {
constructor() {
this.currentPage = 'home';
this.isMenuOpen = false;
this.visitedPages = new Set(['home']);
this.init();
}
init() {
// Suivre la navigation
this.trackNavigation();
// Initialiser les écouteurs d'événements globaux
this.initGlobalEventListeners();
}
trackNavigation() {
// Suivre les pages visitées pour des statistiques potentielles
console.log(`Page actuelle: ${this.currentPage}`);
}
toggleMenu() {
this.isMenuOpen = !this.isMenuOpen;
document.dispatchEvent(new CustomEvent('menuStateChange', {
detail: { isOpen: this.isMenuOpen }
}));
return this.isMenuOpen;
}
navigateTo(page) {
this.currentPage = page;
this.visitedPages.add(page);
document.dispatchEvent(new CustomEvent('pageChange', {
detail: { page }
}));
}
initGlobalEventListeners() {
// Gérer les changements de page
document.addEventListener('pageChange', (e) => {
console.log(`Navigation vers: ${e.detail.page}`);
// Mettre à jour l'URL si nécessaire
if (window.history && window.history.pushState) {
window.history.pushState({ page: e.detail.page }, '', `#${e.detail.page}`);
}
});
// Gérer le bouton retour
window.addEventListener('popstate', (e) => {
if (e.state && e.state.page) {
this.currentPage = e.state.page;
document.dispatchEvent(new CustomEvent('pageChange', {
detail: { page: e.state.page }
}));
}
});
// Écouter les clics sur les liens internes
document.addEventListener('click', (e) => {
const link = e.target.closest('a[href^="#"]');
if (link) {
e.preventDefault();
const targetId = link.getAttribute('href').substring(1);
const targetElement = document.getElementById(targetId);
if (targetElement) {
this.smoothScrollTo(targetElement);
}
}
});
}
smoothScrollTo(element) {
const elementPosition = element.getBoundingClientRect().top;
const offsetPosition = elementPosition + window.pageYOffset - 100;
window.scrollTo({
top: offsetPosition,
behavior: 'smooth'
});
}
}
// ===== GESTIONNAIRE D'ANIMATIONS =====
class AnimationManager {
constructor() {
this.observer = null;
this.animatedElements = new Set();
this.init();
}
init() {
this.setupIntersectionObserver();
this.initScrollAnimations();
this.initHoverEffects();
}
setupIntersectionObserver() {
const options = {
root: null,
rootMargin: '0px',
threshold: 0.1
};
this.observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.animateElement(entry.target);
this.observer.unobserve(entry.target);
}
});
}, options);
// Observer les éléments à animer
document.querySelectorAll('.animate-on-scroll').forEach(el => {
this.observer.observe(el);
});
}
animateElement(element) {
if (this.animatedElements.has(element)) return;
this.animatedElements.add(element);
element.classList.add('animate-in');
// Ajouter un délai basé sur la position
const delay = Array.from(element.parentNode.children).indexOf(element) * 100;
element.style.animationDelay = `${delay}ms`;
}
initScrollAnimations() {
let lastScrollTop = 0;
const navbar = document.querySelector('custom-navbar');
window.addEventListener('scroll', () => {
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
// Gérer la navbar
if (navbar) {
if (scrollTop > 100) {
navbar.setAttribute('scrolled', 'true');
} else {
navbar.removeAttribute('scrolled');
}
}
// Animation au défilement
const scrolled = scrollTop > 50;
document.body.classList.toggle('scrolled', scrolled);
lastScrollTop = scrollTop;
}, { passive: true });
}
initHoverEffects() {
// Effets de survol pour les cartes
document.addEventListener('mouseover', (e) => {
const card = e.target.closest('.factor-card, .process-step, custom-card');
if (card && !card.classList.contains('hovering')) {
card.classList.add('hovering');
this.addHoverEffect(card);
}
});
document.addEventListener('mouseout', (e) => {
const card = e.target.closest('.factor-card, .process-step, custom-card');
if (card) {
card.classList.remove('hovering');
}
});
}
addHoverEffect(element) {
// Effet visuel subtil au survol
element.style.transition = 'all 0.3s ease';
}
}
// ===== GESTIONNAIRE DE THÈME =====
class ThemeManager {
constructor() {
this.currentTheme = 'dark';
this.themes = {
dark: {
'--color-bg-primary': '#0A1929',
'--color-bg-secondary': '#132F4C',
'--color-text-primary': '#E3F2FD'
},
light: {
'--color-bg-primary': '#F5F7FA',
'--color-bg-secondary': '#FFFFFF',
'--color-text-primary': '#0A1929'
}
};
this.init();
}
init() {
this.loadTheme();
this.initThemeToggle();
}
loadTheme() {
const savedTheme = localStorage.getItem('theme') || 'dark';
this.setTheme(savedTheme);
}
setTheme(themeName) {
if (!this.themes[themeName]) return;
this.currentTheme = themeName;
const theme = this.themes[themeName];
Object.entries(theme).forEach(([property, value]) => {
document.documentElement.style.setProperty(property, value);
});
localStorage.setItem('theme', themeName);
document.dispatchEvent(new CustomEvent('themeChange', {
detail: { theme: themeName }
}));
}
toggleTheme() {
const newTheme = this.currentTheme === 'dark' ? 'light' : 'dark';
this.setTheme(newTheme);
}
initThemeToggle() {
// Créer un bouton de bascule de thème si nécessaire
const themeToggle = document.createElement('button');
themeToggle.className = 'theme-toggle';
themeToggle.innerHTML = '<i class="fas fa-moon"></i>';
themeToggle.style.cssText = `
position: fixed;
bottom: 20px;
right: 20px;
width: 50px;
height: 50px;
border-radius: 50%;
background: var(--color-primary);
color: white;
border: none;
cursor: pointer;
z-index: 1000;
box-shadow: var(--shadow-md);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
transition: all 0.3s ease;
`;
themeToggle.addEventListener('click', () => this.toggleTheme());
document.body.appendChild(themeToggle);
}
}
// ===== GESTIONNAIRE DE PERFORMANCE =====
class PerformanceManager {
constructor() {
this.init();
}
init() {
this.optimizeImages();
this.deferNonCriticalResources();
this.monitorPerformance();
}
optimizeImages() {
// Chargement différé des images
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));
}
deferNonCriticalResources() {
// Différer le chargement des polices non critiques
const link = document.createElement('link');
link.rel = 'preload';
link.as = 'style';
link.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=Space+Grotesk:wght@300;400;500;600;700&display=swap';
link.onload = () => link.rel = 'stylesheet';
document.head.appendChild(link);
}
monitorPerformance() {
// Surveiller les métriques de performance
if ('PerformanceObserver' in window) {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(`[Performance] ${entry.name}: ${entry.duration.toFixed(2)}ms`);
}
});
observer.observe({ entryTypes: ['measure', 'paint', 'largest-contentful-paint'] });
}
}
}
// ===== INITIALISATION DE L'APPLICATION =====
class App {
constructor() {
this.state = new AppState();
this.animations = new AnimationManager();
this.theme = new ThemeManager();
this.performance = new PerformanceManager();
this.init();
}
init() {
this.initSmoothScrolling();
this.initBackToTop();
this.initPrintButton();
this.initCopyButtons();
this.initAnalytics();
}
initSmoothScrolling() {
// Scrolling fluide pour les ancres
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
}
initBackToTop() {
// Bouton retour en haut
const backToTop = document.createElement('button');
backToTop.className = 'back-to-top';
backToTop.innerHTML = '<i class="fas fa-chevron-up"></i>';
backToTop.style.cssText = `
position: fixed;
bottom: 80px;
right: 20px;
width: 50px;
height: 50px;
border-radius: 50%;
background: var(--color-primary);
color: white;
border: none;
cursor: pointer;
z-index: 999;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
box-shadow: var(--shadow-md);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
`;
backToTop.addEventListener('click', () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
document.body.appendChild(backToTop);
// Afficher/masquer le bouton
window.addEventListener('scroll', () => {
if (window.scrollY > 300) {
backToTop.style.opacity = '1';
backToTop.style.visibility = 'visible';
} else {
backToTop.style.opacity = '0';
backToTop.style.visibility = 'hidden';
}
});
}
initPrintButton() {
// Bouton d'impression
const printButton = document.createElement('button');
printButton.className = 'print-button';
printButton.innerHTML = '<i class="fas fa-print"></i> Imprimer cette page';
printButton.style.cssText = `
position: fixed;
bottom: 140px;
right: 20px;
padding: 12px 20px;
background: var(--color-secondary);
color: white;
border: none;
border-radius: 25px;
cursor: pointer;
z-index: 999;
box-shadow: var(--shadow-md);
font-family: var(--font-primary);
font-size: 0.9rem;
transition: all 0.3s ease;
`;
printButton.addEventListener('click', () => {
window.print();
});
printButton.addEventListener('mouseenter', () => {
printButton.style.transform = 'translateY(-2px)';
printButton.style.boxShadow = 'var(--shadow-lg)';
});
printButton.addEventListener('mouseleave', () => {
printButton.style.transform = 'translateY(0)';
printButton.style.boxShadow = 'var(--shadow-md)';
});
document.body.appendChild(printButton);
}
initCopyButtons() {
// Boutons de copie pour le code et équations
document.querySelectorAll('.equation-box, pre code').forEach(element => {
const copyButton = document.createElement('button');
copyButton.className = 'copy-button';
copyButton.innerHTML = '<i class="far fa-copy"></i>';
copyButton.title = 'Copier';
copyButton.style.cssText = `
position: absolute;
top: 10px;
right: 10px;
background: rgba(255, 255, 255, 0.1);
border: 1px solid var(--color-border);
color: var(--color-text-muted);
border-radius: 4px;
padding: 5px 10px;
cursor: pointer;
font-size: 0.8rem;
transition: all 0.2s ease;
`;
copyButton.addEventListener('click', async () => {
const text = element.textContent || element.innerText;
try {
await navigator.clipboard.writeText(text);
copyButton.innerHTML = '<i class="fas fa-check"></i>';
copyButton.style.color = 'var(--color-primary-light)';
setTimeout(() => {
copyButton.innerHTML = '<i class="far fa-copy"></i>';
copyButton.style.color = 'var(--color-text-muted)';
}, 2000);
} catch (err) {
console.error('Erreur de copie:', err);
}
});
element.style.position = 'relative';
element.appendChild(copyButton);
});
}
initAnalytics() {
// Analytics simple (peut être étendu)
window.addEventListener('load', () => {
console.log('Page complètement chargée');
const loadTime = window.performance.timing.domContentLoadedEventEnd - window.performance.timing.navigationStart;
console.log(`Temps de chargement: ${loadTime}ms`);
});
}
}
// ===== INITIALISATION AU CHARGEMENT =====
document.addEventListener('DOMContentLoaded', () => {
// Initialiser l'application
window.app = new App();
// Vérifier la compatibilité Web Components
if (!('customElements' in window)) {
console.error('Web Components non supportés par ce navigateur');
return;
}
// Initialiser Feather Icons
if (typeof feather !== 'undefined') {
feather.replace();
}
// Initialiser Font Awesome
if (typeof FontAwesomeKitConfig !== 'undefined') {
console.log('Font Awesome chargé');
}
// Afficher un message de bienvenue
console.log('%c🌱 Photosynthèse Éclatée 🌱', 'color: #4CAF50; font-size: 18px; font-weight: bold;');
console.log('Site éducatif sur la photosynthèse - Chargé avec succès');
});