3 vendors
= ['ms', 'moz', 'webkit', 'o'],
7 for (i
= 0; i
< ln
&& !window
.requestAnimationFrame
; ++i
) {
9 if (window
[vendor
+ 'RequestAnimationFrame']) {
10 window
.requestAnimationFrame
= window
[vendor
+ 'RequestAnimationFrame'];
11 window
.cancelAnimationFrame
= window
[vendor
+ 'CancelAnimationFrame'] || window
[vendor
+ 'CancelRequestAnimationFrame'];
20 if (window
.performance
&& window
.performance
.now
) {
21 Ext
.performance
.now = function() {
22 return window
.performance
.now();
26 Ext
.performance
.now = function() {
31 if (!window
.requestAnimationFrame
) {
32 window
.requestAnimationFrame = function(callback
) {
33 var currTime
= Ext
.performance
.now(),
34 timeToCall
= Math
.max(0, 16 - (currTime
- lastTime
)),
35 id
= window
.setTimeout(function() {
36 callback(currTime
+ timeToCall
);
38 lastTime
= currTime
+ timeToCall
;
43 Ext
.trueRequestAnimationFrames
= true;
46 if (!window
.cancelAnimationFrame
) {
47 window
.cancelAnimationFrame = function(id
) {
58 Ext
.define('Ext.AnimationQueue', {
61 constructor: function() {
62 var bind
= Ext
.Function
.bind
;
66 this.runningQueue
= [];
68 this.isRunning
= false;
71 this.run
= bind(this.run
, this);
72 this.whenIdle
= bind(this.whenIdle
, this);
73 this.processIdleQueueItem
= bind(this.processIdleQueueItem
, this);
74 this.processTaskQueueItem
= bind(this.processTaskQueueItem
, this);
77 // iOS has a nasty bug which causes pending requestAnimationFrame to not release
78 // the callback when the WebView is switched back and forth from / to being background process
79 // We use a watchdog timer to workaround this, and restore the pending state correctly if this happens
80 // This timer has to be set as an interval from the very beginning and we have to keep it running for
81 // as long as the app lives, setting it later doesn't seem to work
83 setInterval(this.watch
, 500);
89 * @param {Function} fn
90 * @param {Object} [scope]
91 * @param {Object} [args]
93 start: function(fn
, scope
, args
) {
94 this.queue
.push(arguments
);
96 if (!this.isRunning
) {
97 if (this.hasOwnProperty('idleTimer')) {
98 clearTimeout(this.idleTimer
);
99 delete this.idleTimer
;
102 if (this.hasOwnProperty('idleQueueTimer')) {
103 clearTimeout(this.idleQueueTimer
);
104 delete this.idleQueueTimer
;
108 this.isRunning
= true;
110 this.startCountTime
= Ext
.performance
.now();
118 if (this.isRunning
&& Date
.now() - this.lastRunTime
>= 500) {
124 if (!this.isRunning
) {
128 var queue
= this.runningQueue
,
131 this.lastRunTime
= Date
.now();
132 this.frameStartTime
= Ext
.performance
.now();
134 queue
.push
.apply(queue
, this.queue
);
136 for (i
= 0, ln
= queue
.length
; i
< ln
; i
++) {
137 this.invoke(queue
[i
]);
143 var now
= this.frameStartTime
,
144 startCountTime
= this.startCountTime
,
145 elapse
= now
- startCountTime
,
146 count
= ++this.count
;
149 this.onFpsChanged(count
* 1000 / elapse
, count
, elapse
);
150 this.startCountTime
= now
;
159 onFpsChanged
: Ext
.emptyFn
,
164 doStart: function() {
165 this.animationFrameId
= requestAnimationFrame(this.run
);
166 this.lastRunTime
= Date
.now();
169 doIterate: function() {
170 this.animationFrameId
= requestAnimationFrame(this.run
);
174 cancelAnimationFrame(this.animationFrameId
);
179 * @param {Function} fn
180 * @param {Object} [scope]
181 * @param {Object} [args]
183 stop: function(fn
, scope
, args
) {
184 if (!this.isRunning
) {
188 var queue
= this.queue
,
192 for (i
= 0; i
< ln
; i
++) {
194 if (item
[0] === fn
&& item
[1] === scope
&& item
[2] === args
) {
206 this.isRunning
= false;
208 this.idleTimer
= setTimeout(this.whenIdle
, 100);
212 onIdle: function(fn
, scope
, args
) {
213 var listeners
= this.idleQueue
,
216 for (i
= 0, ln
= listeners
.length
; i
< ln
; i
++) {
217 listener
= listeners
[i
];
218 if (fn
=== listener
[0] && scope
=== listener
[1] && args
=== listener
[2]) {
223 listeners
.push(arguments
);
226 this.processIdleQueue();
230 unIdle: function(fn
, scope
, args
) {
231 var listeners
= this.idleQueue
,
234 for (i
= 0, ln
= listeners
.length
; i
< ln
; i
++) {
235 listener
= listeners
[i
];
236 if (fn
=== listener
[0] && scope
=== listener
[1] && args
=== listener
[2]) {
237 listeners
.splice(i
, 1);
245 queueTask: function(fn
, scope
, args
) {
246 this.taskQueue
.push(arguments
);
247 this.processTaskQueue();
250 dequeueTask: function(fn
, scope
, args
) {
251 var listeners
= this.taskQueue
,
254 for (i
= 0, ln
= listeners
.length
; i
< ln
; i
++) {
255 listener
= listeners
[i
];
256 if (fn
=== listener
[0] && scope
=== listener
[1] && args
=== listener
[2]) {
257 listeners
.splice(i
, 1);
264 invoke: function(listener
) {
265 var fn
= listener
[0],
269 fn
= (typeof fn
== 'string' ? scope
[fn
] : fn
);
271 if (Ext
.isArray(args
)) {
272 fn
.apply(scope
, args
);
275 fn
.call(scope
, args
);
279 whenIdle: function() {
281 this.processIdleQueue();
284 processIdleQueue: function() {
285 if (!this.hasOwnProperty('idleQueueTimer')) {
286 this.idleQueueTimer
= setTimeout(this.processIdleQueueItem
, 1);
290 processIdleQueueItem: function() {
291 delete this.idleQueueTimer
;
297 var listeners
= this.idleQueue
,
300 if (listeners
.length
> 0) {
301 listener
= listeners
.shift();
302 this.invoke(listener
);
303 this.processIdleQueue();
307 processTaskQueue: function() {
308 if (!this.hasOwnProperty('taskQueueTimer')) {
309 this.taskQueueTimer
= setTimeout(this.processTaskQueueItem
, 15);
313 processTaskQueueItem: function() {
314 delete this.taskQueueTimer
;
316 var listeners
= this.taskQueue
,
319 if (listeners
.length
> 0) {
320 listener
= listeners
.shift();
321 this.invoke(listener
);
322 this.processTaskQueue();
326 showFps: function() {
327 if (!Ext
.trueRequestAnimationFrames
) {
328 alert("This browser does not support requestAnimationFrame. The FPS listed will not be accurate");
330 Ext
.onReady(function() {
338 style
: 'background-color: black; color: white; text-align: center; line-height: 20px; font-size: 8px;'
348 style
: 'background-color: red; color: white; text-align: center; line-height: 50px;'
356 html
: 'Min (Last 1k)',
357 style
: 'background-color: black; color: white; text-align: center; line-height: 20px; font-size: 8px;'
367 style
: 'background-color: orange; color: white; text-align: center; line-height: 50px;'
375 html
: 'Max (Last 1k)',
376 style
: 'background-color: black; color: white; text-align: center; line-height: 20px; font-size: 8px;'
386 style
: 'background-color: yellow; color: black; text-align: center; line-height: 50px;'
395 style
: 'background-color: black; color: white; text-align: center; line-height: 20px; font-size: 8px;'
405 style
: 'background-color: green; color: white; text-align: center; line-height: 50px;'
408 Ext
.AnimationQueue
.resetFps();
413 resetFps: function() {
414 var currentFps
= Ext
.getCmp('__currentFps'),
415 averageFps
= Ext
.getCmp('__averageFps'),
416 minFps
= Ext
.getCmp('__minFps'),
417 maxFps
= Ext
.getCmp('__maxFps'),
423 Ext
.AnimationQueue
.onFpsChanged = function(fps
) {
432 min
= Math
.min(min
, fps
);
433 max
= Math
.max(max
, fps
);
434 currentFps
.setHtml(Math
.round(fps
));
435 averageFps
.setHtml(Math
.round(sum
/ count
));
436 minFps
.setHtml(Math
.round(min
));
437 maxFps
.setHtml(Math
.round(max
));
442 Global FPS indicator. Add ?showfps to use in any application. Note that this REQUIRES true requestAnimationFrame
446 var paramsString
= window
.location
.search
.substr(1),
447 paramsArray
= paramsString
.split("&");
449 if (paramsArray
.indexOf("showfps") !== -1) {
450 Ext
.AnimationQueue
.showFps();