More pretty
This commit is contained in:
@@ -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
|
||||
})();
|
||||
|
Reference in New Issue
Block a user