Angularjs CSS Animation + Done Callback

AngularJS css animation + done callback

Yes you can, using the $animate service, which would usually be done in a custom directive. A simple case of animation would be to animate an element in some way on click. Say, for example to remove an element on click, with an animation specified using .ng-leave, passing a callback

app.directive('leaveOnClick', function($animate) {
return {
scope: {
'leaveOnClick': '&'
},
link: function (scope, element) {
scope.leaveOnClick = scope.leaveOnClick || (function() {});
element.on('click', function() {
scope.$apply(function() {
$animate.leave(element, scope.leaveOnClick);
});
});
}
};
});

which could be used like:

<div class="my-div" leaveOnClick="done()">Click to remove</div>

With CSS to fade the element out:

.my-div.ng-leave {
opacity: 1;
transition: opacity 1s;
-webkit-transition: opacity 1s;
}
.my-div.ng-leave.ng-leave-active {
opacity: 0;
}

You can see the above animation in action at this Plunker.

However, ngIf doesn't have any hooks to pass a callback in that I know of, so you'll have to write your own directive. What follows is a description of a modified version of ngIf, originally copied from the ngIf source, and renamed to animatedIf. It can be used by:

<div class="my-div" animated-if="shown" animated-if-leave-callback="leaveDone()" animated-if-enter-callback="enterDone()" >Some content</div>

The way it works is that it uses a manual watcher to react to changes of the expression passed to animated-if. The key differences to the original ngIf are the addition of a 'scope' parameter to pass the callbacks in:

scope: {
'animatedIf': '=',
'animatedIfEnterCallback': '&',
'animatedIfLeaveCallback': '&'
},

and then modifying the calls to $animate.enter and $animate.leave to call these callbacks after the animation:

var callback = !oldValue && $scope.animatedIfEnterCallback ? $scope.animatedIfEnterCallback : (function() {});
$animate.enter(clone, $element.parent(), $element, callback);

$animate.leave(block.clone, ($scope.animatedIfLeaveCallback || (function() {})));

The enter one is a bit more complicated to not call the callback on initial loading of the directive. Because of the scope parameter, this directive creates an isolated scope, which it then uses when transcluding the contents. So another change that is required is to create and use a scope as a child from the $parent scope of the directive: the line

 childScope = $scope.$new();

must be changed to

 childScope = $scope.$parent.$new();

You can see the full source of the modified ngIf directive in this Plunker. This has only been tested extremely briefly.

There may well be a simpler way of doing this, maybe by not recreating the ngIf directive fully, but creating a directive with template that uses the original ngIf with some wrapper divs, such as

template: '<div><div ng-if="localVariable"><div ng-transclude></div></div></div>'

Running code after an AngularJS animation has completed

@michael-charemza answer worked great for me. If you are using Angular 1.3 they changed the promise a little. I got stuck on this for a little bit but here is the change that got it to work:

if (show) {
$animate.removeClass(element, 'ng-hide').then(scope.afterShow);
}
if (!show) {
$animate.addClass(element, 'ng-hide').then(scope.afterHide);
}

Plunker: Code Example

AngularJS animate with callback, wait for the next animation to start

First off, it is important to remember to include which version of AngularJS you are using. However, I will provide both 1.2 and 1.3+ solutions. If you are using a version less than 1.2, you should really consider upgrading.

The $animate Service

The $animate service provides an addClass method and a removeClass method that will take care of what you need easily. The behavior changes based upon what version of AngularJS you are using.

Either way, you start by including the animate module (it's separate from the angular.js main file) and then inject the $animate service where you need it.

app.directive("myShow", [
'$animate',
function($animate) {
return {
// your directive config
};
}
]);

AngularJS v1.2.x

The $animate (v1.2.26) service's addClass and removeClass methods have three arguments: element, className, and doneCallback. Here, simply, you can use the doneCallback function argument to tell when an animation is done. For example:

$animate.addClass(element, 'draw', function() {
// if we are in here, the animation is complete
});

AngularJS v1.3.x

With AngularJS 1.3, each of the animation methods, on the $animate service, return a promise when called. The promise itself is then resolved once the animation has completed itself, has been cancelled or has been skipped due to animations being disabled. (Note that even if the animation is cancelled it will still call the resolve function of the animation.) See Documentation

With 1.3 and above you need to utilize the promise returned by calling addClass or removeClass through the $animate service. You can do so by chaining the then function at the end. For example:

$animate.addClass(element, 'draw').then(function() {
// if we are in here, the animation is complete
});

If you are using the AngularJS 1.3 RC (latest unstable as of this post) and you aren't familiar with promises then reference the $q service.

Is there a callback on completion of a CSS3 animation?

Yes, there is. The callback is an event, so you must add an event listener to catch it. This is an example with jQuery:

$("#sun").bind('oanimationend animationend webkitAnimationEnd', function() { 
alert("fin")
});

Or pure js:

element.addEventListener("webkitAnimationEnd", callfunction,false);
element.addEventListener("animationend", callfunction,false);
element.addEventListener("oanimationend", callfunction,false);

Live demo:
http://jsfiddle.net/W3y7h/

Animation callbacks runs immediately even without the state being changed

The initial state of animation is a "stateless" state called void - which is the default state when using animations.
After initializing your state variable to steady a non-existing animation is starting from void -> steady.

In order to reach your goal, you should use properties fromState & toState of AnimationEvent.

import { AnimationEvent, } from '@angular/animations';

public animStart(event: AnimationEvent): void {
if(event.fromState === 'steady') {
// ...
}
}

Try to log AnimationEvent in your animStart method in order to better understand this interface.

public animStart(event: AnimationEvent): void {
console.log(event);
}

Getting done event on CSS Transition with AngularJS 1.2 and angular-animate

I stumbled upon this solution - basically it works:
How do I use transitionend in jQuery?

If someone wants to add a more angular-ish solution it would be great!

Angular ng-view animation callback

replace

.ng-enter, .ng-leave {
-webkit-transition: all 1s ease;
-moz-transition: all 1s ease;
-o-transition: all 1s ease;
transition: all 1s ease;
}

.ng-enter {
opacity: 0;
}

with the following code

.ng-enter {
-webkit-transition: all 1s ease;
-moz-transition: all 1s ease;
-o-transition: all 1s ease;
transition: all 1s ease;
opacity: 0;
}

you don't need to make animation on both events(enter/leave) you can make just on enter and it wont make the simultaneous animation it will hide the first view immediately and then animate the second .

http://plnkr.co/edit/iVSRrg9nBaDTtHUZcDDu

if you need one animation after another you need a callback. To avoid repetition you can find the answer here:

AngularJS css animation + done callback

How do you get an animation callback in AngularJS?

It's unfortunately not as easy as you'd hope. There's no way to pass an animation end callback to ng-show, so instead you have to implement your own show/hide directive and make use of the $animate service.

I forked your plunker to demonstrate.

This is the directive I made:

app.directive("myShow", function($animate){
return {
scope:{
// use & so that this will evaluate expressions.
toggle: "&myShow"
},
link: function( scope, element, attrs ){

// watch the toggle function
scope.$watch(scope.toggle, function(newVal, oldVal){

if(newVal && oldVal != undefined){
$animate.removeClass(element, 'ng-hide', function(){
console.log("finished");
})
}

else {
$animate.addClass(element,'ng-hide', function(){
console.log("finished!");
})
}

});
}
}
});

You can see that you call $animate's addClass method with ng-hide and you can use the exact same CSS. Using the $animate service directly means you can make use of the doneCallback.

PS this is just to demonstrate $animate, I haven't checked whether this initialises properly - you might need to make sure ng-hide is correctly on/off when the page loads.



Related Topics



Leave a reply



Submit