pref($animate): group all asynchronous requests into one shared buffer

This commit is contained in:
Matias Niemelä
2014-02-13 14:59:25 -05:00
committed by Igor Minar
parent b7e4e92014
commit f288b8f010
2 changed files with 62 additions and 23 deletions

View File

@@ -268,6 +268,20 @@ angular.module('ngAnimate', ['ng'])
};
}])
.factory('$$asyncQueueBuffer', ['$timeout', function($timeout) {
var timer, queue = [];
return function(fn) {
$timeout.cancel(timer);
queue.push(fn);
timer = $timeout(function() {
for(var i = 0; i < queue.length; i++) {
queue[i]();
}
queue = [];
}, 0, false);
};
}])
.config(['$provide', '$animateProvider', function($provide, $animateProvider) {
var noop = angular.noop;
var forEach = angular.forEach;
@@ -291,9 +305,10 @@ angular.module('ngAnimate', ['ng'])
return extractElementNode(elm1) == extractElementNode(elm2);
}
$provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$timeout', '$rootScope', '$document',
function($delegate, $injector, $sniffer, $rootElement, $timeout, $rootScope, $document) {
$provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$$asyncQueueBuffer', '$rootScope', '$document',
function($delegate, $injector, $sniffer, $rootElement, $$asyncQueueBuffer, $rootScope, $document) {
var globalAnimationCounter = 0;
$rootElement.data(NG_ANIMATE_STATE, rootAnimateState);
// disable animations during bootstrap, but once we bootstrapped, wait again
@@ -315,10 +330,6 @@ angular.module('ngAnimate', ['ng'])
return classNameFilter.test(className);
};
function async(fn) {
return $timeout(fn, 0, false);
}
function lookup(name) {
if (name) {
var matches = [],
@@ -685,7 +696,6 @@ angular.module('ngAnimate', ['ng'])
if(ngAnimateState.running) {
//if an animation is currently running on the element then lets take the steps
//to cancel that animation and fire any required callbacks
$timeout.cancel(ngAnimateState.closeAnimationTimeout);
cleanup(element);
cancelAnimations(ngAnimateState.animations);
@@ -736,12 +746,15 @@ angular.module('ngAnimate', ['ng'])
//parent animations to find and cancel child animations when needed
element.addClass(NG_ANIMATE_CLASS_NAME);
var localAnimationCount = globalAnimationCounter++;
element.data(NG_ANIMATE_STATE, {
running:true,
event:animationEvent,
className:className,
structural:!isClassBased,
animations:animations,
index:localAnimationCount,
done:onBeforeAnimationsComplete
});
@@ -816,19 +829,19 @@ angular.module('ngAnimate', ['ng'])
}
function fireBeforeCallbackAsync() {
async(function() {
$$asyncQueueBuffer(function() {
fireDOMCallback('before');
});
}
function fireAfterCallbackAsync() {
async(function() {
$$asyncQueueBuffer(function() {
fireDOMCallback('after');
});
}
function fireDoneCallbackAsync() {
async(function() {
$$asyncQueueBuffer(function() {
fireDOMCallback('close');
doneCallback && doneCallback();
});
@@ -855,8 +868,11 @@ angular.module('ngAnimate', ['ng'])
if(isClassBased) {
cleanup(element);
} else {
data.closeAnimationTimeout = async(function() {
cleanup(element);
$$asyncQueueBuffer(function() {
var data = element.data(NG_ANIMATE_STATE) || {};
if(localAnimationCount == data.index) {
cleanup(element);
}
});
element.data(NG_ANIMATE_STATE, data);
}

View File

@@ -2563,8 +2563,9 @@ describe("ngAnimate", function() {
});
it("should disable all child animations on structural animations until the post animation timeout has passed", function() {
var intercepted;
it("should disable all child animations on structural animations until the post animation" +
"timeout has passed as well as all structural animations", function() {
var intercepted, continueAnimation;
module(function($animateProvider) {
$animateProvider.register('.animated', function() {
return {
@@ -2578,7 +2579,10 @@ describe("ngAnimate", function() {
function ani(type) {
return function(element, className, done) {
intercepted = type;
(done || className)();
continueAnimation = function() {
continueAnimation = angular.noop;
(done || className)();
}
}
}
});
@@ -2595,26 +2599,45 @@ describe("ngAnimate", function() {
var child2 = $compile('<div class="child2 animated">...</div>')($rootScope);
var container = $compile('<div class="container">...</div>')($rootScope);
jqLite($document[0].body).append($rootElement);
var body = angular.element($document[0].body);
body.append($rootElement);
$rootElement.append(container);
element.append(child1);
element.append(child2);
$animate.enter(element, container);
$rootScope.$digest();
expect(intercepted).toBe('enter');
continueAnimation();
$animate.addClass(child1, 'test');
expect(child1.hasClass('test')).toBe(true);
expect(element.children().length).toBe(2);
expect(intercepted).toBe('enter');
$animate.leave(child1);
$rootScope.$digest();
expect(element.children().length).toBe(1);
expect(intercepted).toBe('enter');
$animate.move(element, null, container);
$rootScope.$digest();
expect(intercepted).toBe('move');
$animate.addClass(child1, 'test');
expect(child1.hasClass('test')).toBe(true);
expect(intercepted).toBe('move');
$animate.leave(child1);
$rootScope.$digest();
//flush the enter reflow
$timeout.flush();
$animate.addClass(child2, 'testing');
expect(intercepted).toBe('move');
//reflow has passed
continueAnimation();
//flush the move reflow
$timeout.flush();
$animate.leave(child2);