feat(05-02): wire forest navigation handlers and fade animation

- Add saveProgress import to scene.ts
- Wire prevBtn/nextBtn onclick handlers in initForestScreen
- Persist currentForestPage to IndexedDB on every navigation
- Add fade-out/fade-in opacity transition on page changes
This commit is contained in:
2026-03-29 23:02:03 +02:00
parent 44b6c9053a
commit 13b8df5db0
+40 -2
View File
@@ -1,6 +1,6 @@
import type { Progress, LevelStatus } from "../types"; import type { Progress, LevelStatus } from "../types";
import { levels } from "../game/levels"; import { levels } from "../game/levels";
import { getProgress, getForestElements } from "../storage/db"; import { getProgress, getForestElements, saveProgress } from "../storage/db";
import { playForestElementSound } from "../audio/sounds"; import { playForestElementSound } from "../audio/sounds";
let activeObjectUrls: string[] = []; let activeObjectUrls: string[] = [];
@@ -21,6 +21,10 @@ export async function renderForestScene(
} }
activeObjectUrls = []; activeObjectUrls = [];
// Fade out before clearing
grid.style.opacity = "0";
grid.style.transition = "opacity 0.3s ease";
const elements = await getForestElements(db); const elements = await getForestElements(db);
grid.innerHTML = ""; grid.innerHTML = "";
@@ -56,6 +60,11 @@ export async function renderForestScene(
grid.appendChild(slot); grid.appendChild(slot);
} }
// Trigger fade-in after a frame
requestAnimationFrame(() => {
grid.style.opacity = "1";
});
// Update navigation UI // Update navigation UI
const totalPages = Math.max(1, Math.ceil(elements.length / SLOTS_PER_PAGE)); const totalPages = Math.max(1, Math.ceil(elements.length / SLOTS_PER_PAGE));
const navContainer = document.getElementById("forest-nav"); const navContainer = document.getElementById("forest-nav");
@@ -139,9 +148,38 @@ export async function initForestScreen(
if (!progress) return; if (!progress) return;
renderLevelMap(mapContainer, progress, onStartLesson); renderLevelMap(mapContainer, progress, onStartLesson);
const currentPage = progress.currentForestPage ?? 0; let currentPage = progress.currentForestPage ?? 0;
await renderForestScene(db, currentPage); await renderForestScene(db, currentPage);
// Wire navigation buttons
const prevBtn = document.getElementById("forest-nav-prev") as HTMLButtonElement;
const nextBtn = document.getElementById("forest-nav-next") as HTMLButtonElement;
const navigateToPage = async (newPage: number) => {
currentPage = newPage;
await renderForestScene(db, currentPage);
// Persist page in progress
const freshProgress = await getProgress(db);
if (freshProgress) {
freshProgress.currentForestPage = currentPage;
await saveProgress(db, freshProgress);
}
};
if (prevBtn) {
prevBtn.onclick = () => {
if (currentPage > 0) navigateToPage(currentPage - 1);
};
}
if (nextBtn) {
nextBtn.onclick = async () => {
const elements = await getForestElements(db);
const totalPages = Math.max(1, Math.ceil(elements.length / SLOTS_PER_PAGE));
if (currentPage < totalPages - 1) navigateToPage(currentPage + 1);
};
}
const allComplete = levels.every((l) => const allComplete = levels.every((l) =>
progress.completedLevels.includes(l.level), progress.completedLevels.includes(l.level),
); );