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

Animation Flicker #5262

Closed
andrelom opened this issue Dec 4, 2013 · 23 comments
Closed

Animation Flicker #5262

andrelom opened this issue Dec 4, 2013 · 23 comments

Comments

@andrelom
Copy link

andrelom commented Dec 4, 2013

I created a directive that uses the "ngShow" and "ngAnimate". When this directive is loaded by the first time, it appears and disappears (flicker).

Tried to fix by checking if the problem was in my code. I don't have idea if it is normal or not, but is strange.

For now, I fixed the problem by using the "element [0] style.display = 'none';".

In the "http://www.yearofmoo.com/2013/05/enhanced-animations-in-angularjs.html", Matias talk about this (Animations are skipped the first time after a directive is compiled).

I read others articles about that, but I believe the "problem" still happens in Angular 1.2.

@matsko
Copy link
Contributor

matsko commented Dec 4, 2013

Aside from the yearofmoo article, can you provide the 1.2 animation code you're using that is causing the flicker?

@ghost ghost assigned matsko Dec 4, 2013
@andrelom
Copy link
Author

andrelom commented Dec 4, 2013

I cut some pieces of code from my application and made it ​​available at the Plunker. When the page is loaded, is possible see the effect (flicker) described above.

http://embed.plnkr.co/VEqZHmY9VhpdQkChxmvP/preview

@matsko
Copy link
Contributor

matsko commented Dec 4, 2013

The example wasn't fully useable, I changed a few variables so that the modal shows up when the page loads. Is this how it should function? http://plnkr.co/edit/YCd1l4HL9gw8Ummb9u3g?p=preview

@andrelom
Copy link
Author

andrelom commented Dec 4, 2013

No. The modal should not open automatically, it should be hidden until the "modal open" event is triggered.
The page will take a while to start and the effect is really fast, but if you see, when the compilation is completed, the modal show and hide again. To repeat the error if it has not been possible to see run the code (stop / play) again, it was the only way I could illustrate.

http://embed.plnkr.co/VEqZHmY9VhpdQkChxmvP/preview

@matsko
Copy link
Contributor

matsko commented Dec 4, 2013

Ahh now I understand. From what I can assume it's that the modal template loading too soon and the animation starting. It could also be that it's trying to remove the ng-hide CSS class when it isn't actually there.

@andrelom
Copy link
Author

andrelom commented Dec 4, 2013

That's it (... too soon modal template loading and starting the animation).

I changed the "templateUrl" property to "template", passing the template as a string, and the problem stopped happening.

http://embed.plnkr.co/VEqZHmY9VhpdQkChxmvP/preview

@matsko
Copy link
Contributor

matsko commented Dec 4, 2013

Very nice! :) Can I close the issue?

@andrelom
Copy link
Author

andrelom commented Dec 4, 2013

Yep :/. But I still believe that this is still a bug, because all the custom directives that have CSS animation class (xyz-animate) in the main element, got a external template, and its default status is "display: none" (ngShow/ngHide) will have this bad behavior. I would like to help, but at the moment, I have no idea of where this error can be explored in the Angular.

@andrelom
Copy link
Author

Hi,

I fixed the error as follows:

  • I created a CSS class called "clear-animation", which has the property "transition: none important!;";
  • I added it with the default animation class "xyz-animation";
  • When the directive is compiled, I cut the class "clear-animation".

At the time it was the best alternative to solve the problem.

Note: The directive "ng-cloak" does not work in this case.

@matsko
Copy link
Contributor

matsko commented Dec 20, 2013

@andrelom what should we do next? I'm still working on another PR that fixes a flicker, but that is related to CSS animations. With the example here you're using CSS transitions.

The clear-animation property is not the way to go. It would better for us to get to the root of the problem. Now that some other blocking issues are fixed and in master I can retest this on 1.2.6. Can you provide the earlier plunkr example which has the broken flicker code?

@andrelom
Copy link
Author

Hi @matsko I created the plunkr example.

http://plnkr.co/edit/ncXb8rU6Dw5wP13nRqW5

@andrelom
Copy link
Author

I've been looking at the source code of the "angular-animate.js (line 291, v1.2.6)" and found this in "$animateProvider":

// disable animations during bootstrap, but once we bootstrapped, wait again
// for another digest until enabling animations. The reason why we digest twice
// is because all structural animations (enter, leave and move) all perform a
// post digest operation before animating. If we only wait for a single digest
// to pass then the structural animation would render its animation on page load.
// (which is what we're trying to avoid when the application first boots up.)
$rootScope.$$postDigest(function() {
  $rootScope.$$postDigest(function() {
    rootAnimateState.running = false;
  });
});

Replacing the piece of code with the current code below, the error has stopped.
I believe this function should be the last to be pushed to the "$$postDigestQueue" but I not found a better way yet.

$rootScope.$$postDigest(function() {
  // Just a "jerry-rig" :P
  $timeout(function () {
    rootAnimateState.running = false;
  }, 500);
});

I have little knowledge of the source code of ngAnimate, but reading the code and the comments, I believe the problem is at this point.

@matsko
Copy link
Contributor

matsko commented Jan 6, 2014

The issue shows up in your plunkr example, but when replicated locally it doesn't. My guess is that plunkr loads things slower than if you do it locally and that way everything downloads in time. Any idea how we can replicate this?

@andrelom
Copy link
Author

andrelom commented Jan 6, 2014

I've tested it using a server created with "Node" and "Express", this worked for me (the error appeared).
I am providing a".zip" file with the module already created.
The command to run the server is "node ./server.js".

https://www.dropbox.com/s/xbp4or1o0m0dbf5/issue.zip

Previously I did a test and it worked, but it is not the best way, right now I'm just discussing an idea about a possible solution as follows:

function isRequesting() {
  return $http.pendingRequests.length > 0;
}
$rootScope.$watch(isRequesting, function (value) {
  if (value) {
    rootAnimateState.running = true;
  } else {
    rootAnimateState.running = false;
  }
});

I'll wait for new ideas about the error, before I can talk about this, possibly because this can be not the way, and I will lose the line.

@tbosch tbosch modified the milestones: 1.2.12, 1.2.11, 1.2.13 Feb 3, 2014
@btford btford modified the milestones: 1.2.14, 1.2.13 Feb 15, 2014
@IgorMinar IgorMinar modified the milestones: 1.3.0-beta.1, 1.2.14 Mar 1, 2014
@btford btford modified the milestones: 1.3.0-beta.2, 1.3.0-beta.1 Mar 10, 2014
@tbosch tbosch modified the milestones: 1.3.0-beta.3, 1.3.0-beta.2 Mar 14, 2014
@btford btford modified the milestones: 1.3.0-beta.4, 1.3.0-beta.3 Mar 24, 2014
@petebacondarwin petebacondarwin removed this from the 1.3.0-beta.7 milestone Apr 27, 2014
@Narretz
Copy link
Contributor

Narretz commented Apr 29, 2014

FWIW, I fixed this is with overwriting the ngShow directive with a delegate:

  $provide.decorator('ngShowDirective', ['$delegate', '$animate', function(
    $delegate, $animate){

    function toBoolean(value) {
      if (typeof value === 'function') {
        value = true;
      } else if (value && value.length !== 0) {
        var v = angular.lowercase("" + value);
        value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]');
      } else {
        value = false;
      }
      return value;
    }

    $delegate[0].compile = function() {
      return function(scope, element, attr){
        scope.$watch(attr.ngShow, function ngShowWatchAction(value, oldVal){
          if ((value === oldVal) && toBoolean(value) === false) {
            element.addClass('ng-hide');
            return;
          }
          $animate[toBoolean(value) ? 'removeClass' : 'addClass'](element, 'ng-hide');
        });
      };
    };
    return $delegate;
  }]);

When the $watch is called for the first time, both values are equal. When these initial values are falsy, don't use $animate, just add the ng-hide class. The toBoolean function is necessary since it's not public.

@caitp caitp modified the milestones: 1.3.0-beta.9, 1.3.0-beta.8 May 9, 2014
@IgorMinar IgorMinar modified the milestones: 1.3.0, 1.3.0-beta.9 May 12, 2014
@Narretz
Copy link
Contributor

Narretz commented Jul 2, 2014

@matsko Can you take another look at this? The above fix might just fix the symptom, but this still an issue with the latest beta / snapshot.

@Narretz
Copy link
Contributor

Narretz commented Jul 21, 2014

@matsko, have you had time to take a look at this? Thanks!

matsko added a commit to matsko/angular.js that referenced this issue Aug 8, 2014
…boostrap

Prior to this fix when an Angular application is bootstrapped it would only
place an animation guard to prevent animations from running when the application
starts for the first two digest cycles. However, if any controllers or directives,
that are executed upon boostrap, trigger any remote code to be downloaded (via $http)
then the guard does not put that into consideration. This fix now properly addresses
that circumstance and removes the guard once all outbound HTTP requests are complete
when an Angular application is bootstrapped.

Closes angular#8275
Closes angular#5262
matsko added a commit to matsko/angular.js that referenced this issue Aug 8, 2014
…bootstrap

Prior to this fix when an Angular application is bootstrapped it would only
place an animation guard to prevent animations from running when the application
starts for the first two digest cycles. However, if any controllers or directives,
that are executed upon boostrap, trigger any remote code to be downloaded (via $http)
then the guard does not put that into consideration. This fix now properly addresses
that circumstance and removes the guard once all outbound HTTP requests are complete
when an Angular application is bootstrapped.

Closes angular#8275
Closes angular#5262
matsko added a commit to matsko/angular.js that referenced this issue Aug 8, 2014
…bootstrap

Prior to this fix when an Angular application is bootstrapped it would only
place an animation guard to prevent animations from running when the application
starts for the first two digest cycles. However, if any controllers or directives,
that are executed upon boostrap, trigger any remote code to be downloaded (via $http)
then the guard does not put that into consideration. This fix now properly addresses
that circumstance and removes the guard once all outbound HTTP requests are complete
when an Angular application is bootstrapped.

Closes angular#8275
Closes angular#5262
matsko added a commit to matsko/angular.js that referenced this issue Aug 8, 2014
…bootstrap

Prior to this fix when an Angular application is bootstrapped it would only
place an animation guard to prevent animations from running when the application
starts for the first two digest cycles. However, if any controllers or directives,
that are executed upon boostrap, trigger any remote code to be downloaded (via $http)
then the guard does not put that into consideration. This fix now properly addresses
that circumstance and removes the guard once all outbound HTTP requests are complete
when an Angular application is bootstrapped.

Closes angular#8275
Closes angular#5262
@matsko
Copy link
Contributor

matsko commented Aug 11, 2014

@andrelom @Narretz Finally there's a fix for this:
http://plnkr.co/edit/lSPUvAuv2wESY0SBvu2Y?p=preview

It should be released by the end of the week.

matsko added a commit to matsko/angular.js that referenced this issue Aug 12, 2014
…bootstrap

Prior to this fix when an Angular application is bootstrapped it would only
place an animation guard to prevent animations from running when the application
starts for the first two digest cycles. However, if any controllers or directives,
that are executed upon boostrap, trigger any remote code to be downloaded (via $http)
then the guard does not put that into consideration. This fix now properly addresses
that circumstance and removes the guard once all outbound HTTP requests are complete
when an Angular application is bootstrapped.

Closes angular#8275
Closes angular#5262
matsko added a commit to matsko/angular.js that referenced this issue Aug 12, 2014
…bootstrap

Prior to this fix when an Angular application is bootstrapped it would only
place an animation guard to prevent animations from running when the application
starts for the first two digest cycles. However, if any controllers or directives,
that are executed upon boostrap, trigger any remote code to be downloaded (via $http)
then the guard does not put that into consideration. This fix now properly addresses
that circumstance and removes the guard once all outbound HTTP requests are complete
when an Angular application is bootstrapped.

Closes angular#8275
Closes angular#5262
matsko added a commit to matsko/angular.js that referenced this issue Aug 12, 2014
…bootstrap

Prior to this fix when an Angular application is bootstrapped it would only
place an animation guard to prevent animations from running when the application
starts for the first two digest cycles. However, if any controllers or directives,
that are executed upon boostrap, trigger any remote code to be downloaded (via $http)
then the guard does not put that into consideration. This fix now properly addresses
that circumstance and removes the guard once all outbound HTTP requests are complete
when an Angular application is bootstrapped.

Closes angular#8275
Closes angular#5262
@Narretz
Copy link
Contributor

Narretz commented Aug 13, 2014

Hi @matsko, thanks for the fix. Unfortunately, in this specific example your fix does not work:

http://plnkr.co/edit/nbo5de9GpmBOZf9J29Hu?p=preview

It's seems like these are two different bugs. In the example, there's a ng-show animation inside the modal, which fires after the modal opens, while it should stay hidden.

matsko added a commit to matsko/angular.js that referenced this issue Aug 28, 2014
…bootstrap

Prior to this fix when an Angular application is bootstrapped it would only
place an animation guard to prevent animations from running when the application
starts for the first two digest cycles. However, if any controllers or directives,
that are executed upon boostrap, trigger any remote code to be downloaded (via $http)
then the guard does not put that into consideration. This fix now properly addresses
that circumstance and removes the guard once all outbound HTTP requests are complete
when an Angular application is bootstrapped.

Closes angular#8275
Closes angular#5262
matsko added a commit to matsko/angular.js that referenced this issue Aug 28, 2014
…bootstrap

Prior to this fix when an Angular application is bootstrapped it would only
place an animation guard to prevent animations from running when the application
starts for the first two digest cycles. However, if any controllers or directives,
that are executed upon boostrap, trigger any remote code to be downloaded (via $http)
then the guard does not put that into consideration. This fix now properly addresses
that circumstance and removes the guard once all outbound HTTP requests are complete
when an Angular application is bootstrapped.

Closes angular#8275
Closes angular#5262
matsko added a commit to matsko/angular.js that referenced this issue Aug 28, 2014
…bootstrap

Prior to this fix when an Angular application is bootstrapped it would only
place an animation guard to prevent animations from running when the application
starts for the first two digest cycles. However, if any controllers or directives,
that are executed upon boostrap, trigger any remote code to be downloaded (via $http)
then the guard does not put that into consideration. This fix now properly addresses
that circumstance and removes the guard once all outbound HTTP requests are complete
when an Angular application is bootstrapped.

Closes angular#8275
Closes angular#5262
matsko added a commit to matsko/angular.js that referenced this issue Aug 28, 2014
…bootstrap

Prior to this fix when an Angular application is bootstrapped it would only
place an animation guard to prevent animations from running when the application
starts for the first two digest cycles. However, if any controllers or directives,
that are executed upon boostrap, trigger any remote code to be downloaded (via $http)
then the guard does not put that into consideration. This fix now properly addresses
that circumstance and removes the guard once all outbound HTTP requests are complete
when an Angular application is bootstrapped.

Closes angular#8275
Closes angular#5262
@matsko matsko closed this as completed in 4bca4c4 Aug 28, 2014
@j-walker23
Copy link

@Narretz thanks so much for the temporary decorator fix. saved me who knows how much time!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.