Supra-Tex / hf_space /mesh_progress.js
UniLat3D's picture
Use Loading wording for 3D mesh progress
8eca6f1 verified
Raw History Blame Contribute Delete
4.52 kB
// Track Babylon's mesh XHR without altering its response or starting another download.
(() => {
const root = document.getElementById('st-app');
if (!root || root.dataset.meshProgressInstalled) return;
root.dataset.meshProgressInstalled = 'true';
let version = 0;
let activeURL = '';
let hideTimer = null;
const meshURL = value => typeof value === 'string' && /\.glb(?:[?#]|$)/i.test(value);
const currentURL = () => root.querySelector('#st-model a[download]')?.href || '';
const bar = () => root.querySelector('.st-mesh-loading');
const show = (label, percent = null) => {
const element = bar();
if (!element) return;
element.hidden = false;
element.querySelector('.st-mesh-loading-label').textContent = label;
const progress = element.querySelector('progress');
if (percent === null) progress.removeAttribute('value');
else progress.value = Math.max(0, Math.min(100, percent));
};
const start = url => {
clearTimeout(hideTimer);
activeURL = url;
const token = ++version;
show('Loading 3D model…');
return token;
};
const complete = token => {
if (token !== version) return;
show('Loading 3D model · 100%', 100);
// This completion describes the download, not an inferred rendering percentage.
hideTimer = setTimeout(() => {
if (token === version && bar()) bar().hidden = true;
}, 2200);
};
const originalOpen = XMLHttpRequest.prototype.open;
const trackedOpen = function(method, url, ...rest) {
const result = originalOpen.call(this, method, url, ...rest);
if (String(method).toUpperCase() !== 'GET' || !meshURL(String(url))) return result;
let token;
this.addEventListener('loadstart', () => { token = start(String(url)); });
this.addEventListener('progress', event => {
if (token !== version) return;
if (event.lengthComputable && event.total > 0) {
const percent = Math.floor(event.loaded / event.total * 100);
show(`Loading 3D model · ${percent}%`, percent);
} else {
show(`Loading 3D model · ${(event.loaded / 1048576).toFixed(1)} MB`);
}
});
this.addEventListener('load', () => {
if (token !== version) return;
if (this.status >= 200 && this.status < 300) complete(token);
else show('Could not load model. Please select the example again.');
});
this.addEventListener('error', () => {
if (token === version) show('Could not load model. Please select the example again.');
});
this.addEventListener('abort', () => {
if (token === version && bar()) bar().hidden = true;
});
return result;
};
XMLHttpRequest.prototype.open = trackedOpen;
const onClick = event => {
const thumbnail = event.target.closest('#st-examples .thumbnail-item');
if (thumbnail && !root.querySelector('.st-estimate')) {
const image = thumbnail.querySelector('img');
const name = decodeURIComponent(image?.src.split('/').pop() || '').replace(/\.png.*$/, '');
if (!currentURL().includes('/' + name + '-')) start('');
}
const generate = event.target.closest('#st-generate');
if (generate && !generate.disabled) {
++version;
activeURL = '';
clearTimeout(hideTimer);
if (bar()) bar().hidden = true;
}
};
root.addEventListener('click', onClick, true);
// The initial component can start downloading before demo.load installs this hook.
const resourceObserver = new PerformanceObserver(list => {
for (const entry of list.getEntries()) {
if (meshURL(entry.name) && entry.name === currentURL() && activeURL === entry.name) {
complete(version);
}
}
});
resourceObserver.observe({type: 'resource', buffered: true});
const initial = currentURL();
if (meshURL(initial) && !performance.getEntriesByName(initial).length) start(initial);
window.addEventListener('pagehide', () => {
if (XMLHttpRequest.prototype.open === trackedOpen) XMLHttpRequest.prototype.open = originalOpen;
resourceObserver.disconnect();
root.removeEventListener('click', onClick, true);
clearTimeout(hideTimer);
}, {once: true});
})();