diff --git a/ember-resources/package.json b/ember-resources/package.json index cbcad26c2..dfeb25b52 100644 --- a/ember-resources/package.json +++ b/ember-resources/package.json @@ -15,6 +15,7 @@ "./util": "./dist/util/index.js", "./util/cell": "./dist/util/cell.js", "./util/keep-latest": "./dist/util/keep-latest.js", + "./util/fps": "./dist/util/fps.js", "./util/map": "./dist/util/map.js", "./util/helper": "./dist/util/helper.js", "./util/remote-data": "./dist/util/remote-data.js", @@ -43,6 +44,9 @@ "util/function": [ "dist/util/function.d.ts" ], + "util/fps": [ + "dist/util/fps.d.ts" + ], "util/map": [ "dist/util/map.d.ts" ], diff --git a/ember-resources/src/util/fps.ts b/ember-resources/src/util/fps.ts new file mode 100644 index 000000000..8806d2f35 --- /dev/null +++ b/ember-resources/src/util/fps.ts @@ -0,0 +1,88 @@ +import { cell, resource, resourceFactory } from '../index'; + +/** + * Utility that uses requestAnimationFrame to report + * how many frames per second the current monitor is + * rendering at. + * + * The result is rounded to two decimal places. + * + * ```js + * import { FramRate } from 'ember-resources/util/fps'; + * + * + * ``` + */ +export const FrameRate = resource(({ on }) => { + let value = cell(0); + let startTime = new Date().getTime(); + let frame: number; + + let update = () => { + // simulate receiving data as fast as possible + frame = requestAnimationFrame(() => { + value.current++; + update(); + }); + }; + + on.cleanup(() => cancelAnimationFrame(frame)); + + // Start the infinite requestAnimationFrame chain + update(); + + return () => { + let elapsed = (new Date().getTime() - startTime) * 0.001; + let fps = value.current * Math.pow(elapsed, -1); + let rounded = Math.round(fps * 100) * 0.01; + // account for https://stackoverflow.com/a/588014/356849 + let formatted = `${rounded}`.substring(0, 5); + + return formatted; + }; +}); + +/** + * Utility that will report the frequency of updates to tracked data. + * + * ```js + * import { UpdateFrequency } from 'ember-resources/util/fps'; + * + * export default class Demo extends Component { + * @tracked someProp; + * + * @use updateFrequency = UpdateFrequency(() => this.someProp); + * + * + * } + * ``` + * + * NOTE: the function passed to UpdateFrequency may not set tracked data. + */ +export const UpdateFrequency = resourceFactory((ofWhat: () => void, updateInterval: number) => { + updateInterval ||= 500; + + let multiplier = 1000 / updateInterval; + let framesSinceUpdate = 0; + + return resource(({ on }) => { + let value = cell(0); + let interval = setInterval(() => { + value.current = framesSinceUpdate * multiplier; + framesSinceUpdate = 0; + }, updateInterval); + + on.cleanup(() => clearInterval(interval)); + + return () => { + ofWhat(); + framesSinceUpdate++; + + return value.current; + }; + }); +}); diff --git a/test-app/tests/type-tests/types.ts b/test-app/tests/type-tests/types.ts index 1ecda30b4..59a2de940 100644 --- a/test-app/tests/type-tests/types.ts +++ b/test-app/tests/type-tests/types.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ /* eslint-disable @typescript-eslint/ban-types */ -import { expectType, type TypeEqual } from 'ts-expect'; +import { expectType,type TypeEqual } from 'ts-expect'; import type { ExpandArgs } from 'ember-resources';