library-ts/browser/animation/OnAnimationFrame.ts

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;
}
}