interface Effect { init ( width , height ) : Promise < void > ; render ( ctx , time ) : void ; } interface LoadedEffect { effect : Effect ; ctx : CanvasRenderingContext2D | null ; } class DemoFX { private effects : { [ name ] : LoadedEffect ; } = { } ; private runningEl : HTMLInputElement | null = null ; private running : number | null = null ; public register ( name , effect ) { if ( this . effects [ name ] != undefined ) { throw new Error ( "Effect '" + name + "' is already registered." ) ; } this . effects [ name ] = { effect , ctx : null } ; } public async loadImage ( path ) { return new Promise < ImageData > ( ( resolve ) => { const img = document . createElement ( "img" ) ; img . onload = ( ) => { const width = img . width ; const height = img . height ; const canvas = document . createElement ( "canvas" ) ; canvas . width = width ; canvas . height = height ; document . body . appendChild ( canvas ) ; const ctx = canvas . getContext ( "2d" ) ; if ( ctx != null ) { ctx . drawImage ( img , 0 , 0 , width , height ) ; const pixels = ctx . getImageData ( 0 , 0 , width , height ) ; document . body . removeChild ( canvas ) ; resolve ( pixels ) ; } } ; img . src = path ; } ) ; } public async toggle ( name , el ) { const fx = this . effects [ name ] ; if ( fx == undefined ) { throw new Error ( "Effect not found: " + name ) ; } if ( fx . ctx == null ) { const canvas = document . getElementById ( name ) as HTMLCanvasElement | null ; if ( canvas == null ) { throw new Error ( "Canvas not found: " + name ) ; } const ctx = canvas . getContext ( "2d" ) ; if ( ctx == null ) { throw new Error ( "No canvas suppport" ) ; } fx . ctx = ctx ; await fx . effect . init ( canvas . width , canvas . height ) ; } if ( this . running != null ) { window . cancelAnimationFrame ( this . running ) ; this . running = null ; if ( this . runningEl != null ) { this . runningEl . value = "Run" ; } } if ( el == this . runningEl ) { this . runningEl = null ; return ; } el . value = "Pause" ; this . runningEl = el ; const render = ( time ) : void => { if ( fx . ctx != null ) { fx . effect . render ( fx . ctx , time ) ; this . running = window . requestAnimationFrame ( render ) ; } else { this . running = null ; } } ; this . running = window . requestAnimationFrame ( render ) ; } } const demoFX = new DemoFX ( ) ; async function toggle ( name , el ) { await demoFX . toggle ( name , el ) ; }