import "./stats-plus.css"; // Adapted from https://github.com/aframevr/aframe/blob/master/src/components/scene/stats.js function createStats(scene) { const threeStats = new window.threeStats(scene.renderer); const aframeStats = new window.aframeStats(scene); const plugins = scene.isMobile ? [] : [threeStats, aframeStats]; return new window.rStats({ css: [], // Our stylesheet is injected from AFrame. values: { fps: { caption: "fps", below: 30 } }, groups: [{ caption: "Framerate", values: ["fps", "raf"] }], plugins: plugins }); } const HIDDEN_CLASS = "a-hidden"; AFRAME.registerComponent("stats-plus", { // Whether or not the stats panel is expanded. // Shows FPS counter when collapsed. schema: { default: false }, init() { this.onExpand = this.onExpand.bind(this); this.onCollapse = this.onCollapse.bind(this); this.onEnterVr = this.onEnterVr.bind(this); this.onExitVr = this.onExitVr.bind(this); const scene = this.el.sceneEl; this.stats = createStats(scene); this.statsEl = document.querySelector(".rs-base"); // Add header to stats panel so we can collapse it const statsHeaderEl = document.createElement("div"); statsHeaderEl.classList.add("rs-header"); const statsTitleEl = document.createElement("h1"); statsTitleEl.innerHTML = "Stats"; statsHeaderEl.appendChild(statsTitleEl); const collapseEl = document.createElement("div"); collapseEl.classList.add("rs-collapse-btn"); collapseEl.innerHTML = "X"; collapseEl.addEventListener("click", this.onCollapse); statsHeaderEl.appendChild(collapseEl); this.statsEl.insertBefore(statsHeaderEl, this.statsEl.firstChild); // Add fps counter to the page this.fpsEl = document.createElement("div"); this.fpsEl.addEventListener("click", this.onExpand); this.fpsEl.classList.add("rs-fps-counter"); document.body.appendChild(this.fpsEl); this.lastFpsUpdate = performance.now(); this.lastFps = 0; this.frameCount = 0; if (scene.isMobile) { this.statsEl.classList.add("rs-mobile"); this.fpsEl.classList.add("rs-mobile"); } scene.addEventListener("enter-vr", this.onEnterVr); scene.addEventListener("exit-vr", this.onExitVr); }, update(oldData) { if (oldData !== this.data) { if (this.data) { this.statsEl.classList.remove(HIDDEN_CLASS); this.fpsEl.classList.add(HIDDEN_CLASS); } else { this.statsEl.classList.add(HIDDEN_CLASS); this.fpsEl.classList.remove(HIDDEN_CLASS); } } }, tick() { if (this.data) { // Update rStats const stats = this.stats; stats("rAF").tick(); stats("FPS").frame(); stats().update(); } else { // Update the fps counter const now = performance.now(); this.frameCount++; // Update the fps counter text once a second if (now >= this.lastFpsUpdate + 1000) { const fps = Math.round(this.frameCount / ((now - this.lastFpsUpdate) / 1000)); if (fps !== this.lastFps) { this.fpsEl.innerHTML = Math.round(fps) + " FPS"; this.lastFps = fps; } this.lastFpsUpdate = now; this.frameCount = 0; } } }, onEnterVr() { // Hide all stats elements when entering VR on mobile if (this.el.sceneEl.isMobile) { this.statsEl.classList.add(HIDDEN_CLASS); this.fpsEl.classList.add(HIDDEN_CLASS); } }, onExitVr() { // Revert to previous state whe exiting VR on mobile if (this.el.sceneEl.isMobile) { if (this.data) { this.statsEl.classList.remove(HIDDEN_CLASS); } else { this.fpsEl.classList.remove(HIDDEN_CLASS); } } }, onExpand() { this.el.setAttribute(this.name, true); }, onCollapse() { this.el.setAttribute(this.name, false); }, remove() { this.el.sceneEl.removeEventListener("enter-vr", this.hide); this.el.sceneEl.removeEventListener("exit-vr", this.show); if (this.statsEl) { this.statsEl.parentNode.removeChild(this.statsEl); this.fpsEl.parentNode.removeChild(this.fpsEl); } } });