CSS Transitions & Animations

The best opportunity for smooth animations are CSS transition and keyframe animation based, especially tapping into hardware-accelerated transforms and opacity changes. Below are notes that summarise a number of very useful articles. Any credit belongs to those.

Background

Animation and interactive development, that historically relied upon javascript, is being replaced to varying degrees with CSS animations. CSS3 allows us to animate the properties of an element, with some degree of complexity, with two types of animation - using either the transition or animation properties.

Letting the browser control animation sequences allows it to optimise performance and efficiency by altering the frame rate, minimising paints and offloading some of the work to the GPU.

CSS Transitions

Where CSS transitions aren't supported, property changes will be applied instantly and therefore still degrade gracefully. The simplest way to apply transitions is with pseudo classes such as :hover. Alternatively they can be applied by using javascript to toggle classes responsible for triggering the transitions. A more programmatic approach may involve setting style propery changes directly with js, but using transitions declared within the stylesheets to handle the tween.

Duration is the only required item in the shorthand declaration of CSS transitions. The timing function defaults to "ease" and the property defaults to "all". Browsers have a set of built-in timing functions, such as linear, ease, ease-in, ease-out, ease-in-out. However, for more complex timing functions we can define our own by specifying four points along a cubic bezier curve (http://cubic-bezier.com/ & http://easings.net/).

It's worth noting that not every css property can be transitioned. There is a finite list (http://oli.jp/2010/css-animatable-properties/). A basic rule is that you can only transition a property through absolute values.

When developing interactive features, there is a decision as to whether to animate with javascript (imperative) or CSS (declarative). Imperative animation gives us lots of control, start/stop/pause etc and furthermore some effects (parallax) can only be achieved with javascript (for optimal coverage). However it also means our animations are running in javascript on the browser's main thread, which is already busy with other javascript, style calculations, layout and painting. This means there's a chance of missing animation frames. Declarative animations written in CSS allows the browser to optimise the execution a great deal (possibly even ahead of time), but lacks the expressive power of javascript animations. Chaining animations for example often becomes more complex for those declared in CSS.

The best option is often to combine both javascript and CSS when developing performant animations. We can define and invoke transitions via javascript, but allow CSS to handle to transition execution. In order to chain transitions we need to utilise transition end callbacks. We can achieve this by listening for transition end events - browsers currently have different event names that we need to listen for. This means we are notified when a particular transition has finished and can therefore start to queue transitions.

It's worth noting that the transitionend event will bubble up from any child selector that also has a transition, resulting in more events being fired than would be expected. To stop the event from bubbling we can stop the event from propagating each time we bind it. e.stopPropagation();

CSS Animations

CSS animations are based on keyframes - a start or end point in an animation sequence. The time between these frames is known as the 'tween' or in CSS, the transition. We can define as many frames during this transition period as required, allowing complex animation sequences to be created. In contrast, with transitions we have little control beyond the end result.

At this time, animations are still an experimental feature and therefore require vendor prefixes for maximum coverage. http://caniuse.com/#feat=css-animation. In cases where proper function of a site is reliant on animations and/or transitions, it's important to consider fallback methods in order to expose the same end result.

When combined with javascript, we can chain animations and assign/remove animations to elements programmatically. In the same way that CSS transitions fire a transitioneend event. three events are fired during each animation sequence: animationstart, animationiteration, animationend.

For a detailed overview of CSS animation syntax, visit dev.opera.com/articles/view/css3-animations. Of particular interest is the lesser known animation-fill-mode property which allows us to specify how an element is displayed after an animation ends, or during an animation-delay. We can use this property to retain styles defined during an animation even after the animation ends.

Performance & Hardware Acceleration

Whilst most CSS transitions/animations across various properties will give good results, modern browsers can animate 4 particular properties really easily - position/translation, scale, rotate (i.e. transformations) and opacity. Animating anything else risks less than optimal animation frame rates in comparison. The reason for this is the animation rendering can be offloaded to the GPU when using CSS transformations. Simply, this means that elements are turned into images during the transition/animation, avoiding style recalculations and improving performance significantly.

This method of "hardware acceleration" can be triggered by setting the transformations z axis, which can be done with translate3D. This isn't a silver bullet and comes with its own considerations though. It shouldn't be enabled on every element, only when required. Hardware acceleration can sometimes cause subtle font issues. The short-term (controversial) fix is to disable sub pixel anti-aliasing completely. However, do your research before doing this as there are important considerations. If there is flickering or juddering with hardware acceleration, check elements aren't being nested with the transform3D property set.

The translate3D hack is becoming less relevant as browsers move towards automatically using the GPU for transitions. Also of note is that ios6 has explicitly disabled the translate3D trick and requires workarounds (http://indiegamr.com/ios6-html-hardware-acceleration-changes-and-how-to-fix-them/).

Animation Performance in More Detail

The process a browser follows when animating is: calculate styles that apply to the element (recalc styles), generate the geometry and position of all affected elements (layout), fill the pixels for each element into layers (paint) and draw the layers out to screen (composite layers).

Transitioning certain properties such as left, margin, width etc causes the browser to recalculate styles and affects layout at every frame. This is expensive and can lead to unnecessary repaints. The larger the DOM tree the longer it takes to perform these layout calculations, as changes may impact lots of child elements also. This will have the most notable impact on performance in less powerful devices such as mobile devices.

By changing only properties that affect compositing in our animations and avoid tiggering layout changes - transforms and opacity, we stand the best chance of achieveing smooth animations, especially when tapping into hardware acceleration.

CSS Transitions or CSS Animations

Whilst with transitions a state change is required in order to trigger the animation (e.g. :hover), animations don't require explicit state changes in this way, and can start playing automatically. Another difference is that a transition occurs on properties that change their values, whereas animations can animate between property values even if those properties were not set set in the default state of the elements.

Using the animation-iteration-count property, it's straightforward to loop animations, whereas transitions only run once when triggered. Whilst we could in theory loop transitions by listening for the transitionend event, this adds a level of complexity.

It makes sense that for animations which include multiple frames and/or looping, use of the CSS animation property is likely the favoured approach. That said, the decision process between use of animations and/or transitions is blurry, and as usual depends on a number of different factors.

References

Back to top