const boot = document.getElementById('boot'); const title = document.getElementById('boot-title'); const detail = document.getElementById('boot-detail'); const progress = document.getElementById('boot-progress'); const retry = document.getElementById('boot-retry'); const formatBytes = bytes => (bytes / 1024 / 1024).toFixed(1) + ' MB'; let entries = new Map(); let failed = false; let lastPaint = 0; export function beginLoading(files, message = '正在载入三维场景') { entries = new Map(files.map(file => [file, {stage:'waiting', loaded:0, total:0}])); failed = false; boot.hidden = false; boot.dataset.state = 'loading'; boot.setAttribute('aria-busy', 'true'); title.textContent = message; detail.textContent = '正在检查本地资源缓存'; progress.hidden = false; progress.removeAttribute('value'); retry.hidden = true; lastPaint = 0; } export function reportProgress(update) { if (failed || !entries.has(update.file)) return; entries.set(update.file, update); // Large streamed files can emit hundreds of chunks in a frame. if (update.stage === 'download' && performance.now() - lastPaint < 100) return; lastPaint = performance.now(); const values = [...entries.values()]; const done = values.filter(item => item.stage === 'ready').length; const loaded = values.reduce((sum, item) => sum + item.loaded, 0); const cached = values.filter(item => item.stage === 'cache').length; const parsing = values.some(item => item.stage === 'parse'); const downloading = values.some(item => item.stage === 'download'); progress.max = Math.max(1, values.length); progress.value = done; title.textContent = `正在载入场景资源 ${done} / ${values.length}`; detail.textContent = `${parsing ? '正在解析模型' : downloading ? '正在下载资源' : cached ? '正在读取本地缓存' : '正在准备场景'} · 已读取 ${formatBytes(loaded)}`; } export function finishLoading() { if (failed) return; boot.setAttribute('aria-busy', 'false'); boot.hidden = true; } export function failLoading(error) { failed = true; boot.hidden = false; boot.dataset.state = 'error'; boot.setAttribute('aria-busy', 'false'); title.textContent = '场景资源加载失败'; detail.textContent = '部分资源未能载入,请检查网络后重试。' + (error?.message ? '(' + error.message + ')' : ''); progress.hidden = true; retry.hidden = false; } retry.addEventListener('click', () => window.location.reload());