1+ import { queueMacrotask } from '../utils/macrotask-scheduler' ;
12import { FPSCallback } from '../fps-meter/fps-native' ;
23import { getTimeInFrameBase } from './animation-native' ;
4+ import { Trace } from 'trace' ;
35
46export interface FrameRequestCallback {
57 ( time : number ) : void ;
68}
79
10+ type AnimationFrameCallbacks = { [ key : string ] : FrameRequestCallback } ;
11+
812let animationId = 0 ;
9- let nextFrameAnimationCallbacks : { [ key : string ] : FrameRequestCallback } = { } ;
13+ let currentFrameAnimationCallbacks : AnimationFrameCallbacks = { } ; // requests that were scheduled in this frame and must be called ASAP
14+ let currentFrameScheduled = false ;
15+ let nextFrameAnimationCallbacks : AnimationFrameCallbacks = { } ; // requests there were scheduled in another request and must be called in the next frame
1016let shouldStop = true ;
1117let inAnimationFrame = false ;
1218let fpsCallback : FPSCallback ;
@@ -23,40 +29,67 @@ function ensureNative() {
2329 fpsCallback = new FPSCallback ( doFrame ) ;
2430}
2531
26- function doFrame ( currentTimeMillis : number ) {
27- lastFrameTime = currentTimeMillis ;
28- shouldStop = true ;
29- const thisFrameCbs = nextFrameAnimationCallbacks ;
30- nextFrameAnimationCallbacks = { } ;
32+ function callAnimationCallbacks ( thisFrameCbs : AnimationFrameCallbacks , frameTime : number ) : void {
3133 inAnimationFrame = true ;
3234 for ( const animationId in thisFrameCbs ) {
3335 if ( thisFrameCbs [ animationId ] ) {
34- thisFrameCbs [ animationId ] ( lastFrameTime ) ;
36+ try {
37+ thisFrameCbs [ animationId ] ( frameTime ) ;
38+ } catch ( err ) {
39+ const msg = err ? err . stack || err : err ;
40+ Trace . write ( `Error in requestAnimationFrame: ${ msg } ` , Trace . categories . Error , Trace . messageType . error ) ;
41+ }
3542 }
3643 }
3744 inAnimationFrame = false ;
45+ }
46+
47+ function doCurrentFrame ( ) {
48+ // if we're not getting accurate frame times
49+ // set last frame time as the current time
50+ if ( ! fpsCallback || ! fpsCallback . running ) {
51+ lastFrameTime = getTimeInFrameBase ( ) ;
52+ }
53+ currentFrameScheduled = false ;
54+ const thisFrameCbs = currentFrameAnimationCallbacks ;
55+ currentFrameAnimationCallbacks = { } ;
56+ callAnimationCallbacks ( thisFrameCbs , lastFrameTime ) ;
57+ }
58+
59+ function doFrame ( currentTimeMillis : number ) {
60+ lastFrameTime = currentTimeMillis ;
61+ shouldStop = true ;
62+ const thisFrameCbs = nextFrameAnimationCallbacks ;
63+ nextFrameAnimationCallbacks = { } ;
64+ callAnimationCallbacks ( thisFrameCbs , lastFrameTime ) ;
3865 if ( shouldStop ) {
3966 fpsCallback . stop ( ) ; // TODO: check performance without stopping to allow consistent frame times
4067 }
4168}
4269
70+ function ensureCurrentFrameScheduled ( ) {
71+ if ( ! currentFrameScheduled ) {
72+ currentFrameScheduled = true ;
73+ queueMacrotask ( doCurrentFrame ) ;
74+ }
75+ }
76+
4377export function requestAnimationFrame ( cb : FrameRequestCallback ) : number {
78+ const animId = getNewId ( ) ;
4479 if ( ! inAnimationFrame ) {
45- inAnimationFrame = true ;
46- zonedCallback ( cb ) ( getTimeInFrameBase ( ) ) ; // TODO: store and use lastFrameTime
47- inAnimationFrame = false ;
48-
49- return getNewId ( ) ;
80+ ensureCurrentFrameScheduled ( ) ;
81+ currentFrameAnimationCallbacks [ animId ] = zonedCallback ( cb ) as FrameRequestCallback ;
82+ return animId ;
5083 }
5184 ensureNative ( ) ;
52- const animId = getNewId ( ) ;
5385 nextFrameAnimationCallbacks [ animId ] = zonedCallback ( cb ) as FrameRequestCallback ;
5486 shouldStop = false ;
5587 fpsCallback . start ( ) ;
5688
5789 return animId ;
5890}
5991
60- export function cancelAnimationFrame ( id : number ) {
92+ export function cancelAnimationFrame ( id : number ) : void {
93+ delete currentFrameAnimationCallbacks [ id ] ;
6194 delete nextFrameAnimationCallbacks [ id ] ;
6295}
0 commit comments