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;
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
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 {
|
.slide-counter {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 20px;
|
left: 20px;
|
||||||
@@ -418,12 +438,15 @@
|
|||||||
<div class="bottom-bar" id="bottomBar" style="display:none;">
|
<div class="bottom-bar" id="bottomBar" style="display:none;">
|
||||||
<div class="slide-counter" id="slideCounter"></div>
|
<div class="slide-counter" id="slideCounter"></div>
|
||||||
<div class="bottom-actions">
|
<div class="bottom-actions">
|
||||||
<button class="back-btn" id="backBtn" onclick="goBack()" style="display:none;">← Retour</button>
|
<button class="footer-btn is-secondary" 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="footer-btn is-secondary" 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-primary" id="footerNextBtn" onclick="goNext()" style="display:none;">Exercice suivant →</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Confetti -->
|
||||||
|
<canvas id="confettiCanvas" style="position:fixed;inset:0;pointer-events:none;z-index:150;display:none;"></canvas>
|
||||||
|
|
||||||
<!-- Modal -->
|
<!-- Modal -->
|
||||||
<div class="modal-overlay" id="modalOverlay" onclick="closeModal(event)">
|
<div class="modal-overlay" id="modalOverlay" onclick="closeModal(event)">
|
||||||
<div class="modal" id="modal">
|
<div class="modal" id="modal">
|
||||||
@@ -472,7 +495,6 @@
|
|||||||
<input type="text" id="idFormateur" placeholder="Formateur (facultatif)" autocomplete="off">
|
<input type="text" id="idFormateur" placeholder="Formateur (facultatif)" autocomplete="off">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-amber" onclick="goTo(2)" style="margin-top:1.5rem;">Commencer l'exercice 1 →</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ============ SLIDE 2 — EXERCICE 1 ============ -->
|
<!-- ============ SLIDE 2 — EXERCICE 1 ============ -->
|
||||||
@@ -597,6 +619,7 @@ const totalSlides = 6;
|
|||||||
|
|
||||||
// Configuration des boutons du footer par slide
|
// Configuration des boutons du footer par slide
|
||||||
const slideConfig = {
|
const slideConfig = {
|
||||||
|
1: { next: 2, nextLabel: "Commencer l'exercice 1 →" },
|
||||||
2: { hint: 'ex1', next: 3, nextLabel: 'Exercice suivant →' },
|
2: { hint: 'ex1', next: 3, nextLabel: 'Exercice suivant →' },
|
||||||
3: { hint: 'ex2', next: 4, nextLabel: 'Exercice suivant →' },
|
3: { hint: 'ex2', next: 4, nextLabel: 'Exercice suivant →' },
|
||||||
4: { hint: 'ex3', next: 5, nextLabel: 'Cas de synthèse →' },
|
4: { hint: 'ex3', next: 5, nextLabel: 'Cas de synthèse →' },
|
||||||
@@ -634,6 +657,7 @@ function goTo(n) {
|
|||||||
const sa = next.querySelector('.scroll-area');
|
const sa = next.querySelector('.scroll-area');
|
||||||
if (sa) sa.scrollTop = 0;
|
if (sa) sa.scrollTop = 0;
|
||||||
updateUI();
|
updateUI();
|
||||||
|
if (n === 6) setTimeout(launchConfetti, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
function goBack() {
|
function goBack() {
|
||||||
@@ -704,6 +728,78 @@ function restartAll() {
|
|||||||
updateUI();
|
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) {
|
function escapeHtml(str) {
|
||||||
if (!str) return '';
|
if (!str) return '';
|
||||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||||
|
|||||||
Reference in New Issue
Block a user