From 66ed3c505fb522e56ccf50e74da3ebae0169baf3 Mon Sep 17 00:00:00 2001 From: "gauthier.chombart" Date: Sun, 19 Jul 2026 16:53:32 +0200 Subject: [PATCH] Uniformise les boutons du footer BioCID et ajoute des confettis MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bouton 'Commencer l'exercice 1' déplacé de la slide 1 vers la bottom bar - Nouveau style .footer-btn uniforme pour les trois boutons (is-primary / is-secondary) - Effet de confettis au canvas déclenché à l'arrivée sur la slide 6 (comme le TP Nuisibles) --- exercices/Exo_BioCID.html | 104 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 4 deletions(-) diff --git a/exercices/Exo_BioCID.html b/exercices/Exo_BioCID.html index 157ec57..edb93f1 100644 --- a/exercices/Exo_BioCID.html +++ b/exercices/Exo_BioCID.html @@ -53,6 +53,26 @@ flex-wrap: wrap; justify-content: center; } +.footer-btn { + font-family: 'Syne', sans-serif; + font-size: 13px; + font-weight: 600; + padding: 11px 24px; + border-radius: 40px; + border: 1.5px solid var(--amber); + cursor: pointer; + display: inline-flex; + align-items: center; + gap: 6px; + line-height: 1; + transition: all 0.2s; +} +.footer-btn:hover { transform: translateY(-1px); } +.footer-btn:active { transform: scale(0.98); } +.footer-btn.is-primary { background: var(--amber); color: #fff; } +.footer-btn.is-primary:hover { background: var(--amber-dark); } +.footer-btn.is-secondary { background: transparent; color: var(--amber-dark); } +.footer-btn.is-secondary:hover { background: rgba(186,117,23,0.08); } .slide-counter { position: absolute; left: 20px; @@ -418,12 +438,15 @@ + + + - @@ -597,6 +619,7 @@ const totalSlides = 6; // Configuration des boutons du footer par slide const slideConfig = { + 1: { next: 2, nextLabel: "Commencer l'exercice 1 →" }, 2: { hint: 'ex1', next: 3, nextLabel: 'Exercice suivant →' }, 3: { hint: 'ex2', next: 4, nextLabel: 'Exercice suivant →' }, 4: { hint: 'ex3', next: 5, nextLabel: 'Cas de synthèse →' }, @@ -634,6 +657,7 @@ function goTo(n) { const sa = next.querySelector('.scroll-area'); if (sa) sa.scrollTop = 0; updateUI(); + if (n === 6) setTimeout(launchConfetti, 200); } function goBack() { @@ -704,6 +728,78 @@ function restartAll() { updateUI(); } +function launchConfetti() { + const canvas = document.getElementById('confettiCanvas'); + if (!canvas) return; + const ctx = canvas.getContext('2d'); + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + canvas.style.display = 'block'; + + const colors = ['#BA7517', '#D85A30', '#F5DFB8', '#633806', '#F0A855', '#E8C068', '#FF8C5A']; + const particles = Array.from({ length: 180 }, () => ({ + x: Math.random() * canvas.width, + y: -(Math.random() * canvas.height * 0.5), + w: Math.random() * 10 + 5, + h: Math.random() * 5 + 3, + color: colors[Math.floor(Math.random() * colors.length)], + rotation: Math.random() * Math.PI * 2, + rotSpeed: (Math.random() - 0.5) * 0.18, + vx: (Math.random() - 0.5) * 2.5, + vy: Math.random() * 3 + 2, + opacity: 1, + shape: Math.random() < 0.4 ? 'circle' : 'rect' + })); + + let rafId; + let start = null; + const totalDuration = 3800; + + function draw(ts) { + if (!start) start = ts; + const elapsed = ts - start; + + ctx.clearRect(0, 0, canvas.width, canvas.height); + let active = false; + + particles.forEach(p => { + p.x += p.vx; + p.y += p.vy; + p.vy += 0.04; + p.rotation += p.rotSpeed; + p.opacity = elapsed < totalDuration * 0.55 + ? 1 + : Math.max(0, 1 - (elapsed - totalDuration * 0.55) / (totalDuration * 0.45)); + + if (p.y < canvas.height + 20) active = true; + + ctx.save(); + ctx.translate(p.x, p.y); + ctx.rotate(p.rotation); + ctx.globalAlpha = p.opacity; + ctx.fillStyle = p.color; + if (p.shape === 'circle') { + ctx.beginPath(); + ctx.arc(0, 0, p.w / 2, 0, Math.PI * 2); + ctx.fill(); + } else { + ctx.fillRect(-p.w / 2, -p.h / 2, p.w, p.h); + } + ctx.restore(); + }); + + if (active && elapsed < totalDuration * 1.3) { + rafId = requestAnimationFrame(draw); + } else { + canvas.style.display = 'none'; + ctx.clearRect(0, 0, canvas.width, canvas.height); + } + } + + if (rafId) cancelAnimationFrame(rafId); + rafId = requestAnimationFrame(draw); +} + function escapeHtml(str) { if (!str) return ''; return str.replace(/&/g, '&').replace(//g, '>');