1+ global . zonedCallback = function ( callback ) {
2+ if ( global . zone ) {
3+ // Zone v0.5.* style callback wrapping
4+ return global . zone . bind ( callback ) ;
5+ }
6+ if ( global . Zone ) {
7+ // Zone v0.6.* style callback wrapping
8+ return global . Zone . current . wrap ( callback ) ;
9+ } else {
10+ return callback ;
11+ }
12+ } ;
13+
14+
15+ class FPSCallback {
16+ constructor ( onFrame ) {
17+
18+ this . impl = null
19+ this . onFrame
20+ this . running = false
21+ this . sdkVersion = 0
22+ this . nativeFramesSupported = false
23+ this . running = false ;
24+ this . onFrame = onFrame ;
25+
26+ this . sdkVersion = parseInt ( android . os . Build . VERSION . SDK ) ;
27+ this . nativeFramesSupported = this . sdkVersion >= 24 && this . _isNativeFramesSupported ( ) ;
28+
29+ if ( this . nativeFramesSupported ) {
30+ this . impl = ( nanos ) => {
31+ this . handleFrame ( nanos ) ;
32+ } ;
33+
34+ console . log ( 'impl' , this . impl ) ;
35+ } else {
36+ this . impl = new android . view . Choreographer . FrameCallback ( {
37+ doFrame : ( nanos ) => {
38+ this . handleFrame ( nanos ) ;
39+ } ,
40+ } ) ;
41+ }
42+ }
43+
44+ _isNativeFramesSupported ( ) {
45+ return typeof global . __postFrameCallback === 'function' && typeof global . __removeFrameCallback === 'function' ;
46+ }
47+
48+ start ( ) {
49+ if ( this . running ) {
50+ return ;
51+ }
52+
53+ if ( this . nativeFramesSupported ) {
54+ global . __postFrameCallback ( this . impl ) ;
55+ } else {
56+ android . view . Choreographer . getInstance ( ) . postFrameCallback ( this . impl ) ;
57+ }
58+
59+ this . running = true ;
60+ }
61+
62+ stop ( ) {
63+ if ( ! this . running ) {
64+ return ;
65+ }
66+
67+ if ( this . nativeFramesSupported ) {
68+ global . __removeFrameCallback ( this . impl ) ;
69+ } else {
70+ android . view . Choreographer . getInstance ( ) . removeFrameCallback ( this . impl ) ;
71+ }
72+
73+ this . running = false ;
74+ }
75+
76+ handleFrame ( nanos ) {
77+ if ( ! this . running ) {
78+ return ;
79+ }
80+
81+ // divide by 1 000 000 since the parameter is in nanoseconds
82+ this . onFrame ( nanos / 1000000 ) ;
83+ // add the FrameCallback instance again since it is automatically removed from the Choreographer
84+
85+ if ( this . nativeFramesSupported ) {
86+ global . __postFrameCallback ( this . impl ) ;
87+ } else {
88+ android . view . Choreographer . getInstance ( ) . postFrameCallback ( this . impl ) ;
89+ }
90+ }
91+ }
92+
93+
94+ function getTimeInFrameBase ( ) {
95+ return java . lang . System . nanoTime ( ) / 1000000 ;
96+ }
97+
98+
99+ function dispatchToMainThread ( func ) {
100+ const runOnMainThread = global . __runOnMainThread ;
101+ if ( runOnMainThread ) {
102+ runOnMainThread ( ( ) => {
103+ func ( ) ;
104+ } ) ;
105+ } else {
106+ new android . os . Handler ( android . os . Looper . getMainLooper ( ) ) . post (
107+ new java . lang . Runnable ( {
108+ run : func ,
109+ } )
110+ ) ;
111+ }
112+ }
113+
114+
115+ let scheduled = false ;
116+ let macroTaskQueue = [ ] ;
117+
118+ function drainMacrotaskQueue ( ) {
119+ const currentQueue = macroTaskQueue ;
120+ macroTaskQueue = [ ] ;
121+ scheduled = false ;
122+ currentQueue . forEach ( ( task ) => {
123+ try {
124+ task ( ) ;
125+ } catch ( err ) {
126+ const msg = err ? err . stack || err : err ;
127+ }
128+ } ) ;
129+ }
130+
131+ function queueMacrotask ( task ) {
132+ macroTaskQueue . push ( task ) ;
133+ if ( ! scheduled ) {
134+ scheduled = true ;
135+ dispatchToMainThread ( drainMacrotaskQueue ) ;
136+ }
137+ }
138+
139+ let animationId = 0 ;
140+ let currentFrameAnimationCallbacks = { } ; // requests that were scheduled in this frame and must be called ASAP
141+ let currentFrameScheduled = false ;
142+ let nextFrameAnimationCallbacks = { } ; // requests there were scheduled in another request and must be called in the next frame
143+ let shouldStop = true ;
144+ let inAnimationFrame = false ;
145+ let fpsCallback ;
146+ let lastFrameTime = 0 ;
147+
148+ function getNewId ( ) {
149+ return animationId ++ ;
150+ }
151+
152+ function ensureNative ( ) {
153+ if ( fpsCallback ) {
154+ return ;
155+ }
156+ fpsCallback = new FPSCallback ( doFrame ) ;
157+ }
158+
159+ function callAnimationCallbacks ( thisFrameCbs , frameTime ) {
160+ inAnimationFrame = true ;
161+ for ( const animationId in thisFrameCbs ) {
162+ if ( thisFrameCbs [ animationId ] ) {
163+ try {
164+ thisFrameCbs [ animationId ] ( frameTime ) ;
165+ } catch ( err ) {
166+ const msg = err ? err . stack || err : err ;
167+ }
168+ }
169+ }
170+ inAnimationFrame = false ;
171+ }
172+
173+ function doCurrentFrame ( ) {
174+ // if we're not getting accurate frame times
175+ // set last frame time as the current time
176+ if ( ! fpsCallback || ! fpsCallback . running ) {
177+ lastFrameTime = getTimeInFrameBase ( ) ;
178+ }
179+ currentFrameScheduled = false ;
180+ const thisFrameCbs = currentFrameAnimationCallbacks ;
181+ currentFrameAnimationCallbacks = { } ;
182+ callAnimationCallbacks ( thisFrameCbs , lastFrameTime ) ;
183+ }
184+
185+ function doFrame ( currentTimeMillis ) {
186+ lastFrameTime = currentTimeMillis ;
187+ shouldStop = true ;
188+ const thisFrameCbs = nextFrameAnimationCallbacks ;
189+ nextFrameAnimationCallbacks = { } ;
190+ callAnimationCallbacks ( thisFrameCbs , lastFrameTime ) ;
191+ if ( shouldStop ) {
192+ fpsCallback . stop ( ) ; // TODO: check performance without stopping to allow consistent frame times
193+ }
194+ }
195+
196+ function ensureCurrentFrameScheduled ( ) {
197+ if ( ! currentFrameScheduled ) {
198+ currentFrameScheduled = true ;
199+ queueMacrotask ( doCurrentFrame ) ;
200+ }
201+ }
202+
203+ function requestAnimationFrame ( cb ) {
204+ const animId = getNewId ( ) ;
205+ if ( ! inAnimationFrame ) {
206+ ensureCurrentFrameScheduled ( ) ;
207+ currentFrameAnimationCallbacks [ animId ] = zonedCallback ( cb ) ;
208+ return animId ;
209+ }
210+ ensureNative ( ) ;
211+ nextFrameAnimationCallbacks [ animId ] = zonedCallback ( cb ) ;
212+ shouldStop = false ;
213+ fpsCallback . start ( ) ;
214+
215+ return animId ;
216+ }
217+
218+ function cancelAnimationFrame ( id ) {
219+ delete currentFrameAnimationCallbacks [ id ] ;
220+ delete nextFrameAnimationCallbacks [ id ] ;
221+ }
222+
223+
224+ module . exports = {
225+ FPSCallback,
226+ getTimeInFrameBase,
227+ requestAnimationFrame,
228+ cancelAnimationFrame,
229+ queueMacrotask,
230+ dispatchToMainThread
231+ }
0 commit comments