Add loading overlay while scenes initialize

- Show a spinner overlay that fades out once Cesium globe
  data is ready, with a 1s minimum and 8s timeout
- Expose a ready promise on CesiumGlobe resolved after country
  and city data load
- Remove temporary inspect script
This commit is contained in:
2026-09-19 01:18:15 +08:00
parent 58bc998c6e
commit 32bb48cc0b
5 changed files with 57 additions and 26 deletions
+8
View File
@@ -63,6 +63,14 @@
<div class="hud hint" id="hint">拖动旋转 · 滚轮缩放 · 时间轴视图拖拽左右滑动 · 点击选中 · ← → 翻页</div> <div class="hud hint" id="hint">拖动旋转 · 滚轮缩放 · 时间轴视图拖拽左右滑动 · 点击选中 · ← → 翻页</div>
</div> </div>
<!-- 加载界面 -->
<div id="loading" aria-hidden="true">
<div class="loading-box">
<div class="spinner"></div>
<div class="loading-text">载入中…</div>
</div>
</div>
<script type="module" src="/src/main.js"></script> <script type="module" src="/src/main.js"></script>
</body> </body>
</html> </html>
-26
View File
@@ -1,26 +0,0 @@
import puppeteer from 'puppeteer-core';
const CHROME = 'C:/Program Files/Google/Chrome/Application/chrome.exe';
const browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new',
args: ['--no-sandbox', '--disable-gpu', '--use-gl=swiftshader', '--enable-unsafe-swiftshader', '--window-size=1440,900'] });
const page = await browser.newPage();
await page.setViewport({ width: 1440, height: 900 });
const errors = [];
page.on('pageerror', (e) => errors.push(e.message));
page.on('console', (m) => { if (m.type() === 'error') errors.push('console: ' + m.text()); });
await page.goto('http://localhost:5250/', { waitUntil: 'domcontentloaded', timeout: 60000 });
await new Promise(r => setTimeout(r, 8000));
// 还没切到地理视图,Cesium 应该已经创建
const pre = await page.evaluate(() => {
const g = window.__app.globe;
return { created: !!(g && g.viewer), markers: g && g.markers ? g.markers.length : 0 };
});
console.log('启动即创建:', JSON.stringify(pre));
await page.click('.view-btn[data-view="globe"]');
await new Promise(r => setTimeout(r, 2000));
const after = await page.evaluate(() => {
const el = document.getElementById('cesium-container');
return { visible: el.classList.contains('visible'), visibility: getComputedStyle(el).visibility };
});
console.log('切到地理:', JSON.stringify(after));
console.log('ERRORS', errors.length ? errors.join(' | ') : '(none)');
await browser.close();
+13
View File
@@ -8,3 +8,16 @@ for (const ev of ['copy', 'cut', 'contextmenu']) {
// 启动应用 // 启动应用
const app = new App(); const app = new App();
window.__app = app; // 便于调试 window.__app = app; // 便于调试
// 加载界面:等三个视图初始化完成(最少展示 1 秒,最多等 8 秒)后淡出
const t0 = Date.now();
const MIN_DISPLAY = 1000;
Promise.race([
app.globe.ready,
new Promise((r) => setTimeout(r, 8000)),
]).then(() => {
const wait = Math.max(0, MIN_DISPLAY - (Date.now() - t0));
setTimeout(() => {
document.getElementById('loading').classList.add('hidden');
}, wait);
});
+13
View File
@@ -69,6 +69,8 @@ export class CesiumGlobe {
this.selected = -1; this.selected = -1;
this._created = false; this._created = false;
this._visible = false; this._visible = false;
this._pendingData = 2; // 国家边界 + 城市数据
this.ready = new Promise((resolve) => { this._resolveReady = resolve; });
} }
ensureCreated() { ensureCreated() {
@@ -316,6 +318,8 @@ export class CesiumGlobe {
} }
} catch (err) { } catch (err) {
console.warn('[CesiumGlobe] 行政区划加载失败:', err); console.warn('[CesiumGlobe] 行政区划加载失败:', err);
} finally {
this._dataReady();
} }
} }
@@ -362,6 +366,15 @@ export class CesiumGlobe {
}, },
}); });
} }
this._dataReady();
}
_dataReady() {
this._pendingData--;
if (this._pendingData <= 0 && this._resolveReady) {
this._resolveReady();
this._resolveReady = null;
}
} }
setSelected(index) { setSelected(index) {
+23
View File
@@ -52,6 +52,29 @@ html, body {
background: #0c0906; opacity: 0; transition: opacity 0.18s ease; background: #0c0906; opacity: 0; transition: opacity 0.18s ease;
} }
/* 加载界面 */
#loading {
position: fixed; inset: 0; z-index: 1000;
display: flex; align-items: center; justify-content: center;
background: #14100d;
transition: opacity 0.6s ease;
}
#loading.hidden { opacity: 0; pointer-events: none; }
.loading-box { display: flex; flex-direction: column; align-items: center; gap: 18px; }
.spinner {
width: 54px; height: 54px;
border: 3px solid rgba(201, 162, 39, 0.22);
border-top-color: #c9a227;
border-radius: 50%;
animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.loading-text {
font-family: var(--serif);
font-size: 14px; letter-spacing: 0.3em;
color: rgba(230, 216, 182, 0.7);
}
/* 顶部标题 */ /* 顶部标题 */
.hud { position: absolute; z-index: 5; } .hud { position: absolute; z-index: 5; }
.top-left { top: 22px; left: 26px; } .top-left { top: 22px; left: 26px; }