Improve Cesium preview loading state
This commit is contained in:
@@ -739,6 +739,16 @@ function cesiumPreviewHtml(glbName, metadataName, routeName, vehicleModelName, a
|
|||||||
</div>
|
</div>
|
||||||
<div id="diagnostics">Loading diagnostics...</div>
|
<div id="diagnostics">Loading diagnostics...</div>
|
||||||
<div id="status">Loading ${escapeHtml(glbName)}...</div>
|
<div id="status">Loading ${escapeHtml(glbName)}...</div>
|
||||||
|
<div id="loadingOverlay">
|
||||||
|
<div class="loading-card">
|
||||||
|
<div class="loading-spinner" aria-hidden="true"></div>
|
||||||
|
<div class="loading-copy">
|
||||||
|
<strong>Loading scene</strong>
|
||||||
|
<span>${escapeHtml(areaId)}</span>
|
||||||
|
</div>
|
||||||
|
<div class="loading-bar" aria-hidden="true"><span></span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<script>window.OSM_ASSET_PREVIEW_CONFIG = ${escapeScriptJson(JSON.stringify(previewConfig))};</script>
|
<script>window.OSM_ASSET_PREVIEW_CONFIG = ${escapeScriptJson(JSON.stringify(previewConfig))};</script>
|
||||||
<script src="cesium-preview.js"></script>
|
<script src="cesium-preview.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -9,6 +9,15 @@ body,
|
|||||||
background: #d9e0e2;
|
background: #d9e0e2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#cesiumContainer {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 180ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.scene-ready #cesiumContainer {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
#controls,
|
#controls,
|
||||||
#diagnostics,
|
#diagnostics,
|
||||||
#status {
|
#status {
|
||||||
@@ -22,6 +31,110 @@ body,
|
|||||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.22);
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.22);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#loadingOverlay {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 3;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
background: #d9e0e2;
|
||||||
|
color: #1b2528;
|
||||||
|
font-size: 13px;
|
||||||
|
letter-spacing: 0;
|
||||||
|
transition: opacity 220ms ease-out, visibility 220ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-card {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 32px minmax(180px, 260px);
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
padding: 14px 16px;
|
||||||
|
border: 1px solid rgba(70, 86, 90, 0.16);
|
||||||
|
border-radius: 6px;
|
||||||
|
background: rgba(244, 248, 248, 0.88);
|
||||||
|
box-shadow: 0 12px 34px rgba(54, 68, 72, 0.20);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-spinner {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border: 3px solid rgba(42, 67, 72, 0.18);
|
||||||
|
border-top-color: #147d8a;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: loading-spin 840ms linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-copy {
|
||||||
|
display: grid;
|
||||||
|
gap: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-copy strong {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 650;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loadingOverlay span {
|
||||||
|
color: #526064;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-bar {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
position: relative;
|
||||||
|
height: 3px;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(42, 67, 72, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-bar span {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0 auto 0 0;
|
||||||
|
width: 42%;
|
||||||
|
border-radius: inherit;
|
||||||
|
background: #147d8a;
|
||||||
|
animation: loading-bar 1.2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes loading-spin {
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes loading-bar {
|
||||||
|
0% {
|
||||||
|
transform: translateX(-110%);
|
||||||
|
}
|
||||||
|
55% {
|
||||||
|
transform: translateX(70%);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateX(250%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body.scene-ready #loadingOverlay {
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.scene-error #loadingOverlay {
|
||||||
|
color: #5b1414;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.scene-error .loading-spinner {
|
||||||
|
border-color: rgba(120, 30, 30, 0.18);
|
||||||
|
border-top-color: #9f2f2f;
|
||||||
|
animation-play-state: paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.scene-error .loading-bar span {
|
||||||
|
background: #9f2f2f;
|
||||||
|
animation-play-state: paused;
|
||||||
|
}
|
||||||
|
|
||||||
#controls {
|
#controls {
|
||||||
top: 12px;
|
top: 12px;
|
||||||
left: 12px;
|
left: 12px;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
const config = window.OSM_ASSET_PREVIEW_CONFIG || {};
|
const config = window.OSM_ASSET_PREVIEW_CONFIG || {};
|
||||||
const statusEl = document.getElementById("status");
|
const statusEl = document.getElementById("status");
|
||||||
const diagnosticsEl = document.getElementById("diagnostics");
|
const diagnosticsEl = document.getElementById("diagnostics");
|
||||||
|
const loadingOverlay = document.getElementById("loadingOverlay");
|
||||||
const toggleCruise = document.getElementById("toggleCruise");
|
const toggleCruise = document.getElementById("toggleCruise");
|
||||||
const toggleFollow = document.getElementById("toggleFollow");
|
const toggleFollow = document.getElementById("toggleFollow");
|
||||||
const toggleScene = document.getElementById("toggleScene");
|
const toggleScene = document.getElementById("toggleScene");
|
||||||
@@ -22,14 +23,17 @@
|
|||||||
// from a control the user just touched. Keep the summary so the transient
|
// from a control the user just touched. Keep the summary so the transient
|
||||||
// note can be replaced instead of destroying it.
|
// note can be replaced instead of destroying it.
|
||||||
let baseStatus = "";
|
let baseStatus = "";
|
||||||
|
const PREVIEW_BACKGROUND = "#d9e0e2";
|
||||||
|
|
||||||
Cesium.Ion.defaultAccessToken = "";
|
Cesium.Ion.defaultAccessToken = "";
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
|
setLoadingMessage("Loading scene", config.areaId || "");
|
||||||
const metadata = await fetchJson(config.metadataName);
|
const metadata = await fetchJson(config.metadataName);
|
||||||
const routeData = await fetchOptionalJson(config.routeName);
|
const routeData = await fetchOptionalJson(config.routeName);
|
||||||
const placement = scenePlacement(metadata);
|
const placement = scenePlacement(metadata);
|
||||||
const viewer = createViewer();
|
const viewer = createViewer();
|
||||||
|
setLoadingMessage("Loading model", config.glbName || "");
|
||||||
const assets = await loadSceneAssets(viewer, metadata, placement);
|
const assets = await loadSceneAssets(viewer, metadata, placement);
|
||||||
const cruise = addVehicleCruises(viewer, routeData, config.vehicleModelName);
|
const cruise = addVehicleCruises(viewer, routeData, config.vehicleModelName);
|
||||||
const cameras = createCameraPresets(viewer, metadata, placement, cruise);
|
const cameras = createCameraPresets(viewer, metadata, placement, cruise);
|
||||||
@@ -40,6 +44,9 @@
|
|||||||
cameras.overview();
|
cameras.overview();
|
||||||
baseStatus = summaryText(metadata, assets, cruise);
|
baseStatus = summaryText(metadata, assets, cruise);
|
||||||
setStatus(baseStatus);
|
setStatus(baseStatus);
|
||||||
|
setLoadingMessage("Preparing view", "finalizing materials");
|
||||||
|
await waitForStableFrames(viewer);
|
||||||
|
document.body.classList.add("scene-ready");
|
||||||
// Handle for the browser console and for headless checks: everything else
|
// Handle for the browser console and for headless checks: everything else
|
||||||
// in here is closed over by the IIFE and unreachable from outside.
|
// in here is closed over by the IIFE and unreachable from outside.
|
||||||
window.osmPreview = { viewer, metadata, placement, assets, cruise, cameras };
|
window.osmPreview = { viewer, metadata, placement, assets, cruise, cameras };
|
||||||
@@ -78,16 +85,49 @@
|
|||||||
selectionIndicator: false,
|
selectionIndicator: false,
|
||||||
baseLayer: false
|
baseLayer: false
|
||||||
});
|
});
|
||||||
viewer.scene.globe.depthTestAgainstTerrain = true;
|
viewer.scene.globe.show = false;
|
||||||
viewer.scene.backgroundColor = Cesium.Color.fromCssColorString("#d9e0e2");
|
viewer.scene.globe.depthTestAgainstTerrain = false;
|
||||||
|
viewer.scene.backgroundColor = Cesium.Color.fromCssColorString(PREVIEW_BACKGROUND);
|
||||||
viewer.scene.skyAtmosphere.show = false;
|
viewer.scene.skyAtmosphere.show = false;
|
||||||
viewer.scene.skyBox.show = false;
|
viewer.scene.skyBox.show = false;
|
||||||
viewer.scene.sun.show = false;
|
viewer.scene.sun.show = false;
|
||||||
viewer.scene.moon.show = false;
|
viewer.scene.moon.show = false;
|
||||||
viewer.scene.globe.baseColor = Cesium.Color.fromCssColorString("#d4dcde");
|
viewer.scene.globe.baseColor = Cesium.Color.fromCssColorString(PREVIEW_BACKGROUND);
|
||||||
return viewer;
|
return viewer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setLoadingMessage(title, detail) {
|
||||||
|
if (!loadingOverlay) return;
|
||||||
|
const titleEl = loadingOverlay.querySelector("strong");
|
||||||
|
const detailEl = loadingOverlay.querySelector("span");
|
||||||
|
if (titleEl) titleEl.textContent = title;
|
||||||
|
if (detailEl) detailEl.textContent = detail || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForStableFrames(viewer) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const started = performance.now();
|
||||||
|
let frames = 0;
|
||||||
|
let done = false;
|
||||||
|
let remove = function () {};
|
||||||
|
function finish() {
|
||||||
|
if (done) return;
|
||||||
|
done = true;
|
||||||
|
remove();
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
remove = viewer.scene.postRender.addEventListener(() => {
|
||||||
|
frames += 1;
|
||||||
|
const elapsed = performance.now() - started;
|
||||||
|
if ((frames >= 14 && elapsed >= 650) || elapsed >= 2800) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
window.setTimeout(finish, 3200);
|
||||||
|
viewer.scene.requestRender();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function scenePlacement(metadata) {
|
function scenePlacement(metadata) {
|
||||||
const anchor = metadata.anchor || {};
|
const anchor = metadata.anchor || {};
|
||||||
const longitude = Number(anchor.longitude || 0);
|
const longitude = Number(anchor.longitude || 0);
|
||||||
@@ -626,5 +666,7 @@
|
|||||||
main().catch((error) => {
|
main().catch((error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
setStatus("Failed to load Cesium preview: " + error.message);
|
setStatus("Failed to load Cesium preview: " + error.message);
|
||||||
|
document.body.classList.add("scene-error");
|
||||||
|
setLoadingMessage("Failed to load scene", error.message);
|
||||||
});
|
});
|
||||||
}());
|
}());
|
||||||
|
|||||||
Reference in New Issue
Block a user