Animating an ng-class
addition or removal using CSS transition has 3 stages. The order of these stages are very important, I almost spent a day figuring out why a simple animation wasn't working due incorrect understanding of the order in which classes are added.
Stage 1:
classname-add
/classname-remove
class is added.
Unlike what someone might think, this is actually added before the class is added to/removed from the element.
This is the stage where we should add the transition
property 1 as well as initial state of our animation.
Stage 2:
classname
class is added or removed.
This is where you specify the eventual styles of the element. This class often has nothing to do with our animation. Remember that we are animating the addition/removal of this class. This class itself shouldn't even need to be aware that there is an animation taking place around it.
Stage 3:
classname-add-active
/classname-remove-active
class is added.
This is added after the class is added to/removed from the element.
This is the stage where we should specify the final state of our animation.
To see this in action, let's create a classic fade-in-out animation shown when an element's selected state changes (selected
class change using ng-class).
angular.module('demo', ['ngAnimate'])
.controller('demoCtrl', function($scope) {
$scope.selected = false;
$scope.selectToggle = function() {
$scope.selected = !$scope.selected;
};
});
.item {
width: 50px;
height: 50px;
background: grey;
}
.item.selected {
/* this is the actual change to the elment
* which has nothing to do with the animation
*/
background-color: dodgerblue;
}
.item.selected-add,
.item.selected-remove {
/* Here we specify the transition property and
* initial state of the animation, which is hidden
* state having 0 opacity
*/
opacity: 0;
transition: opacity 3s;
}
.item.selected-add-active,
.item.selected-remove-active {
/* Here we specify the final state of the animation,
* which is visible having 1 opacity
*/
opacity: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular-animate.js"></script>
<div ng-app="demo" ng-controller="demoCtrl">
<div class="item" ng-class="{selected:selected}"></div>
<br>
<br>
<button ng-click="selectToggle();">
{{selected? 'Unselect' : 'Select'}}
</button>
</div>
1 Why should I specify the transition in the first state, instead of just adding it to the class being toggled or a static selector on the element?, you ask.
Well to explain this, assume you need a one-directional animation, for example a fade-out animation when a fade-out
class is added.
If you add transition
property on the fade-out
class itself, the transition stays on the element even after the animation. Which means when your final state (fade-out-add-active) is removed, the element will slowly fade-in back, so we get a fade-out-fade-in which is not what we wanted.