three
Version:
JavaScript 3D library
221 lines (154 loc) • 4.55 kB
JavaScript
/**
* This class is an alternative to {@link Clock} with a different API design and behavior.
* The goal is to avoid the conceptual flaws that became apparent in `Clock` over time.
*
* - `Timer` has an `update()` method that updates its internal state. That makes it possible to
* call `getDelta()` and `getElapsed()` multiple times per simulation step without getting different values.
* - The class can make use of the Page Visibility API to avoid large time delta values when the app
* is inactive (e.g. tab switched or browser hidden).
*
* ```js
* const timer = new Timer();
* timer.connect( document ); // use Page Visibility API
* ```
*/
class Timer {
/**
* Constructs a new timer.
*/
constructor() {
this._previousTime = 0;
this._currentTime = 0;
this._startTime = now();
this._delta = 0;
this._elapsed = 0;
this._timescale = 1;
this._document = null;
this._pageVisibilityHandler = null;
}
/**
* Connect the timer to the given document.Calling this method is not mandatory to
* use the timer but enables the usage of the Page Visibility API to avoid large time
* delta values.
*
* @param {Document} document - The document.
*/
connect( document ) {
this._document = document;
// use Page Visibility API to avoid large time delta values
if ( document.hidden !== undefined ) {
this._pageVisibilityHandler = handleVisibilityChange.bind( this );
document.addEventListener( 'visibilitychange', this._pageVisibilityHandler, false );
}
}
/**
* Disconnects the timer from the DOM and also disables the usage of the Page Visibility API.
*/
disconnect() {
if ( this._pageVisibilityHandler !== null ) {
this._document.removeEventListener( 'visibilitychange', this._pageVisibilityHandler );
this._pageVisibilityHandler = null;
}
this._document = null;
}
/**
* Returns the time delta in seconds.
*
* @return {number} The time delta in second.
*/
getDelta() {
return this._delta / 1000;
}
/**
* Returns the elapsed time in seconds.
*
* @return {number} The elapsed time in second.
*/
getElapsed() {
return this._elapsed / 1000;
}
/**
* Returns the timescale.
*
* @return {number} The timescale.
*/
getTimescale() {
return this._timescale;
}
/**
* Sets the given timescale which scale the time delta computation
* in `update()`.
*
* @param {number} timescale - The timescale to set.
* @return {Timer} A reference to this timer.
*/
setTimescale( timescale ) {
this._timescale = timescale;
return this;
}
/**
* Resets the time computation for the current simulation step.
*
* @return {Timer} A reference to this timer.
*/
reset() {
this._currentTime = now() - this._startTime;
return this;
}
/**
* Can be used to free all internal resources. Usually called when
* the timer instance isn't required anymore.
*/
dispose() {
this.disconnect();
}
/**
* Updates the internal state of the timer. This method should be called
* once per simulation step and before you perform queries against the timer
* (e.g. via `getDelta()`).
*
* @param {number} timestamp - The current time in milliseconds. Can be obtained
* from the `requestAnimationFrame` callback argument. If not provided, the current
* time will be determined with `performance.now`.
* @return {Timer} A reference to this timer.
*/
update( timestamp ) {
if ( this._pageVisibilityHandler !== null && this._document.hidden === true ) {
this._delta = 0;
} else {
this._previousTime = this._currentTime;
this._currentTime = ( timestamp !== undefined ? timestamp : now() ) - this._startTime;
this._delta = ( this._currentTime - this._previousTime ) * this._timescale;
this._elapsed += this._delta; // _elapsed is the accumulation of all previous deltas
}
return this;
}
}
/**
* A special version of a timer with a fixed time delta value.
* Can be useful for testing and debugging purposes.
*
* @augments Timer
*/
class FixedTimer extends Timer {
/**
* Constructs a new timer.
*
* @param {number} [fps=60] - The fixed FPS of this timer.
*/
constructor( fps = 60 ) {
super();
this._delta = ( 1 / fps ) * 1000;
}
update() {
this._elapsed += ( this._delta * this._timescale ); // _elapsed is the accumulation of all previous deltas
return this;
}
}
function now() {
return performance.now();
}
function handleVisibilityChange() {
if ( this._document.hidden === false ) this.reset();
}
export { Timer, FixedTimer };