stats-widget.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. import {Stats} from "./stats.js";
  2. function _defineProperty(obj, key, value) {
  3. if (key in obj) {
  4. Object.defineProperty(obj, key, {
  5. value,
  6. enumerable: true,
  7. configurable: true,
  8. writable: true
  9. });
  10. } else {
  11. obj[key] = value;
  12. }
  13. return obj;
  14. }
  15. const KB = 1024;
  16. const MB = 1024 * KB;
  17. const GB = 1024 * MB;
  18. function formatTime(t) {
  19. let value;
  20. let unit;
  21. let precision;
  22. if (t < 1) {
  23. value = t * 1e3;
  24. unit = "\u03BCs";
  25. precision = 0;
  26. } else if (t < 1e3) {
  27. value = t;
  28. unit = "ms";
  29. precision = 2;
  30. } else {
  31. value = t / 1e3;
  32. unit = "s";
  33. precision = 2;
  34. }
  35. return "".concat(value.toFixed(precision)).concat(unit);
  36. }
  37. function formatMemory(b) {
  38. let value;
  39. let unit;
  40. let precision;
  41. if (b < KB) {
  42. value = b;
  43. unit = " bytes";
  44. precision = 0;
  45. } else if (b < MB) {
  46. value = b / KB;
  47. unit = "kB";
  48. precision = 2;
  49. } else if (b < GB) {
  50. value = b / MB;
  51. unit = "MB";
  52. precision = 2;
  53. } else {
  54. value = b / GB;
  55. unit = "GB";
  56. precision = 2;
  57. }
  58. return "".concat(value.toFixed(precision)).concat(unit);
  59. }
  60. const RIGHT_ARROW = "\u25B6";
  61. const DOWN_ARROW = "\u2B07";
  62. const DEFAULT_CSS = {
  63. css: {
  64. position: "fixed",
  65. zIndex: 1e4,
  66. color: "#ccc",
  67. background: "#000",
  68. fontFamily: "Helvetica,Arial,sans-serif",
  69. padding: "8px",
  70. fontSize: "12px",
  71. lineSpacing: 6
  72. },
  73. headerCSS: {
  74. fontSize: "16px",
  75. cursor: "pointer"
  76. },
  77. itemCSS: {
  78. paddingLeft: "8px"
  79. }
  80. };
  81. const DEFAULT_FORMATTERS = {
  82. count: (stat) => "".concat(stat.name, ": ").concat(stat.count),
  83. averageTime: (stat) => "".concat(stat.name, ": ").concat(formatTime(stat.getAverageTime())),
  84. totalTime: (stat) => "".concat(stat.name, ": ").concat(formatTime(stat.time)),
  85. fps: (stat) => "".concat(stat.name, ": ").concat(Math.round(stat.getHz()), "fps"),
  86. memory: (stat) => "".concat(stat.name, ": ").concat(formatMemory(stat.count))
  87. };
  88. class StatsWidget {
  89. constructor(stats2, options) {
  90. _defineProperty(this, "stats", void 0);
  91. _defineProperty(this, "title", void 0);
  92. _defineProperty(this, "collapsed", false);
  93. _defineProperty(this, "_framesPerUpdate", void 0);
  94. _defineProperty(this, "_formatters", DEFAULT_FORMATTERS);
  95. _defineProperty(this, "_css", void 0);
  96. _defineProperty(this, "_headerCSS", void 0);
  97. _defineProperty(this, "_itemCSS", void 0);
  98. _defineProperty(this, "_container", null);
  99. _defineProperty(this, "_innerContainer", null);
  100. _defineProperty(this, "_statsContainer", null);
  101. _defineProperty(this, "_header", null);
  102. _defineProperty(this, "_resetOnUpdate", {});
  103. _defineProperty(this, "_counter", 0);
  104. _defineProperty(this, "_items", {});
  105. _defineProperty(this, "_added", false);
  106. this.stats = stats2;
  107. this.title = options === null || options === void 0 ? void 0 : options.title;
  108. this._framesPerUpdate = Math.round(Math.max((options === null || options === void 0 ? void 0 : options.framesPerUpdate) || 1, 1));
  109. this._initializeFormatters(options);
  110. this._initializeUpdateConfigs(options);
  111. this._css = Object.assign({}, DEFAULT_CSS.css, options === null || options === void 0 ? void 0 : options.css);
  112. this._headerCSS = Object.assign({}, DEFAULT_CSS.headerCSS, this._css.header);
  113. this._itemCSS = Object.assign({}, DEFAULT_CSS.itemCSS, this._css.item);
  114. delete this._css.header;
  115. delete this._css.item;
  116. this._createDOM(options === null || options === void 0 ? void 0 : options.container);
  117. this._createDOMStats();
  118. }
  119. setStats(stats2) {
  120. this.stats = stats2;
  121. this._createDOMStats();
  122. this._setHeaderContent();
  123. this.update();
  124. }
  125. update() {
  126. const stats2 = this.stats && this.stats.stats;
  127. if (!stats2 || Object.keys(stats2).length === 0) {
  128. return;
  129. }
  130. if (this._counter++ % this._framesPerUpdate !== 0) {
  131. return;
  132. }
  133. this._update();
  134. }
  135. remove() {
  136. this._container.removeChild(this._innerContainer);
  137. }
  138. setCollapsed(collapsed) {
  139. this.collapsed = collapsed;
  140. if (this._statsContainer) {
  141. this._statsContainer.style.display = this.collapsed ? "none" : "block";
  142. }
  143. this._setHeaderContent();
  144. }
  145. _update() {
  146. this.stats.forEach((stat) => {
  147. this._createDOMItem(stat.name);
  148. this._items[stat.name].innerHTML = this._getLines(stat).join("<BR>");
  149. if (this._resetOnUpdate[stat.name]) {
  150. stat.reset();
  151. }
  152. });
  153. }
  154. _initializeFormatters(options) {
  155. if (options !== null && options !== void 0 && options.formatters) {
  156. for (const name in options.formatters) {
  157. let formatter = options.formatters[name];
  158. if (typeof formatter === "string") {
  159. formatter = DEFAULT_FORMATTERS[formatter];
  160. }
  161. this._formatters[name] = formatter;
  162. }
  163. }
  164. }
  165. _initializeUpdateConfigs(options) {
  166. if (options !== null && options !== void 0 && options.resetOnUpdate) {
  167. for (const name in options.resetOnUpdate) {
  168. this._resetOnUpdate[name] = options.resetOnUpdate[name];
  169. }
  170. }
  171. }
  172. _createDOM(container) {
  173. if (typeof document === "undefined" || !document) {
  174. return;
  175. }
  176. this._container = container;
  177. if (!this._container) {
  178. this._container = document.createElement("div");
  179. for (const name in this._css) {
  180. this._container.style[name] = this._css[name];
  181. }
  182. document.body.appendChild(this._container);
  183. }
  184. this._innerContainer = document.createElement("div");
  185. this._container.appendChild(this._innerContainer);
  186. this._createDOMHeader();
  187. this._statsContainer = document.createElement("div");
  188. this._statsContainer.style.display = "block";
  189. this._innerContainer.appendChild(this._statsContainer);
  190. }
  191. _createDOMHeader() {
  192. if (!this._header) {
  193. this._header = document.createElement("div");
  194. for (const name in this._headerCSS) {
  195. this._header.style[name] = this._headerCSS[name];
  196. }
  197. this._header.onclick = this._toggleCollapsed.bind(this);
  198. this._innerContainer.appendChild(this._header);
  199. }
  200. this._setHeaderContent();
  201. }
  202. _setHeaderContent() {
  203. if (this._header) {
  204. const collapsedState = this.collapsed ? RIGHT_ARROW : DOWN_ARROW;
  205. const title = this.title || this.stats && this.stats.id || "Stats";
  206. this._header.innerText = "".concat(collapsedState, " ").concat(title);
  207. }
  208. }
  209. _toggleCollapsed() {
  210. this.setCollapsed(!this.collapsed);
  211. }
  212. _createDOMStats() {
  213. if (this.stats instanceof Stats) {
  214. this.stats.forEach((stat) => {
  215. this._createDOMItem(stat.name);
  216. });
  217. }
  218. }
  219. _createDOMItem(statName) {
  220. if (!this._statsContainer) {
  221. return;
  222. }
  223. if (this._items[statName]) {
  224. return;
  225. }
  226. this._items[statName] = document.createElement("div");
  227. for (const name in this._itemCSS) {
  228. this._items[statName].style[name] = this._itemCSS[name];
  229. }
  230. this._statsContainer.appendChild(this._items[statName]);
  231. }
  232. _getLines(stat) {
  233. const formatter = this._formatters[stat.name] || this._formatters[stat.type] || DEFAULT_FORMATTERS.count;
  234. return formatter(this.stats.get(stat.name)).split("\n");
  235. }
  236. }
  237. export default StatsWidget;
  238. export {StatsWidget};