Autosave: 20260305-195720
This commit is contained in:
parent
4227c05243
commit
aae6b26656
@ -49,6 +49,29 @@ a {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.quiz-progress-wrap {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
background: #fff7ef;
|
||||
padding: 0.8rem 0.95rem;
|
||||
}
|
||||
|
||||
.quiz-progress-bar {
|
||||
width: 100%;
|
||||
height: 13px;
|
||||
border-radius: 999px;
|
||||
background: #f3dfcc;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.quiz-progress-fill {
|
||||
height: 100%;
|
||||
width: 0;
|
||||
border-radius: inherit;
|
||||
background: linear-gradient(90deg, #f59e0b, #22c55e);
|
||||
transition: width 0.35s ease;
|
||||
}
|
||||
|
||||
.question-area {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
@ -99,6 +122,11 @@ a {
|
||||
background: #f0fdf4;
|
||||
}
|
||||
|
||||
.option-item-correct-pop {
|
||||
animation: correctPop 0.9s cubic-bezier(0.2, 0.9, 0.28, 1.2);
|
||||
box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.35);
|
||||
}
|
||||
|
||||
.option-item-wrong {
|
||||
border-color: #dc2626;
|
||||
background: #fef2f2;
|
||||
@ -247,6 +275,15 @@ body.quiz-live #quiz #revealBtn {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
body.quiz-live #quiz .quiz-progress-wrap {
|
||||
margin-bottom: 1rem !important;
|
||||
padding: 0.95rem 1.1rem;
|
||||
}
|
||||
|
||||
body.quiz-live #quiz .quiz-progress-bar {
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
body.quiz-live #quiz .quiz-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@ -325,3 +362,47 @@ body.lightbox-open {
|
||||
height: 2.4rem;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.correct-spark {
|
||||
position: fixed;
|
||||
width: var(--size, 14px);
|
||||
height: var(--size, 14px);
|
||||
border-radius: 5px;
|
||||
pointer-events: none;
|
||||
z-index: 1100;
|
||||
box-shadow: 0 0 14px rgba(255, 180, 75, 0.75);
|
||||
transform: translate(-50%, -50%) scale(0.55);
|
||||
animation: sparkBurst 1.15s ease-out forwards;
|
||||
}
|
||||
|
||||
@keyframes correctPop {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.42);
|
||||
}
|
||||
35% {
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 0 0 28px rgba(34, 197, 94, 0);
|
||||
}
|
||||
65% {
|
||||
transform: scale(1.04);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 0 0 0 rgba(34, 197, 94, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes sparkBurst {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translate(-50%, -50%) scale(0.45) rotate(0deg);
|
||||
}
|
||||
18% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--dy))) scale(0.25) rotate(var(--rot));
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,14 +78,14 @@ const quizData = [
|
||||
imageAlt: "Город Витебск"
|
||||
},
|
||||
{
|
||||
question: "Белорусский народный инструмент — это…",
|
||||
question: "Как называется белорусский струнный ударный музыкальный инструмент?",
|
||||
options: ["Цимбалы", "Скрипка", "Дудка", "Сопилка"],
|
||||
answer: 0,
|
||||
fact: "Цимбалы — струнный инструмент, по ним ударяют молоточками.",
|
||||
fact: "Цимбалы — это инструмент со струнами, по которым играют маленькими молоточками.",
|
||||
facts: [
|
||||
"Цимбалы относятся к струнным ударным инструментам.",
|
||||
"Музыкант играет на цимбалах двумя маленькими молоточками.",
|
||||
"Звук цимбал часто можно услышать в белорусской народной музыке."
|
||||
"Цимбалы похожи на большую деревянную трапецию со струнами.",
|
||||
"По струнам ударяют двумя маленькими молоточками, и получается звонкий звук.",
|
||||
"Цимбалы часто звучат в белорусской народной музыке."
|
||||
],
|
||||
imagePath: "assets/pasted-20260305-190344-ec46e879.jpg",
|
||||
imageAlt: "Традиционный музыкальный инструмент"
|
||||
@ -100,7 +100,7 @@ const quizData = [
|
||||
"Замок сочетает элементы готики, ренессанса и барокко.",
|
||||
"Комплекс Мирского замка входит в список Всемирного наследия ЮНЕСКО."
|
||||
],
|
||||
imagePath: "assets/images/quiz/q08-castle.jpg",
|
||||
imagePath: "assets/pasted-20260305-193451-5d1a5abc.jpg",
|
||||
imageAlt: "Мирский замок"
|
||||
},
|
||||
{
|
||||
@ -139,7 +139,7 @@ const quizData = [
|
||||
"В городе находится Софийский собор — важный исторический памятник.",
|
||||
"Полоцк был крупным культурным и торговым центром в Средние века."
|
||||
],
|
||||
imagePath: "assets/images/quiz/q11-polotsk.jpg",
|
||||
imagePath: "assets/pasted-20260305-193618-2a0dd0c5.jpg",
|
||||
imageAlt: "Город Полоцк"
|
||||
},
|
||||
{
|
||||
@ -161,6 +161,8 @@ const startBtn = document.getElementById("startQuizBtn");
|
||||
const quizSection = document.getElementById("quiz");
|
||||
const summarySection = document.getElementById("summary");
|
||||
const progressEl = document.getElementById("questionProgress");
|
||||
const progressLabel = document.getElementById("progressLabel");
|
||||
const progressFill = document.getElementById("progressFill");
|
||||
const questionText = document.getElementById("questionText");
|
||||
const optionList = document.getElementById("optionList");
|
||||
const totalQuestions = document.getElementById("totalQuestions");
|
||||
@ -185,37 +187,65 @@ const state = {
|
||||
score: 0,
|
||||
revealed: false,
|
||||
selectedOption: null,
|
||||
locked: false
|
||||
locked: false,
|
||||
lastAnswerCorrect: false,
|
||||
optionOrders: []
|
||||
};
|
||||
|
||||
totalQuestions.textContent = quizData.length.toString();
|
||||
|
||||
function updateProgressBar() {
|
||||
const completed = state.index + (state.revealed ? 1 : 0);
|
||||
const percent = Math.max(0, Math.min(100, Math.round((completed / quizData.length) * 100)));
|
||||
progressLabel.textContent = `${completed} из ${quizData.length}`;
|
||||
progressFill.style.width = `${percent}%`;
|
||||
progressFill.parentElement?.setAttribute("aria-valuenow", String(percent));
|
||||
}
|
||||
|
||||
function shuffleArray(items) {
|
||||
const arr = items.slice();
|
||||
for (let i = arr.length - 1; i > 0; i -= 1) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[arr[i], arr[j]] = [arr[j], arr[i]];
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
function buildOptionOrders() {
|
||||
return quizData.map((item) => shuffleArray(item.options.map((_, idx) => idx)));
|
||||
}
|
||||
|
||||
function renderQuestion() {
|
||||
const current = quizData[state.index];
|
||||
const currentOrder = state.optionOrders[state.index] || current.options.map((_, idx) => idx);
|
||||
progressEl.textContent = `Вопрос ${state.index + 1} из ${quizData.length}`;
|
||||
updateProgressBar();
|
||||
questionText.textContent = current.question;
|
||||
optionList.innerHTML = "";
|
||||
optionList.classList.toggle("d-none", state.revealed);
|
||||
optionList.setAttribute("aria-hidden", state.revealed ? "true" : "false");
|
||||
|
||||
current.options.forEach((option, idx) => {
|
||||
currentOrder.forEach((originalIdx, displayIdx) => {
|
||||
const option = current.options[originalIdx];
|
||||
const li = document.createElement("li");
|
||||
li.className = "option-item";
|
||||
li.setAttribute("role", "button");
|
||||
li.setAttribute("tabindex", "0");
|
||||
li.dataset.index = String(idx);
|
||||
li.dataset.index = String(originalIdx);
|
||||
|
||||
if (idx === state.selectedOption) {
|
||||
if (originalIdx === state.selectedOption) {
|
||||
li.classList.add("option-item-selected");
|
||||
}
|
||||
|
||||
if (state.revealed) {
|
||||
if (idx === current.answer) {
|
||||
if (originalIdx === current.answer) {
|
||||
li.classList.add("option-item-correct");
|
||||
} else if (idx === state.selectedOption) {
|
||||
} else if (originalIdx === state.selectedOption) {
|
||||
li.classList.add("option-item-wrong");
|
||||
}
|
||||
}
|
||||
|
||||
li.innerHTML = `<span class="option-index">${String.fromCharCode(65 + idx)}</span><span>${option}</span>`;
|
||||
li.innerHTML = `<span class="option-index">${String.fromCharCode(65 + displayIdx)}</span><span>${option}</span>`;
|
||||
optionList.appendChild(li);
|
||||
});
|
||||
|
||||
@ -274,6 +304,8 @@ startBtn.addEventListener("click", () => {
|
||||
state.revealed = false;
|
||||
state.selectedOption = null;
|
||||
state.locked = false;
|
||||
state.lastAnswerCorrect = false;
|
||||
state.optionOrders = buildOptionOrders();
|
||||
renderQuestion();
|
||||
quizSection.scrollIntoView({ behavior: "smooth" });
|
||||
});
|
||||
@ -284,6 +316,7 @@ function goToNextQuestion() {
|
||||
state.revealed = false;
|
||||
state.selectedOption = null;
|
||||
state.locked = false;
|
||||
state.lastAnswerCorrect = false;
|
||||
renderQuestion();
|
||||
} else {
|
||||
showSummary();
|
||||
@ -297,12 +330,46 @@ restartBtn.addEventListener("click", () => {
|
||||
state.revealed = false;
|
||||
state.selectedOption = null;
|
||||
state.locked = false;
|
||||
state.lastAnswerCorrect = false;
|
||||
state.optionOrders = buildOptionOrders();
|
||||
summarySection.classList.add("d-none");
|
||||
quizSection.classList.remove("d-none");
|
||||
renderQuestion();
|
||||
quizSection.scrollIntoView({ behavior: "smooth" });
|
||||
});
|
||||
|
||||
function playCorrectClickEffect() {
|
||||
const selectedEl = optionList.querySelector(`.option-item[data-index="${state.selectedOption}"]`);
|
||||
if (!selectedEl) return;
|
||||
|
||||
selectedEl.classList.remove("option-item-correct-pop");
|
||||
void selectedEl.offsetWidth;
|
||||
selectedEl.classList.add("option-item-correct-pop");
|
||||
|
||||
const rect = selectedEl.getBoundingClientRect();
|
||||
const burstCount = 28;
|
||||
const colors = ["#f59e0b", "#f97316", "#fb7185", "#22c55e", "#facc15"];
|
||||
|
||||
for (let i = 0; i < burstCount; i += 1) {
|
||||
const spark = document.createElement("span");
|
||||
spark.className = "correct-spark";
|
||||
spark.style.left = `${rect.left + rect.width / 2}px`;
|
||||
spark.style.top = `${rect.top + rect.height / 2}px`;
|
||||
spark.style.background = colors[i % colors.length];
|
||||
|
||||
const angle = ((Math.PI * 2) / burstCount) * i + (Math.random() * 0.3 - 0.15);
|
||||
const distance = 88 + Math.random() * 148;
|
||||
const size = 12 + Math.random() * 18;
|
||||
spark.style.setProperty("--dx", `${Math.cos(angle) * distance}px`);
|
||||
spark.style.setProperty("--dy", `${Math.sin(angle) * distance}px`);
|
||||
spark.style.setProperty("--rot", `${Math.random() * 280 - 140}deg`);
|
||||
spark.style.setProperty("--size", `${size}px`);
|
||||
|
||||
document.body.appendChild(spark);
|
||||
setTimeout(() => spark.remove(), 1250);
|
||||
}
|
||||
}
|
||||
|
||||
function closeLightbox() {
|
||||
imageLightbox.classList.add("d-none");
|
||||
document.body.classList.remove("lightbox-open");
|
||||
@ -334,13 +401,17 @@ function selectOption(index) {
|
||||
if (state.revealed || state.locked) return;
|
||||
const current = quizData[state.index];
|
||||
state.selectedOption = index;
|
||||
if (state.selectedOption === current.answer) {
|
||||
state.lastAnswerCorrect = state.selectedOption === current.answer;
|
||||
|
||||
if (state.lastAnswerCorrect) {
|
||||
playCorrectClickEffect();
|
||||
state.score += 1;
|
||||
}
|
||||
state.revealed = true;
|
||||
state.locked = true;
|
||||
renderQuestion();
|
||||
renderAnswer();
|
||||
|
||||
nextBtn.classList.remove("d-none");
|
||||
}
|
||||
|
||||
|
||||
BIN
assets/pasted-20260305-193451-5d1a5abc.jpg
Normal file
BIN
assets/pasted-20260305-193451-5d1a5abc.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 212 KiB |
BIN
assets/pasted-20260305-193618-2a0dd0c5.jpg
Normal file
BIN
assets/pasted-20260305-193618-2a0dd0c5.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 337 KiB |
BIN
assets/vm-shot-2026-03-05T19-56-11-555Z.jpg
Normal file
BIN
assets/vm-shot-2026-03-05T19-56-11-555Z.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 99 KiB |
11
index.php
11
index.php
@ -85,6 +85,15 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
<span class="fs-4 fw-semibold" id="classScore">0</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="quiz-progress-wrap mb-3" aria-live="polite">
|
||||
<div class="d-flex justify-content-between align-items-center gap-2 mb-2">
|
||||
<span class="small text-muted">Пройдено</span>
|
||||
<span class="small fw-semibold" id="progressLabel">0 из 12</span>
|
||||
</div>
|
||||
<div class="quiz-progress-bar" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0" aria-label="Прогресс по вопросам">
|
||||
<div class="quiz-progress-fill" id="progressFill"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="question-area">
|
||||
<div class="question-label text-uppercase small text-muted mb-2">Вопрос</div>
|
||||
@ -140,7 +149,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
<ol class="text-muted">
|
||||
<li>Нажмите «Начать викторину» и выводите вопросы на экран.</li>
|
||||
<li>Дайте детям 20–30 секунд на обсуждение.</li>
|
||||
<li>Выберите вариант ответа, затем нажмите «Показать ответ».</li>
|
||||
<li>Выберите вариант ответа — карточка с правильным ответом откроется сразу.</li>
|
||||
<li>В конце покажите итоги и обсудите факты.</li>
|
||||
</ol>
|
||||
</article>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user