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/
Callback on CSS animation end
$('#my_object').animate_scale().fadeOut(2000);
if you want .fadeOut()
to wait for animate_scale()
to finish, animate_scale
needs to be queued:
Queue your plugin:
Usually, when you chain fx methods like i.e:
$("#ball").animate({left:200}).fadeOut();
you'll see the ball animate, and only once that animation is finished --- it'll fade out.
Why? Cause jQuery will stach animate
and than fadeOut
into a queue
array and wait each to resolve before triggering the next Method.
To replicate the same behavior within your plugin:
jsFiddle demo (Queue in action!)
$.fn.animate_scale = function( callback ) {
var $this = this;
return $this.queue(function() {
$this.addClass('animate_scale').on("animationend", function() {
$this.dequeue();
if (typeof callback == 'function') callback.call( $this );
});
});
};
$('#my_object').animate_scale(function() {
console.log( "Scale is done!" );
}).fadeOut( 2000 ); // fadeOut will wait for animate_scale to dequeue (complete)
I don't need queue stacking
If you want your plugin to unobstructively (simultaneously) process other chained fx Methods,
use just the callback:
jsFiddle demo (no Queue)
$.fn.animate_scale = function( callback ) {
var $this = $(this);
return $this.addClass('animate_scale').on("animationend", function() {
if (typeof callback == 'function') callback.call( this );
});
};
$('#my_object').animate_scale(function(){
console.log("Scale done.");
// use $(this).fadeOut(2000); here!! cause otherwise...
}).fadeOut(2000); // ...if chained here, will fade immediately!!!!!
Callback on CSS transition
I know that Safari implements a webkitTransitionEnd callback that you can attach directly to the element with the transition.
Their example (reformatted to multiple lines):
box.addEventListener(
'webkitTransitionEnd',
function( event ) {
alert( "Finished transition!" );
}, false );
Adding fallback for functionality related to CSS3 animation completion
Will animation end event be fired if browser doesn't support CSS animations?
Generally if the browser does not support CSS3 animations then the animationEnd
event would also not be fired and so the callback function which gets executed upon completion of animation will not be called at all.
What is the solution to execute the callback function?
One way to make sure that the callback function gets executed even on browsers that do not support CSS3 animations would be to test for animation support and (a) execute the callback function after the completion of animation if it is supported or (b) execute callback function immediately if animation isn't supported.
In the below snippet, I have used the code provided by MDN to test for animation support. In browsers that support animation, the below snippet would produce the shake effect and at the end of it change background color to red whereas in browsers that don't support animations, it will change background color to red immediately.
This has been tested in IE9 (doesn't support CSS3 animation), IE10, IE11, Edge, Mozilla and Chrome. In Opera, the code works when tested separately in a Fiddle or CodePen but the color doesn't change after animation in the snippet here. I assume that this must be something to do with Stack Snippet and not the code itself.
/* Code from MDN */
var animation = false, animationstring = 'animation', keyframeprefix = '', domPrefixes = 'Webkit Moz O ms Khtml'.split(' '), pfx = '', elm = document.createElement('div');
if (elm.style.animationName !== undefined) { animation = true;}
if (animation === false) { for (var i = 0; i < domPrefixes.length; i++) { if (elm.style[domPrefixes[i] + 'AnimationName'] !== undefined) { pfx = domPrefixes[i]; animationstring = pfx + 'Animation'; keyframeprefix = '-' + pfx.toLowerCase() + '-'; animation = true; break; } }}
/* End of Code from MDN */
if (animation) { // check if animation is supported var animationEnd = 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend'; $('.animated-element').one(animationEnd, function() { // if supported attach animationEnd handler callback(); })} else { // if animation is not supported execute callback immediately callback();}
function callback() { console.log('Hello World!'); $('.animated-element').css('background-color', 'red');}
.animated-element { display: inline-block; width: 200px; height: 200px; border: 1px solid; animation: shake 1s 2s linear forwards;}@keyframes shake { 0% { transform: translateX(10px); } 25% { transform: translateX(-10px); } 50% { transform: translateX(10px); } 75% { transform: translateX(-10px); } 100% { transform: translateX(0px); }}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><div class='animated-element'>Hello!</div>
Bind CSS3 Animation Callback using Knockout.js
From my experience there is no 'knockout way' to handle those sorts of events.
Knockout's author suggests using the KO event binding for simple bindings. But for more complex and/or unobtrusive event binding scenarios suggests using jQuery: http://knockoutjs.com/documentation/unobtrusive-event-handling.html
I'm working on a project now using Knockout and I followed his suggestion. Using some KO event bindings (for form submits) and some jQuery event bindings (for a window resize event in my case).
Of course, you don't need to use jQuery, but going outside the KO library in your case is probably the correct route. Have fun!
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>'
Related Topics
Convert Svg to Png With Applied Images as Background to Svg Elements
Jquery: Get Selected Element Tag Name
Pass Mouse Events Through Absolutely-Positioned Element
In Jquery, How to Select an Element by Its Name Attribute
How to Bind Events on Ajax Loaded Content
Bootstrap Navbar Active State Not Working
How to Pass Parameter to Function Using in Addeventlistener
Get Base64 Encode File-Data from Input Form
Saving Binary Data as File Using JavaScript from a Browser
How to Embed an Autoplaying Youtube Video in an Iframe
How to Detect a Mobile Device With JavaScript
Getting Binary (Base64) Data from Html5 Canvas (Readasbinarystring)
Text-Overflow Ellipsis on Left Side
Es6 Modules in the Browser: Uncaught Syntaxerror: Unexpected Token Import
Alternative For Deprecated Svg Pathseglist
Html ≪Select≫ Selected Option Background-Color CSS Style