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:
+40
-2
@@ -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),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user