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

fix($animateCss): ensure that detection cache is flushed after animation is closed #11805

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 21 additions & 8 deletions src/ngAnimate/animateCss.js
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
var KEY = "$$ngAnimateParentKey";
var parentNode = node.parentNode;
var parentID = parentNode[KEY] || (parentNode[KEY] = ++parentCounter);
return parentID + '-' + node.getAttribute('class') + '-' + extraClasses;
return parentID + '-' + (node.getAttribute('class') || '') + '-' + (extraClasses || '');
}

function computeCachedCssStyles(node, className, cacheKey, properties) {
Expand Down Expand Up @@ -472,6 +472,11 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
return stagger || {};
}

function flushGCSCache() {
gcsLookup.flush();
gcsStaggerLookup.flush();
}

var bod = $document[0].body;
var cancelLastRAFRequest;
var rafWaitQueue = [];
Expand All @@ -482,8 +487,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
rafWaitQueue.push(callback);
cancelLastRAFRequest = $$rAF(function() {
cancelLastRAFRequest = null;
gcsLookup.flush();
gcsStaggerLookup.flush();
flushGCSCache();

//the line below will force the browser to perform a repaint so
//that all the animated elements within the animation frame will
Expand Down Expand Up @@ -514,7 +518,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
? Math.max(aD, tD)
: (aD || tD);
timings.maxDuration = Math.max(
timings.animationDuration * timings.animationIterationCount,
(timings.animationDuration * timings.animationIterationCount) || 0,
timings.transitionDuration);

return timings;
Expand All @@ -525,7 +529,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
options = prepareAnimationOptions(options);

var temporaryStyles = [];
var classes = element.attr('class');
var classes = element.attr('class') || '';
var styles = packageStyles(options);
var animationClosed;
var animationPaused;
Expand Down Expand Up @@ -649,7 +653,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {

var timings = computeTimings(node, fullClassName, cacheKey);
var relativeDelay = timings.maxDelay;
maxDelay = Math.max(relativeDelay, 0);
maxDelay = Math.max(relativeDelay || 0, 0);
maxDuration = timings.maxDuration;

var flags = {};
Expand Down Expand Up @@ -715,6 +719,11 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
start: function() {
if (animationClosed) return;

// the code below will wait until the first RAF has passed. By waiting
// we allow multiple similar animations to be grouped together to allow
// for stagger calculations. This waiting phase is known as the quiet
// phase and any code that runs after is known as "post-quiet" code.

runnerHost = {
end: endFn,
cancel: cancelFn,
Expand Down Expand Up @@ -765,6 +774,10 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
applyAnimationClasses(element, options);
applyAnimationStyles(element, options);

// we need to clear the cache since the post-quiet state may add and remove CSS
// classes which contain follow-up animation data which will be cached.
$$rAF(flushGCSCache);

// the reason why we have this option is to allow a synchronous closing callback
// that is fired as SOON as the animation ends (when the CSS is removed) or if
// the animation never takes off at all. A good example is a leave animation since
Expand Down Expand Up @@ -860,7 +873,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {

timings = computeTimings(node, fullClassName, cacheKey);
relativeDelay = timings.maxDelay;
maxDelay = Math.max(relativeDelay, 0);
maxDelay = Math.max(relativeDelay || 0, 0);
maxDuration = timings.maxDuration;

if (maxDuration === 0) {
Expand All @@ -877,7 +890,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
? parseFloat(options.delay)
: relativeDelay;

maxDelay = Math.max(relativeDelay, 0);
maxDelay = Math.max(relativeDelay || 0, 0);

var delayStyle;
if (flags.applyTransitionDelay) {
Expand Down
30 changes: 30 additions & 0 deletions test/ngAnimate/animateCssSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,36 @@ describe("ngAnimate $animateCss", function() {
expect(count.normal).toBe(3);
}));

it("should cache the post-quiet state detection and flush one frame after the animation is complete",
inject(function($animateCss, $document, $rootElement, $$rAF) {

var i, animator, elms = [];
for (i = 0; i < 5; i++) {
var elm = jqLite('<div>' + i + '</div>');
$rootElement.append(elm);
animator = $animateCss(elm, { to: {color:'green'}, duration: 0.5 });
var runner = animator.start();
elms.push(elm);
}

expect(count.normal).toBe(2); //first + stagger
triggerAnimationStartFrame();

expect(count.normal).toBe(3); //first + stagger + post-quiet
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you by any chance mean quit (here and elsewhere) ?
(If not, I wonder what post-quiet is about.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this:

          // the code below will wait until the first RAF has passed. By waiting
          // we allow multiple similar animations to be grouped together to allow
          // for stagger calculations. This waiting phase is known as the quiet
          // phase and any code that runs after is known as "post-quiet" code.

for (i = 0; i < elms.length; i++) {
browserTrigger(elms[i], 'transitionend',
{ timeStamp: Date.now() + 1000, elapsedTime: 1 });
}

$$rAF.flush();

for (i = 0; i < elms.length; i++) {
animator = $animateCss(elms[i], { to: {color:'red'}, duration: 0.8 });
}

expect(count.normal).toBe(5); //first + stagger + post-quiet + first + stagger
}));

it("should cache frequent calls to getComputedStyle for stagger animations before the next animation frame kicks in",
inject(function($animateCss, $document, $rootElement, $$rAF) {

Expand Down