As the title implies, is there a proper way to set some initial CSS properties (or class) and tell the browser to transition these to another value?
For example (fiddle):
var el = document.querySelector('div'),
st = el.style;
st.opacity = 0;
st.transition = 'opacity 2s';
st.opacity = 1;
This will not animate the opacity of the element in Chrome 29/Firefox 23. This is because (source):
[...] you’ll find that if you apply both sets of properties, one immediately
after the other, then the browser tries to optimize the property
changes, ignoring your initial properties and preventing a transition.
Behind the scenes, browsers batch up property changes before painting
which, while usually speeding up rendering, can sometimes have adverse
affects.
The solution is to force a redraw between applying the two sets of
properties. A simple method of doing this is just by accessing a DOM
element’s offsetHeight
property [...]
In fact, the hack does work in the current Chrome/Firefox versions. Updated code (fiddle - click Run
after opening the fiddle to run animation again):
var el = document.querySelector('div'),
st = el.style;
st.opacity = 0;
el.offsetHeight; //force a redraw
st.transition = 'opacity 2s';
st.opacity = 1;
However, this is rather hackish and is reported to not work on some android devices.
Another answer suggests using setTimeout
so the browser has time to perform a redraw, but it also fails in that we don't know how long it will take for a redraw to take place. Guessing a decent number of milliseconds (30-100?) to ensure that a redraw occurred means sacrificing performance, unnecessarily idling in the hopes that the browser performs some magic in that little while.
Through testing, I've found yet another solution which has been working great on latest Chrome, using requestAnimationFrame
(fiddle):
var el = document.querySelector('div'),
st = el.style;
st.opacity = 0;
requestAnimationFrame(function() {
st.transition = 'opacity 2s';
st.opacity = 1;
});
I assume that requestAnimationFrame
waits until right before the beginning of the next repaint before executing the callback, hence the browser does not batch up the property changes. Not entirely sure here, but works nicely on Chrome 29.
Update: after further testing, the requestAnimationFrame
method does not work very well on Firefox 23 - it seems to fail most of the time. (fiddle)
Is there a proper or recommended (cross-browser) way of achieving this?
See Question&Answers more detail:
os