More pretty

This commit is contained in:
Dmitry Bikulov
2025-09-11 23:12:39 +03:00
parent d1bb3559bb
commit ef0f55afaf
5 changed files with 108 additions and 16 deletions

View File

@@ -8,6 +8,10 @@
const statusEl = document.getElementById('status');
const resultEl = document.getElementById('result');
const fxLayer = document.getElementById('fxLayer');
const btnColorClasses = ['c-yellow','c-orange','c-pink','c-purple','c-blue','c-teal'];
const praise = ['Отлично! ⭐','Молодец! 🎉','Супер! 🌟','Здорово! 🥳','Так держать! 👍'];
// decorative assets removed
let sessionId = null;
let totalQuestions = 20;
@@ -41,9 +45,10 @@
if (!q) return;
problemEl.textContent = `${q.a} × ${q.b}`;
optionsEl.innerHTML = '';
q.options.forEach(value => {
const colors = shuffle(btnColorClasses).slice(0, q.options.length);
q.options.forEach((value, idx) => {
const btn = document.createElement('button');
btn.className = 'option-btn';
btn.className = `option-btn ${colors[idx % colors.length]}`;
btn.textContent = String(value);
btn.addEventListener('click', () => answer(value, btn));
optionsEl.appendChild(btn);
@@ -51,6 +56,15 @@
progressEl.textContent = `${q.index}/${totalQuestions}`;
}
function shuffle(arr) {
const a = arr.slice();
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
function stopTimer() {
if (countdown) {
clearInterval(countdown);
@@ -129,6 +143,43 @@
}
}
function celebrateFullscreen() {
if (!fxLayer) return;
// quick flash overlay
const flash = document.createElement('div');
flash.className = 'flash';
fxLayer.appendChild(flash);
setTimeout(() => flash.remove(), 550);
const vw = window.innerWidth;
const vh = window.innerHeight;
const count = Math.min(220, Math.floor((vw * vh) / 7000)); // scale with viewport
for (let i = 0; i < count; i++) {
const p = document.createElement('span');
p.className = 'confetti';
// random start across the screen
const sx = Math.random() * vw;
const sy = Math.random() * vh * 0.6 + vh * 0.1; // avoid extreme edges
p.style.left = `${sx}px`;
p.style.top = `${sy}px`;
// random trajectory
const angle = Math.random() * Math.PI * 2;
const distance = 180 + Math.random() * 520;
const tx = Math.cos(angle) * distance;
const ty = Math.sin(angle) * distance + 80; // slight gravity bias
const rot = (Math.random() * 1080 - 540).toFixed(0) + 'deg';
p.style.setProperty('--tx', `${tx.toFixed(0)}px`);
p.style.setProperty('--ty', `${ty.toFixed(0)}px`);
p.style.setProperty('--rot', rot);
const hue = Math.floor(Math.random() * 360);
p.style.backgroundColor = `hsl(${hue} 90% 55%)`;
const dur = 1000 + Math.random() * 900;
p.style.animation = `confetti ${dur}ms ease-out forwards`;
fxLayer.appendChild(p);
setTimeout(() => p.remove(), dur + 200);
}
}
async function answer(value, clickedBtn) {
if (!sessionId || inFlight) return;
inFlight = true;
@@ -147,7 +198,8 @@
// feedback
if (data.correct) {
clickedBtn.classList.add('correct');
celebrateFromElement(clickedBtn);
celebrateFullscreen();
setStatus(praise[Math.floor(Math.random() * praise.length)]);
} else {
clickedBtn.classList.add('wrong');
}
@@ -174,4 +226,5 @@
}
startBtn.addEventListener('click', start);
// no decor positioning
})();