Skip to content

Commit 6d43dc4

Browse files
committed
Effects: stabilize rAF logic & align timeout logic with it
rAF logic was introduced almost three years ago relative to this commit, as a primary method for scheduling animation (see gh-1578 pull). With it there was two substantial changes - one was explicitly mentioned and the other was not. First, if browser window was hidden aka `document.hidden === true` it would immediately execute all scheduled animation without waiting for time pass i.e. tick time become `0` instead of 13 ms of a default value. Which created possibility for circular executions in case if `complete` method executed the same animation (see gh-3434 issue). And the second one - since then there was two ways of scheduling animation: with `setInterval` and `requestAnimationFrame`, but there was a difference in their execution. In case of `setInterval` it waited default `jQuery.fx.interval` value before actually starting the new tick, not counting the first step which wasn't set to be executed through tick method (aka `jQuery.fx.tick`). Whereas `requestAnimationFrame` first scheduled the call and executed the `step` method right after that, counting the first call of `jQuery.fx.timer`, `tick` was happening twice in one frame. But since tests explicitly disabled rAF method i.e. `requestAnimationFrame = null` and checking only `setInterval` logic, since it's impossible to do it otherwise - we missed that change. Faulty logic also was presented with `cancelAnimationFrame`, which couldn't clear any timers since `raf` scheduler didn't define new `timerId` value. Because that change was so subtle, apparently no user noticed it proving that both `cancelAnimationFrame` and `clearInterval` code paths are redundant. Since `cancelAnimationFrame` didn't work properly and rAF is and was a primary used code path, plus the same approach is used in other popular animation libs. Therefore those code paths were removed. These changes also replace two different functions which schedule the animation with one, which checks what type of logic should be used and executes it appropriatley, but for secondary path it now uses `setTimeout` making it more consistent with rAF path. Since ticks are happening globally we also don't require to listen `visibilitychange` event. It also changes the way how first call is scheduled so execution of animation will not happen twice in one frame. No new tests were not introduced, since now `setTimeout` logic should be equivalent to the rAF one, but one test was changed since now we actually execute animation at the first tick. Fixes gh-3434 Closes gh-3559
1 parent ac9e301 commit 6d43dc4

File tree

2 files changed

+21
-37
lines changed

2 files changed

+21
-37
lines changed

src/effects.js

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,18 @@ define( [
2323
"use strict";
2424

2525
var
26-
fxNow, timerId,
26+
fxNow, inProgress,
2727
rfxtypes = /^(?:toggle|show|hide)$/,
2828
rrun = /queueHooks$/;
2929

30-
function raf() {
31-
if ( timerId ) {
32-
window.requestAnimationFrame( raf );
30+
function schedule() {
31+
if ( inProgress ) {
32+
if ( document.hidden === false && window.requestAnimationFrame ) {
33+
window.requestAnimationFrame( schedule );
34+
} else {
35+
window.setTimeout( schedule, jQuery.fx.interval );
36+
}
37+
3338
jQuery.fx.tick();
3439
}
3540
}
@@ -458,8 +463,8 @@ jQuery.speed = function( speed, easing, fn ) {
458463
easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
459464
};
460465

461-
// Go to the end state if fx are off or if document is hidden
462-
if ( jQuery.fx.off || document.hidden ) {
466+
// Go to the end state if fx are off
467+
if ( jQuery.fx.off ) {
463468
opt.duration = 0;
464469

465470
} else {
@@ -664,36 +669,22 @@ jQuery.fx.tick = function() {
664669
};
665670

666671
jQuery.fx.timer = function( timer ) {
667-
var i = jQuery.timers.push( timer ) - 1,
668-
timers = jQuery.timers;
669-
670-
if ( timer() ) {
671-
jQuery.fx.start();
672-
673-
// If the timer finished immediately, safely remove it (allowing for external removal)
674-
// Use a superfluous post-decrement for better compressibility w.r.t. jQuery.fx.tick above
675-
} else if ( timers[ i ] === timer ) {
676-
timers.splice( i--, 1 );
677-
}
672+
jQuery.timers.push( timer );
673+
jQuery.fx.start();
678674
};
679675

680676
jQuery.fx.interval = 13;
681677
jQuery.fx.start = function() {
682-
if ( !timerId ) {
683-
timerId = window.requestAnimationFrame ?
684-
window.requestAnimationFrame( raf ) :
685-
window.setInterval( jQuery.fx.tick, jQuery.fx.interval );
678+
if ( inProgress ) {
679+
return;
686680
}
681+
682+
inProgress = true;
683+
schedule();
687684
};
688685

689686
jQuery.fx.stop = function() {
690-
if ( window.cancelAnimationFrame ) {
691-
window.cancelAnimationFrame( timerId );
692-
} else {
693-
window.clearInterval( timerId );
694-
}
695-
696-
timerId = null;
687+
inProgress = null;
697688
};
698689

699690
jQuery.fx.speeds = {

test/unit/effects.js

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1847,12 +1847,12 @@ QUnit.test( "non-px animation handles non-numeric start (#11971)", function( ass
18471847
} );
18481848

18491849
QUnit.test( "Animation callbacks (#11797)", function( assert ) {
1850-
assert.expect( 16 );
1850+
assert.expect( 15 );
18511851

18521852
var prog = 0,
18531853
targets = jQuery( "#foo" ).children(),
18541854
done = false,
1855-
expectedProgress = 0;
1855+
expectedProgress = 1;
18561856

18571857
targets.eq( 0 ).animate( {}, {
18581858
duration: 1,
@@ -1910,14 +1910,7 @@ QUnit.test( "Animation callbacks (#11797)", function( assert ) {
19101910
assert.ok( true, "async: start" );
19111911
},
19121912
progress: function( anim, percent ) {
1913-
1914-
// occasionally the progress handler is called twice in first frame.... *shrug*
1915-
if ( percent === 0 && expectedProgress === 1 ) {
1916-
return;
1917-
}
19181913
assert.equal( percent, expectedProgress, "async: progress " + expectedProgress );
1919-
1920-
// once at 0, once at 1
19211914
expectedProgress++;
19221915
},
19231916
done: function() {

0 commit comments

Comments
 (0)