Are javascript (timeout, interval) and css (animations, delay) timing synchronized ?
For instance :
#anim1 {
animation: anim1 10s linear;
display: none;
}
anim1.style.display = "block" ;
setTimeout(function() {
anim2.style.webkitAnimation= 'anim2 10s linear';
}, 10000);
Will anim2 be precisely triggered at the end of anim1 ? Is it different depending on the browser ? In this case I'm more interested in a webkit focus.
Note that anim1 is triggered via javascript to avoid loading time inconsistencies.
NB : This is a theoretical question, the above code is an illustration and you must not use it at home as there are way more proper means to do so.
As far as I know, there is no guarantee. However, there are events which you can listen for;
anim1.addEventListener('animationend',function(){
anim2.style.webkitAnimation= 'anim2 10s linear';
}
Note that because these are new, there are still vendor prefixes you need to account for; webkitAnimationEnd and oanimationend for Webkit and Opera.
Also as my original answer (wrongly) suggested, there is transitionend (with similar prefixes) if you want to use CSS transitions instead of animations.
This is the wrong way to do this. There isn't any guarantee that they will be in sync, though it's likely they'll be close.
Events are provided for the start, end an repeat of an animation.
https://developer.apple.com/documentation/webkitjs/webkitanimationevent details these.
Use code like:
element.addEventListener("webkitAnimationEnd", callfunction,false);
to bind to it.
Related
I'm having some trouble understanding the requestAnimationFrame API. I think it makes the browser wait for the callback to complete until the browser triggers a repaint (or reflow?). Is that correct? If you call requestAnimationFrame within the callback, the browser will repaint and execute the new RAF's callback and then repaint again? If this is the case, how can I make two sequential calls to this API be more async/promisified rather than nesting them like my example below?
Here I have used the FLIP technique to insert a document fragment into the DOM and animate the height of a DOM element, it seems to work fine. I do not like the callback nature/nesting of the code. I struggle to understand how I can make this more async. Is it possible use a promise and make it thenable?
window.requestAnimationFrame(() => {
this.appendChild(frag);
this.style.height = `${rows * rowHeight}px`;
const { top: after } = this.getBoundingClientRect();
this.style.setProps({
"transform" : `translateY(${before - after}px)`,
"transition" : "transform 0s"
});
window.requestAnimationFrame(() => {
this.style.setProps({
"transform" : "",
"transition" : "transform .5s ease-out"
});
});
});
So, are my preconceived notions about RAF correct? And how can the above code example be void of callbacks and not as nested?
I'm not sure to get your own explanation of requestAnimationFrame, but to put it simply, it is just a setTimeout(fn, the_time_until_painting_frame).
To put it a little bit less simply, it puts fn in a list of callbacks that will all get executed when the next painting event loop will occur. This painting event loop is a special event loop, where browsers will call their own painting operations (CSS animations, WebAnimations etc. They will mark one event loop every n-th time to be such a painting frame. This is generally in sync with screen refresh-rate.
So our rAF callbacks will get executed in this event loop, just before browser's execute its painting operations.
Now, what you stumbled upon is how browser do calculate CSS transitions: They take the current computed values of your element.
But these computed values are not updated synchronously. Browsers will generally wait this very painting frame in order to do the recalc, because to calculate one element, you need to recalculate all the CSSOM tree.
Fortunately for you, there are some DOM methods which will trigger such a reflow synchronously, because they do need updated box-values, for instance, Element.offsetTop getter.
So simply calling one of these methods after you did set the initial styles (including the transition one), you will be able to trigger your transition synchronously:
_this.style.height = "50px";
_this.style.setProperty("transform", "translateY(140px)");
_this.style.setProperty("transition", "transform 0s");
_this.offsetTop; // trigger reflow
_this.style.setProperty("transform", "");
_this.style.setProperty("transition", "transform .5s ease-out");
#_this {
background: red;
width: 50px;
}
<div id="_this"></div>
I am building a website that has a few animations when you load the home page (for example, the main logo and a few menus slide in from the sides of the screen). Simultaneously, I am also using AJAX in the background to start to load some images that might be viewed later. The problem with this is that when the images are loading, the animations become quite choppy. Is there any way to stop this? Or maybe give the AJAX function a lower priority so that it doesn't try to do anything when an animation is running?
Here is the current script I'm using to load these images:
$('.lightbox-container.first').load('/images/first_set/', function(){
$('.lightbox-container.second').load('/images/second_set/', function(){
$('.lightbox-container.third').load('/images/third_set/', function(){
$('.lightbox-container.fourth').load('/images/fourth_set/', function(){
$('.lightbox-container.fifth').load('/images/fifth_set/', function(){
$('.lightbox-container.sixth').load('/images/sixth_set/');
});
});
});
});
});
An all of my animation function look something like:
$('.third-section').animate({ 'opacity': '1', 'height': '200px', 'padding-top': '20px', }, 500);
The problem
Since all your ajax and js animations run on the same browser thread, you are bound to have this problems. You are reaching the limits of your CPU, which causes the choppiness.
How to solve this
Use CSS3 transforms. Those are hardware accelerated in all modern browsers and run on a separate threads, so their performance is generally not affected by ajax calls. Since you said you only slide things around, I think this would be the ideal solution for you. There is a great article about it here:
http://www.html5rocks.com/en/tutorials/speed/high-performance-animations/
Your case
So to actually make this work for you. Leave the ugly ajax calls as they are for now. Instead of using jQuery animate, you need 2 states - the initial, which positions the slide away and one with an extra class, which positions your slide in it's target place.
All you have to do is add the class to the slide and it will nicely come in place. Theory is simple.
Sample
Your initial state could be something like this:
.slide {
transform: translate(-400px, -200px);
transition: all 5s;
}
And the one to show in place:
.slide.show {
transform: translate(0px, 0px);
}
I was wondering if it was possible, using some javascript or jquery, to skip to the next, or go to the last part of a css animation. Lets say we have the following:
#keyframe effect{
0%{opacity:1;}
45%{opacity:1;}
50%{opacity:0;}
95%{opacity:0;}
100%{opacity:1;}
}
that will fade something out and then back in
so lets say I made some buttons. How would I do the following:
$('#next').click(function(){
//skip to the next animation part
});
$('#previous').click(function(){
//skip to the previous animation part
});
It's not really possible unless you break the CSS into different parts based on classes and then add/remove classes.
However, there is an absolutely fantastic javascript library called Greensock, that allows timeline-based animation - and in many cases is faster than CSS animations. It also has the benefit of being cross-browser compatible.
If you were, for example to create something similar using Greensock, it would look something like this:
var effect = new TimelineMax({paused:true});
effect.addLabel('start');
effect.to(
'#myItem',
1,
{css:{opacity:1}}
);
effect.addLabel('step1');
effect.to(
'#myItem',
1,
{css:{opacity:0}}
);
effect.addLabel('end');
effect.play();
$('#gotoEnd').on('click', function(e){
e.preventDefault();
effect.seek('end');
});
With the use of the animation-play-state Property, you can pause an animation and update the transform, then restart it.
element.style.webkitAnimationPlayState = "paused";
element.style.webkitAnimationPlayState = "running";
However you can't pause the animation, transform it, resume it, and expect it to run fluidly from the new transformed state.
At this time, there is no way to get the exact current "percentage completed" of a CSS keyframe animation. The best method to approximate it is using a setInterval or requestAnimationFrame.
This CSS tricks article explains this further, and gives an example of using setInterval. Another option is to use request animation frame
As mentioned GreenSock or Velocity are animation libraries which allow for extremely fast and smooth animations
I'm fairly new to Javascript, so let me know if I'm doing something a little silly, but here's the gist:
I'm working with integrating a new feature into a very rigidly constructed template (I basically only get a single plaintext link). My workaround for this was to just add some jQuery that would add an onclick method that would replace the link with the element that I actually wanted to have.
$(document).ready(function(){
$("li a:contains('Search')").bind("click", replaceWithSearch);
});
function replaceWithSearch(){
var searchWrapper = constructSearchBox("");
this.parentNode.replaceChild(searchWrapper, this);
}
That all works, but I've been talking with UI people over here and they want animations for this replacement. Of course their goto is to use CSS animations, but I'm not really sure how to add a smooth fade or slide animation to the replaceChild operation. Am I thinking about this the right way? If so how exactly would I add that animation?
Using CSS animations, you'd do something like the following:
.your-selector {
animation: fadeIn 400ms ease-in-out;
}
#keyframes fadeIn {
from { opacity: 0; }
}
Here's a fiddle showing this: http://jsfiddle.net/zt3QB/. That will make it start from 0 opacity when it is injected into the DOM, and go to the default, which is 1.
If you want to use jQuery:
function replaceWithSearch(){
var searchWrapper = constructSearchBox("").css('opacity', 0);
this.parentNode.replaceChild(searchWrapper, this);
// Using setTimeout because sometimes the DOM is too fast...
setTimeout(function() {
searchWrapper.fadeTo(400, 1);
}, 0);
}
I haven't tested the jQuery one, but I've done similar things. Just finished a project using the CSS version.
I've been trying to animate a Dashboard Widget's div disappearance, but it just brutally goes "poof" (as in, disappears as expected, only instantly).
function removeElement(elementId)
{
duration = 9000; // The length of the animation
interval = 13; // How often the animation should change
start = 1.0; // The starting value
finish = 0.0; // The finishing value
handler = function(animation, current, start, finish) {
// Called every interval; provides a current value between start and finish
document.getElementById(elementId).style.opacity = current;
};
new AppleAnimator(duration, interval, start, finish, handler).start();
interval = 1;
start= "visible";
finish = "hidden";
duration = 9001;
handler = function(animation, current, start, finish) {
document.getElementById(elementId).style.visibility="hidden";
};
new AppleAnimator(duration, interval, start, finish, handler).start();
}
I expected this to "disappear" the div a millisecond after its opacity reaches zero, but for a not so obvious reason (to me), it just disappears immediately. If I comment out the second animation code, the div fades out (but it's still active, which I don't want).
All solutions I've yet seen rely on using JQuery and wait for the event at the end of the animation, is there another way to do that, other than JQuery?
If you are looking for a pure javascript solution it probably needs a good understanding of how javascript event work and basically about javascript language. As reference you should check this question on CodeReview
But as I think the best solution for you and not to rely on jQuery is to checkout CSS3 animations. Even if they are not supported by all browsers you could use Modernizer to fill polyfills for animations.
My favorite CSS3 Animation library is Animate.css. It's pretty neat and gives you a variety of demos in the page.
You'll first have to choose an animation and add it to your css stylesheets. Then have another custom class that contain everything about the animation.
.disappear{
-webkit-animation-duration: 3s;
-webkit-animation-delay: 2s;
-webkit-animation-iteration-count: infinite;
}
Then you could use javascript events to toggle in classes to your Elements. Below is how you add a class to an element.
var animationObject = document.getElementById("poof");
animationObject.className = animationObject.className + " disappear";
If you need more help regarding javascript of how this should be done check out this answer.
Hope this helps...
I found it: AppleAnimator possesses animator.oncomplete: A handler called when the timer is complete.
In my case:
var anim = new AppleAnimator(duration, interval, start, finish, handler);
anim.oncomplete= function(){
document.getElementById(elementId).style.visibility="hidden";
};
anim.start();
The Apple documentation actually calls "Callback" the animation code itself, and "handler" the callback, which makes it a bit hard to realize at first.
Thanks frenchie though, the "YourCallbackFunction" made me realize I was missing something related to callbacks :D