155 lines
2.9 KiB
TypeScript
155 lines
2.9 KiB
TypeScript
![]() |
import { EventSlot } from '../events/EventSlot';
|
||
|
import { AnimationFrameEvent } from './AnimationFrameEvent';
|
||
|
import { sleep } from './sleep';
|
||
|
|
||
|
export type Idle = "Idle";
|
||
|
export type Running = "Running";
|
||
|
export type Stopping = "Stopping";
|
||
|
|
||
|
export type OnAnimationFrameStatus = Idle | Running | Stopping;
|
||
|
|
||
|
|
||
|
export class OnAnimationFrame extends EventSlot<AnimationFrameEvent>
|
||
|
{
|
||
|
static readonly Idle:Idle = "Idle";
|
||
|
static readonly Running:Running = "Running";
|
||
|
static readonly Stopping:Stopping = "Stopping";
|
||
|
|
||
|
#status:OnAnimationFrameStatus = OnAnimationFrame.Idle;
|
||
|
get status(){ return this.#status; }
|
||
|
|
||
|
#stopFlag = false;
|
||
|
#updater:()=>void = null;
|
||
|
#animationFrameEvent = new AnimationFrameEvent();
|
||
|
|
||
|
|
||
|
constructor()
|
||
|
{
|
||
|
super();
|
||
|
this.#animationFrameEvent.onAnimationFrame = this;
|
||
|
}
|
||
|
|
||
|
doUntil( condition:()=>boolean, callback:( e:AnimationFrameEvent )=>void )
|
||
|
{
|
||
|
let conditionalCallback = ( e:AnimationFrameEvent )=>
|
||
|
{
|
||
|
callback( e );
|
||
|
|
||
|
if ( ! condition() )
|
||
|
{
|
||
|
this.removeListener( conditionalCallback );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.addListener( conditionalCallback );
|
||
|
}
|
||
|
|
||
|
doOnceWhen( condition:()=>boolean, callback:( e:AnimationFrameEvent )=>void )
|
||
|
{
|
||
|
let conditionalCallback = ( e:AnimationFrameEvent )=>
|
||
|
{
|
||
|
if ( ! condition() )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
callback( e );
|
||
|
this.removeListener( conditionalCallback );
|
||
|
|
||
|
}
|
||
|
|
||
|
this.addListener( conditionalCallback );
|
||
|
}
|
||
|
|
||
|
run()
|
||
|
{
|
||
|
if ( OnAnimationFrame.Running === this.#status )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this._schedulRunning();
|
||
|
}
|
||
|
|
||
|
private async _schedulRunning()
|
||
|
{
|
||
|
if ( ! this.#updater )
|
||
|
{
|
||
|
this._createUpdaterFunction();
|
||
|
}
|
||
|
|
||
|
if ( OnAnimationFrame.Idle === this.#status )
|
||
|
{
|
||
|
this._startUpdaterLoop();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
let currentState = this.#status as OnAnimationFrameStatus;
|
||
|
let maxTries = 1000;
|
||
|
|
||
|
while ( OnAnimationFrame.Idle !== currentState )
|
||
|
{
|
||
|
maxTries--;
|
||
|
|
||
|
if ( maxTries <= 0 )
|
||
|
{
|
||
|
return Promise.reject();
|
||
|
}
|
||
|
|
||
|
await sleep( 1 );
|
||
|
currentState = this.#status as OnAnimationFrameStatus;
|
||
|
|
||
|
if ( OnAnimationFrame.Idle === currentState )
|
||
|
{
|
||
|
this._startUpdaterLoop();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
private _startUpdaterLoop()
|
||
|
{
|
||
|
this.#stopFlag = false;
|
||
|
this.#status = OnAnimationFrame.Running;
|
||
|
this.#updater();
|
||
|
}
|
||
|
|
||
|
private _createUpdaterFunction()
|
||
|
{
|
||
|
this.#updater =
|
||
|
|
||
|
( ) =>
|
||
|
{
|
||
|
this._update();
|
||
|
|
||
|
if ( this.#stopFlag )
|
||
|
{
|
||
|
this.#stopFlag = false;
|
||
|
this.#status = OnAnimationFrame.Idle;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
requestAnimationFrame( this.#updater );
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
private _update()
|
||
|
{
|
||
|
|
||
|
this.#animationFrameEvent.update();
|
||
|
|
||
|
this.dispatch( this.#animationFrameEvent );
|
||
|
}
|
||
|
|
||
|
stop()
|
||
|
{
|
||
|
this.#stopFlag = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|