Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit dc48aad

Browse files
committed
fix(ngAnimate): only buffer rAF requests within the animation runners
Closes #12280
1 parent ebce2f7 commit dc48aad

16 files changed

+269
-220
lines changed

src/ngAnimate/animateJs.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
// by the time...
66

77
var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
8-
this.$get = ['$injector', '$$AnimateRunner', '$$rAFMutex', '$$jqLite',
9-
function($injector, $$AnimateRunner, $$rAFMutex, $$jqLite) {
8+
this.$get = ['$injector', '$$AnimateRunner', '$$jqLite',
9+
function($injector, $$AnimateRunner, $$jqLite) {
1010

1111
var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
1212
// $animateJs(element, 'enter');

src/ngAnimate/animateRunner.js

+21-6
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,33 @@
11
'use strict';
22

3-
var $$rAFMutexFactory = ['$$rAF', function($$rAF) {
3+
var $$AnimateAsyncRunFactory = ['$$rAF', function($$rAF) {
4+
var waitQueue = [];
5+
6+
function waitForTick(fn) {
7+
waitQueue.push(fn);
8+
if (waitQueue.length > 1) return;
9+
$$rAF(function() {
10+
for (var i = 0; i < waitQueue.length; i++) {
11+
waitQueue[i]();
12+
}
13+
waitQueue = [];
14+
});
15+
}
16+
417
return function() {
518
var passed = false;
6-
$$rAF(function() {
19+
waitForTick(function() {
720
passed = true;
821
});
9-
return function(fn) {
10-
passed ? fn() : $$rAF(fn);
22+
return function(callback) {
23+
passed ? callback() : waitForTick(callback);
1124
};
1225
};
1326
}];
1427

15-
var $$AnimateRunnerFactory = ['$q', '$$rAFMutex', function($q, $$rAFMutex) {
28+
var $$AnimateRunnerFactory = ['$q', '$sniffer', '$$animateAsyncRun',
29+
function($q, $sniffer, $$animateAsyncRun) {
30+
1631
var INITIAL_STATE = 0;
1732
var DONE_PENDING_STATE = 1;
1833
var DONE_COMPLETE_STATE = 2;
@@ -57,7 +72,7 @@ var $$AnimateRunnerFactory = ['$q', '$$rAFMutex', function($q, $$rAFMutex) {
5772
this.setHost(host);
5873

5974
this._doneCallbacks = [];
60-
this._runInAnimationFrame = $$rAFMutex();
75+
this._runInAnimationFrame = $$animateAsyncRun();
6176
this._state = 0;
6277
}
6378

src/ngAnimate/module.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
/* global angularAnimateModule: true,
44
55
$$BodyProvider,
6-
$$rAFMutexFactory,
6+
$$AnimateAsyncRunFactory,
77
$$AnimateChildrenDirective,
88
$$AnimateRunnerFactory,
99
$$AnimateQueueProvider,
@@ -745,9 +745,8 @@ angular.module('ngAnimate', [])
745745

746746
.directive('ngAnimateChildren', $$AnimateChildrenDirective)
747747

748-
.factory('$$rAFMutex', $$rAFMutexFactory)
749-
750748
.factory('$$AnimateRunner', $$AnimateRunnerFactory)
749+
.factory('$$animateAsyncRun', $$AnimateAsyncRunFactory)
751750

752751
.provider('$$animateQueue', $$AnimateQueueProvider)
753752
.provider('$$animation', $$AnimationProvider)

src/ngMock/angular-mocks.js

+44-19
Original file line numberDiff line numberDiff line change
@@ -763,25 +763,50 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
763763
return reflowFn;
764764
});
765765

766-
$provide.decorator('$animate', ['$delegate', '$timeout', '$browser', '$$rAF', '$$forceReflow',
767-
function($delegate, $timeout, $browser, $$rAF, $$forceReflow) {
766+
$provide.factory('$$animateAsyncRun', function() {
767+
var queue = [];
768+
var queueFn = function() {
769+
return function(fn) {
770+
queue.push(fn);
771+
};
772+
};
773+
queueFn.flush = function() {
774+
if (queue.length === 0) return false;
775+
776+
for (var i = 0; i < queue.length; i++) {
777+
queue[i]();
778+
}
779+
queue = [];
780+
781+
return true;
782+
};
783+
return queueFn;
784+
});
785+
786+
$provide.decorator('$animate', ['$delegate', '$timeout', '$browser', '$$rAF', '$$forceReflow', '$$animateAsyncRun',
787+
function($delegate, $timeout, $browser, $$rAF, $$forceReflow, $$animateAsyncRun) {
768788

769789
var animate = {
770790
queue: [],
771791
cancel: $delegate.cancel,
792+
on: $delegate.on,
793+
off: $delegate.off,
794+
pin: $delegate.pin,
772795
get reflows() {
773796
return $$forceReflow.totalReflows;
774797
},
775798
enabled: $delegate.enabled,
776-
triggerCallbackEvents: function() {
777-
$$rAF.flush();
778-
},
779-
triggerCallbackPromise: function() {
780-
$timeout.flush(0);
781-
},
782-
triggerCallbacks: function() {
783-
this.triggerCallbackEvents();
784-
this.triggerCallbackPromise();
799+
flush: function() {
800+
var rafsFlushed = false;
801+
if ($$rAF.queue.length) {
802+
$$rAF.flush();
803+
rafsFlushed = true;
804+
}
805+
806+
var animatorsFlushed = $$animateAsyncRun.flush();
807+
if (!rafsFlushed && !animatorsFlushed) {
808+
throw new Error('No pending animations ready to be closed or flushed');
809+
}
785810
}
786811
};
787812

@@ -1733,28 +1758,28 @@ angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $
17331758
}];
17341759

17351760
angular.mock.$RAFDecorator = ['$delegate', function($delegate) {
1736-
var queue = [];
17371761
var rafFn = function(fn) {
1738-
var index = queue.length;
1739-
queue.push(fn);
1762+
var index = rafFn.queue.length;
1763+
rafFn.queue.push(fn);
17401764
return function() {
1741-
queue.splice(index, 1);
1765+
rafFn.queue.splice(index, 1);
17421766
};
17431767
};
17441768

1769+
rafFn.queue = [];
17451770
rafFn.supported = $delegate.supported;
17461771

17471772
rafFn.flush = function() {
1748-
if (queue.length === 0) {
1773+
if (rafFn.queue.length === 0) {
17491774
throw new Error('No rAF callbacks present');
17501775
}
17511776

1752-
var length = queue.length;
1777+
var length = rafFn.queue.length;
17531778
for (var i = 0; i < length; i++) {
1754-
queue[i]();
1779+
rafFn.queue[i]();
17551780
}
17561781

1757-
queue = queue.slice(i);
1782+
rafFn.queue = rafFn.queue.slice(i);
17581783
};
17591784

17601785
return rafFn;

test/ng/directive/ngClassSpec.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ describe('ngClass animations', function() {
468468
};
469469
});
470470
});
471-
inject(function($compile, $rootScope, $browser, $rootElement, $animate, $timeout, $$body, $$rAF) {
471+
inject(function($compile, $rootScope, $browser, $rootElement, $animate, $timeout, $$body) {
472472
$animate.enabled(true);
473473

474474
$rootScope.val = 'crazy';
@@ -488,7 +488,7 @@ describe('ngClass animations', function() {
488488
expect(enterComplete).toBe(false);
489489

490490
$rootScope.$digest();
491-
$$rAF.flush();
491+
$animate.flush();
492492
$rootScope.$digest();
493493

494494
expect(element.hasClass('crazy')).toBe(true);

test/ng/directive/ngIncludeSpec.js

+10-8
Original file line numberDiff line numberDiff line change
@@ -428,9 +428,11 @@ describe('ngInclude', function() {
428428
});
429429

430430
expect(autoScrollSpy).not.toHaveBeenCalled();
431-
expect($animate.queue.shift().event).toBe('enter');
432-
$animate.triggerCallbacks();
433431

432+
$animate.flush();
433+
$rootScope.$digest();
434+
435+
expect($animate.queue.shift().event).toBe('enter');
434436
expect(autoScrollSpy).toHaveBeenCalledOnce();
435437
}));
436438

@@ -446,7 +448,6 @@ describe('ngInclude', function() {
446448
});
447449

448450
expect($animate.queue.shift().event).toBe('enter');
449-
$animate.triggerCallbacks();
450451

451452
$rootScope.$apply(function() {
452453
$rootScope.tpl = 'another.html';
@@ -455,7 +456,6 @@ describe('ngInclude', function() {
455456

456457
expect($animate.queue.shift().event).toBe('leave');
457458
expect($animate.queue.shift().event).toBe('enter');
458-
$animate.triggerCallbacks();
459459

460460
$rootScope.$apply(function() {
461461
$rootScope.tpl = 'template.html';
@@ -464,7 +464,9 @@ describe('ngInclude', function() {
464464

465465
expect($animate.queue.shift().event).toBe('leave');
466466
expect($animate.queue.shift().event).toBe('enter');
467-
$animate.triggerCallbacks();
467+
468+
$animate.flush();
469+
$rootScope.$digest();
468470

469471
expect(autoScrollSpy).toHaveBeenCalled();
470472
expect(autoScrollSpy.callCount).toBe(3);
@@ -480,7 +482,6 @@ describe('ngInclude', function() {
480482
});
481483

482484
expect($animate.queue.shift().event).toBe('enter');
483-
$animate.triggerCallbacks();
484485
expect(autoScrollSpy).not.toHaveBeenCalled();
485486
}));
486487

@@ -496,7 +497,6 @@ describe('ngInclude', function() {
496497
});
497498

498499
expect($animate.queue.shift().event).toBe('enter');
499-
$animate.triggerCallbacks();
500500

501501
$rootScope.$apply(function() {
502502
$rootScope.tpl = 'template.html';
@@ -518,7 +518,9 @@ describe('ngInclude', function() {
518518

519519
$rootScope.$apply("tpl = 'template.html'");
520520
expect($animate.queue.shift().event).toBe('enter');
521-
$animate.triggerCallbacks();
521+
522+
$animate.flush();
523+
$rootScope.$digest();
522524

523525
expect(autoScrollSpy).toHaveBeenCalledOnce();
524526
}

test/ng/directive/ngRepeatSpec.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1462,7 +1462,7 @@ describe('ngRepeat animations', function() {
14621462
}));
14631463

14641464
it('should not change the position of the block that is being animated away via a leave animation',
1465-
inject(function($compile, $rootScope, $animate, $document, $window, $sniffer, $timeout, $$rAF) {
1465+
inject(function($compile, $rootScope, $animate, $document, $window, $sniffer, $timeout) {
14661466
if (!$sniffer.transitions) return;
14671467

14681468
var item;
@@ -1487,7 +1487,7 @@ describe('ngRepeat animations', function() {
14871487
$rootScope.$digest();
14881488

14891489
expect(element.text()).toBe('123'); // the original order should be preserved
1490-
$$rAF.flush();
1490+
$animate.flush();
14911491
$timeout.flush(1500); // 1s * 1.5 closing buffer
14921492
expect(element.text()).toBe('13');
14931493
} finally {

0 commit comments

Comments
 (0)