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