/** * 배경 씬 라이브러리 JavaScript * Unity에서 업로드된 JSON 데이터를 기반으로 배경 목록을 표시 */ (function() { 'use strict'; // 데이터 파일 경로 const DATA_PATH = 'data/backgrounds.json'; // 상태 let backgroundsData = []; let filteredData = []; let currentTag = 'all'; let currentView = 'grid'; let searchQuery = ''; // DOM 요소 const elements = { grid: document.getElementById('backgroundsGrid'), filterTags: document.getElementById('filterTags'), searchInput: document.getElementById('searchInput'), totalCount: document.getElementById('totalCount'), filteredCount: document.getElementById('filteredCount'), lastUpdated: document.getElementById('lastUpdated'), noData: document.getElementById('noData'), noResults: document.getElementById('noResults'), imageModal: document.getElementById('imageModal'), modalImage: document.getElementById('modalImage'), modalTitle: document.getElementById('modalTitle'), modalCategory: document.getElementById('modalCategory'), modalTags: document.getElementById('modalTags') }; /** * 초기화 */ async function init() { await loadData(); setupEventListeners(); } /** * 데이터 로드 */ async function loadData() { try { const response = await fetch(DATA_PATH + '?t=' + Date.now()); if (!response.ok) { throw new Error('데이터를 찾을 수 없습니다'); } const data = await response.json(); backgroundsData = data.backgrounds || []; // 마지막 업데이트 시간 표시 if (data.lastUpdated) { const date = new Date(data.lastUpdated); elements.lastUpdated.textContent = `마지막 업데이트: ${formatDate(date)}`; } // 태그 필터 생성 createTagFilters(); // 통계 업데이트 elements.totalCount.textContent = backgroundsData.length; // 데이터 렌더링 filterAndRender(); } catch (error) { console.error('데이터 로드 실패:', error); showNoData(); } } /** * 태그 필터 버튼 생성 */ function createTagFilters() { // 모든 태그 수집 const allTags = new Set(); backgroundsData.forEach(bg => { if (bg.tags && Array.isArray(bg.tags)) { bg.tags.forEach(tag => allTags.add(tag)); } }); // 기존 태그 버튼 제거 (전체 버튼 제외) const existingTags = elements.filterTags.querySelectorAll('.filter-tag:not([data-tag="all"])'); existingTags.forEach(tag => tag.remove()); // 태그 버튼 생성 allTags.forEach(tag => { const btn = document.createElement('button'); btn.className = 'filter-tag'; btn.dataset.tag = tag; btn.textContent = tag; btn.addEventListener('click', () => setTagFilter(tag)); elements.filterTags.appendChild(btn); }); } /** * 이벤트 리스너 설정 */ function setupEventListeners() { // 검색 elements.searchInput.addEventListener('input', debounce((e) => { searchQuery = e.target.value.toLowerCase(); filterAndRender(); }, 300)); // 전체 태그 필터 const allTagBtn = elements.filterTags.querySelector('[data-tag="all"]'); if (allTagBtn) { allTagBtn.addEventListener('click', () => setTagFilter('all')); } // 뷰 전환 document.querySelectorAll('.view-btn').forEach(btn => { btn.addEventListener('click', () => { document.querySelectorAll('.view-btn').forEach(b => b.classList.remove('active')); btn.classList.add('active'); currentView = btn.dataset.view; updateViewMode(); }); }); // 모달 닫기 elements.imageModal.querySelector('.modal-overlay').addEventListener('click', closeModal); elements.imageModal.querySelector('.modal-close').addEventListener('click', closeModal); // ESC 키로 모달 닫기 document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeModal(); }); } /** * 태그 필터 설정 */ function setTagFilter(tag) { currentTag = tag; // 버튼 활성화 상태 업데이트 document.querySelectorAll('.filter-tag').forEach(btn => { btn.classList.toggle('active', btn.dataset.tag === tag); }); filterAndRender(); } /** * 필터링 및 렌더링 */ function filterAndRender() { // 필터링 filteredData = backgroundsData.filter(bg => { // 태그 필터 if (currentTag !== 'all') { if (!bg.tags || !bg.tags.includes(currentTag)) { return false; } } // 검색 필터 if (searchQuery) { const searchTarget = `${bg.sceneName} ${bg.categoryName} ${(bg.tags || []).join(' ')}`.toLowerCase(); if (!searchTarget.includes(searchQuery)) { return false; } } return true; }); // 통계 업데이트 elements.filteredCount.textContent = filteredData.length; // 렌더링 render(); } /** * 카드 렌더링 */ function render() { // 로딩/에러 상태 숨기기 const loadingPlaceholder = elements.grid.querySelector('.loading-placeholder'); if (loadingPlaceholder) { loadingPlaceholder.remove(); } elements.noData.style.display = 'none'; elements.noResults.style.display = 'none'; // 데이터 없음 if (backgroundsData.length === 0) { elements.grid.innerHTML = ''; showNoData(); return; } // 검색 결과 없음 if (filteredData.length === 0) { elements.grid.innerHTML = ''; elements.noResults.style.display = 'block'; return; } // 카드 생성 elements.grid.innerHTML = filteredData.map(bg => createCard(bg)).join(''); // 카드 클릭 이벤트 elements.grid.querySelectorAll('.background-card').forEach((card, index) => { card.addEventListener('click', () => openModal(filteredData[index])); }); // 뷰 모드 적용 updateViewMode(); } /** * 카드 HTML 생성 */ function createCard(bg) { const thumbnailHtml = bg.thumbnailUrl ? `${escapeHtml(bg.sceneName)}` : `
🏞️
`; const tagsHtml = (bg.tags || []) .map(tag => `${escapeHtml(tag)}`) .join(''); const category = bg.category || extractCategory(bg.categoryName); return `
${thumbnailHtml} ${escapeHtml(category)}

${escapeHtml(bg.sceneName)}

${tagsHtml}
`; } /** * 뷰 모드 업데이트 */ function updateViewMode() { elements.grid.classList.toggle('list-view', currentView === 'list'); } /** * 모달 열기 */ function openModal(bg) { if (bg.thumbnailUrl) { elements.modalImage.src = bg.thumbnailUrl; elements.modalImage.alt = bg.sceneName; } else { elements.modalImage.src = ''; elements.modalImage.alt = ''; } elements.modalTitle.textContent = bg.sceneName; elements.modalCategory.textContent = bg.categoryName; elements.modalTags.innerHTML = (bg.tags || []) .map(tag => `${escapeHtml(tag)}`) .join(''); elements.imageModal.classList.add('active'); document.body.style.overflow = 'hidden'; } /** * 모달 닫기 */ function closeModal() { elements.imageModal.classList.remove('active'); document.body.style.overflow = ''; } /** * 데이터 없음 표시 */ function showNoData() { elements.grid.innerHTML = ''; elements.noData.style.display = 'block'; } /** * 카테고리 추출 (폴더명에서) */ function extractCategory(folderName) { if (!folderName) return '기타'; const match = folderName.match(/\[([^\]]+)\]/); return match ? match[1] : folderName; } /** * 날짜 포맷 */ function formatDate(date) { return date.toLocaleString('ko-KR', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); } /** * HTML 이스케이프 */ function escapeHtml(str) { if (!str) return ''; const div = document.createElement('div'); div.textContent = str; return div.innerHTML; } /** * 디바운스 */ function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // 페이지 로드 시 초기화 document.addEventListener('DOMContentLoaded', init); })();