From 58134bc7c205e78c2098e95ca1c80417890ec4a7 Mon Sep 17 00:00:00 2001
From: Robert Long <robert@robertlong.me>
Date: Fri, 13 Apr 2018 14:22:56 -0700
Subject: [PATCH] Stats panel component that collapses to an FPS counter

---
 src/components/stats-plus.css |  26 +++++++
 src/components/stats-plus.js  | 130 ++++++++++++++++++++++++++++++++++
 src/hub.js                    |   7 +-
 3 files changed, 159 insertions(+), 4 deletions(-)
 create mode 100644 src/components/stats-plus.css
 create mode 100644 src/components/stats-plus.js

diff --git a/src/components/stats-plus.css b/src/components/stats-plus.css
new file mode 100644
index 000000000..fc4417bf5
--- /dev/null
+++ b/src/components/stats-plus.css
@@ -0,0 +1,26 @@
+:global(.rs-header) {
+  display: flex;
+  justify-content: space-between;
+  border-bottom: 1px rgba(255,255,255,0.1) solid;
+  margin-bottom: 8px;
+}
+
+:global(.rs-collapse-btn) {
+  cursor: pointer;
+  font-size: 12px;
+}
+
+:global(.rs-fps-counter) {
+  cursor: pointer;
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  padding: 8px;
+  color: #aaa;
+  font-size: 10px;
+}
+
+:global(.rs-mobile) {
+  bottom: auto;
+  top: 0;
+}
\ No newline at end of file
diff --git a/src/components/stats-plus.js b/src/components/stats-plus.js
new file mode 100644
index 000000000..c64100eea
--- /dev/null
+++ b/src/components/stats-plus.js
@@ -0,0 +1,130 @@
+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.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 = this.frameCount / ((now - this.lastFpsUpdate) / 1000);
+        this.fpsEl.innerHTML = Math.round(fps) + " 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.removeListener("enter-vr", this.hide);
+    this.el.sceneEl.removeListener("exit-vr", this.show);
+
+    if (this.statsEl) {
+      this.statsEl.parentNode.removeChild(this.statsEl);
+      this.fpsEl.parentNode.removeChild(this.fpsEl);
+    }
+  }
+});
diff --git a/src/hub.js b/src/hub.js
index 9dc3247e3..081bc5442 100644
--- a/src/hub.js
+++ b/src/hub.js
@@ -46,6 +46,7 @@ import "./components/hand-poses";
 import "./components/gltf-model-plus";
 import "./components/gltf-bundle";
 import "./components/hud-controller";
+import "./components/stats-plus";
 
 import ReactDOM from "react-dom";
 import React from "react";
@@ -133,6 +134,8 @@ async function enterScene(mediaStream, enterInVR, janusRoomId) {
   document.querySelector("a-scene canvas").classList.remove("blurred");
   scene.render();
 
+  scene.setAttribute("stats-plus", false);
+
   if (enterInVR) {
     scene.enterVR();
   }
@@ -146,10 +149,6 @@ async function enterScene(mediaStream, enterInVR, janusRoomId) {
     serverURL: process.env.JANUS_SERVER
   });
 
-  if (!qsTruthy("no_stats")) {
-    scene.setAttribute("stats", true);
-  }
-
   if (isMobile || qsTruthy("mobile")) {
     playerRig.setAttribute("virtual-gamepad-controls", {});
   }
-- 
GitLab