diff --git a/examples/jsm/misc/Timer.js b/examples/jsm/misc/Timer.js new file mode 100644 index 00000000000000..32004a7b388573 --- /dev/null +++ b/examples/jsm/misc/Timer.js @@ -0,0 +1,144 @@ +class Timer { + + constructor() { + + this._previousTime = 0; + this._currentTime = 0; + this._startTime = now(); + + this._delta = 0; + this._elapsed = 0; + + this._timescale = 1; + + this._useFixedDelta = false; + this._fixedDelta = 16.67; // ms, corresponds to approx. 60 FPS + + // use Page Visibility API to avoid large time delta values + + this._usePageVisibilityAPI = ( typeof document !== 'undefined' && document.hidden !== undefined ); + + if ( this._usePageVisibilityAPI === true ) { + + this._pageVisibilityHandler = handleVisibilityChange.bind( this ); + + document.addEventListener( 'visibilitychange', this._pageVisibilityHandler, false ); + + } + + } + + disableFixedDelta() { + + this._useFixedDelta = false; + + return this; + + } + + dispose() { + + if ( this._usePageVisibilityAPI === true ) { + + document.removeEventListener( 'visibilitychange', this._pageVisibilityHandler ); + + } + + return this; + + } + + enableFixedDelta() { + + this._useFixedDelta = true; + + return this; + + } + + getDelta() { + + return this._delta / 1000; + + } + + getElapsed() { + + return this._elapsed / 1000; + + } + + getFixedDelta() { + + return this._fixedDelta / 1000; + + } + + getTimescale() { + + return this._timescale; + + } + + reset() { + + this._currentTime = now() - this._startTime; + + return this; + + } + + setFixedDelta( fixedDelta ) { + + this._fixedDelta = fixedDelta * 1000; + + return this; + + } + + setTimescale( timescale ) { + + this._timescale = timescale; + + return this; + + } + + update() { + + if ( this._useFixedDelta === true ) { + + this._delta = this._fixedDelta; + + } else { + + this._previousTime = this._currentTime; + this._currentTime = now() - this._startTime; + + this._delta = this._currentTime - this._previousTime; + + } + + this._delta *= this._timescale; + + this._elapsed += this._delta; // _elapsed is the accumulation of all previous deltas + + return this; + + } + +} + +function now() { + + return ( typeof performance === 'undefined' ? Date : performance ).now(); + +} + +function handleVisibilityChange() { + + if ( document.hidden === false ) this.reset(); + +} + +export { Timer }; diff --git a/examples/webgl_morphtargets_sphere.html b/examples/webgl_morphtargets_sphere.html index 877dac514d968b..e34aaa47726a71 100644 --- a/examples/webgl_morphtargets_sphere.html +++ b/examples/webgl_morphtargets_sphere.html @@ -28,8 +28,9 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + import { Timer } from 'three/addons/misc/Timer.js'; - let camera, scene, renderer, clock; + let camera, scene, renderer, timer; let mesh; @@ -48,7 +49,7 @@ scene = new THREE.Scene(); - clock = new THREE.Clock(); + timer = new Timer(); const light1 = new THREE.PointLight( 0xff2200, 50000 ); light1.position.set( 100, 100, 100 ); @@ -102,8 +103,6 @@ window.addEventListener( 'resize', onWindowResize ); - document.addEventListener( 'visibilitychange', onVisibilityChange ); - } function onWindowResize() { @@ -115,30 +114,17 @@ } - function onVisibilityChange() { - - if ( document.hidden === true ) { - - clock.stop(); - - } else { - - clock.start(); - - } - - } - function animate() { requestAnimationFrame( animate ); + timer.update(); render(); } function render() { - const delta = clock.getDelta(); + const delta = timer.getDelta(); if ( mesh !== undefined ) {