feat(03-03): render forest elements from IndexedDB in grid
- renderForestScene() loads elements from IDB and renders as images - Object URLs tracked and revoked on re-render to prevent memory leaks - Random +-10-20px offset for natural element placement - New element animation class applied via newElementId parameter - Integrated into initForestScreen alongside renderLevelMap
This commit is contained in:
+51
-1
@@ -1,6 +1,55 @@
|
||||
import type { Progress, LevelStatus } from "../types";
|
||||
import { levels } from "../game/levels";
|
||||
import { getProgress } from "../storage/db";
|
||||
import { getProgress, getForestElements } from "../storage/db";
|
||||
|
||||
let activeObjectUrls: string[] = [];
|
||||
|
||||
export async function renderForestScene(
|
||||
db: IDBDatabase,
|
||||
newElementId?: number,
|
||||
): Promise<void> {
|
||||
const grid = document.getElementById("forest-grid");
|
||||
if (!grid) return;
|
||||
|
||||
// Revoke old object URLs to prevent memory leaks
|
||||
for (const url of activeObjectUrls) {
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
activeObjectUrls = [];
|
||||
|
||||
const elements = await getForestElements(db);
|
||||
grid.innerHTML = "";
|
||||
|
||||
// Render up to 12 elements (4x3 grid)
|
||||
const maxSlots = 12;
|
||||
const toRender = elements.slice(0, maxSlots);
|
||||
|
||||
for (const element of toRender) {
|
||||
const slot = document.createElement("div");
|
||||
slot.className = "forest__element";
|
||||
|
||||
const img = document.createElement("img");
|
||||
const objectUrl = URL.createObjectURL(element.imageBlob);
|
||||
activeObjectUrls.push(objectUrl);
|
||||
img.src = objectUrl;
|
||||
img.alt = element.description;
|
||||
|
||||
// Random offset +-10-20px for natural placement
|
||||
const offsetX =
|
||||
(Math.random() > 0.5 ? 1 : -1) * (10 + Math.random() * 10);
|
||||
const offsetY =
|
||||
(Math.random() > 0.5 ? 1 : -1) * (10 + Math.random() * 10);
|
||||
img.style.transform = `translate(${Math.round(offsetX)}px, ${Math.round(offsetY)}px)`;
|
||||
|
||||
// Animate newly added element
|
||||
if (element.id === newElementId) {
|
||||
slot.classList.add("forest__element--new");
|
||||
}
|
||||
|
||||
slot.appendChild(img);
|
||||
grid.appendChild(slot);
|
||||
}
|
||||
}
|
||||
|
||||
export function getLevelStatus(
|
||||
levelNum: number,
|
||||
@@ -62,6 +111,7 @@ export async function initForestScreen(
|
||||
if (!progress) return;
|
||||
|
||||
renderLevelMap(mapContainer, progress, onStartLesson);
|
||||
await renderForestScene(db);
|
||||
|
||||
const allComplete = levels.every((l) =>
|
||||
progress.completedLevels.includes(l.level),
|
||||
|
||||
Reference in New Issue
Block a user