Uniformise les boutons du footer BioCID et ajoute des confettis
- 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)
This commit is contained in:
@@ -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 @@
|
||||
<div class="bottom-bar" id="bottomBar" style="display:none;">
|
||||
<div class="slide-counter" id="slideCounter"></div>
|
||||
<div class="bottom-actions">
|
||||
<button class="back-btn" id="backBtn" onclick="goBack()" style="display:none;">← Retour</button>
|
||||
<button class="hint-btn" id="footerHintBtn" onclick="showHintCurrent()" style="display:none;">💡 Voir les indications</button>
|
||||
<button class="btn btn-amber" id="footerNextBtn" onclick="goNext()" style="display:none;">Exercice suivant →</button>
|
||||
<button class="footer-btn is-secondary" id="backBtn" onclick="goBack()" style="display:none;">← Retour</button>
|
||||
<button class="footer-btn is-secondary" id="footerHintBtn" onclick="showHintCurrent()" style="display:none;">💡 Voir les indications</button>
|
||||
<button class="footer-btn is-primary" id="footerNextBtn" onclick="goNext()" style="display:none;">Exercice suivant →</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Confetti -->
|
||||
<canvas id="confettiCanvas" style="position:fixed;inset:0;pointer-events:none;z-index:150;display:none;"></canvas>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal-overlay" id="modalOverlay" onclick="closeModal(event)">
|
||||
<div class="modal" id="modal">
|
||||
@@ -472,7 +495,6 @@
|
||||
<input type="text" id="idFormateur" placeholder="Formateur (facultatif)" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-amber" onclick="goTo(2)" style="margin-top:1.5rem;">Commencer l'exercice 1 →</button>
|
||||
</div>
|
||||
|
||||
<!-- ============ SLIDE 2 — EXERCICE 1 ============ -->
|
||||
@@ -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, '<').replace(/>/g, '>');
|
||||
|
||||
Reference in New Issue
Block a user