waves/public/assets/js/g.js
2025-05-21 23:20:31 -05:00

98 lines
2.6 KiB
JavaScript

document.addEventListener('DOMContentLoaded', () => {
const searchInput = document.getElementById('gameSearchInput');
const grid = document.querySelector('.games-grid');
let gamesData = [];
let filteredData = [];
const BATCH_SIZE = 50;
let renderedCount = 0;
const sentinel = document.createElement('div');
sentinel.className = 'sentinel';
grid.after(sentinel);
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
renderNextBatch();
}
});
}, { rootMargin: '200px', threshold: 0.1 });
function debounce(fn, wait = 200) {
let t;
return (...args) => {
clearTimeout(t);
t = setTimeout(() => fn.apply(this, args), wait);
};
}
fetch('/assets/data/g.json')
.then(r => r.json())
.then(data => {
gamesData = data;
filteredData = data;
searchInput.placeholder = `Search through ${data.length} Games…`;
observer.observe(sentinel);
renderNextBatch();
searchInput.addEventListener('input', debounce(() => {
const q = searchInput.value.trim().toLowerCase();
filteredData = gamesData.filter(g =>
(g.name || '').toLowerCase().includes(q)
);
resetRendering();
}, 250));
})
.catch(console.error);
function resetRendering() {
grid.innerHTML = '';
renderedCount = 0;
observer.observe(sentinel);
renderNextBatch();
}
function renderNextBatch() {
const batch = filteredData.slice(renderedCount, renderedCount + BATCH_SIZE);
if (!batch.length) {
observer.unobserve(sentinel);
return;
}
const frag = document.createDocumentFragment();
batch.forEach(game => {
const card = document.createElement('div');
card.className = 'game-card';
card.innerHTML = `
<img
loading="lazy"
src="/assets/g/${game.directory}/${game.image}"
alt="${game.name} Icon"
/>
<h2>${game.name}</h2>
`;
card.addEventListener('click', () => {
window.handleSearch(`/assets/g/${game.directory}`);
});
frag.appendChild(card);
});
grid.appendChild(frag);
renderedCount += batch.length;
preloadNextImages(5);
if (renderedCount >= filteredData.length) {
observer.unobserve(sentinel);
}
}
function preloadNextImages(limit = 3) {
const next = filteredData.slice(renderedCount, renderedCount + limit);
next.forEach(({ directory, image }) => {
const img = new Image();
img.src = `/assets/g/${directory}/${image}`;
});
}
});